roojs-debug.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     html :  '&lt;div name="{id}"&gt;' + 
4405         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4406         '&lt;/div&gt;',
4407     myformat: function (value, allValues) {
4408         return 'XX' + value;
4409     }
4410 });
4411 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4412 </code></pre>
4413 * For more information see this blog post with examples: <a href="http://www.jackslocum.com/yui/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">DomHelper - Create Elements using DOM, HTML fragments and Templates</a>. 
4414 * @constructor
4415 * @param {Object} cfg - Configuration object.
4416 */
4417 Roo.Template = function(cfg){
4418     // BC!
4419     if(cfg instanceof Array){
4420         cfg = cfg.join("");
4421     }else if(arguments.length > 1){
4422         cfg = Array.prototype.join.call(arguments, "");
4423     }
4424     
4425     
4426     if (typeof(cfg) == 'object') {
4427         Roo.apply(this,cfg)
4428     } else {
4429         // bc
4430         this.html = cfg;
4431     }
4432     
4433     
4434 };
4435 Roo.Template.prototype = {
4436     
4437     /**
4438      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4439      */
4440     html : '',
4441     /**
4442      * Returns an HTML fragment of this template with the specified values applied.
4443      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4444      * @return {String} The HTML fragment
4445      */
4446     applyTemplate : function(values){
4447         try {
4448             
4449             if(this.compiled){
4450                 return this.compiled(values);
4451             }
4452             var useF = this.disableFormats !== true;
4453             var fm = Roo.util.Format, tpl = this;
4454             var fn = function(m, name, format, args){
4455                 if(format && useF){
4456                     if(format.substr(0, 5) == "this."){
4457                         return tpl.call(format.substr(5), values[name], values);
4458                     }else{
4459                         if(args){
4460                             // quoted values are required for strings in compiled templates, 
4461                             // but for non compiled we need to strip them
4462                             // quoted reversed for jsmin
4463                             var re = /^\s*['"](.*)["']\s*$/;
4464                             args = args.split(',');
4465                             for(var i = 0, len = args.length; i < len; i++){
4466                                 args[i] = args[i].replace(re, "$1");
4467                             }
4468                             args = [values[name]].concat(args);
4469                         }else{
4470                             args = [values[name]];
4471                         }
4472                         return fm[format].apply(fm, args);
4473                     }
4474                 }else{
4475                     return values[name] !== undefined ? values[name] : "";
4476                 }
4477             };
4478             return this.html.replace(this.re, fn);
4479         } catch (e) {
4480             Roo.log(e);
4481             throw e;
4482         }
4483          
4484     },
4485     
4486     /**
4487      * Sets the HTML used as the template and optionally compiles it.
4488      * @param {String} html
4489      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4490      * @return {Roo.Template} this
4491      */
4492     set : function(html, compile){
4493         this.html = html;
4494         this.compiled = null;
4495         if(compile){
4496             this.compile();
4497         }
4498         return this;
4499     },
4500     
4501     /**
4502      * True to disable format functions (defaults to false)
4503      * @type Boolean
4504      */
4505     disableFormats : false,
4506     
4507     /**
4508     * The regular expression used to match template variables 
4509     * @type RegExp
4510     * @property 
4511     */
4512     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4513     
4514     /**
4515      * Compiles the template into an internal function, eliminating the RegEx overhead.
4516      * @return {Roo.Template} this
4517      */
4518     compile : function(){
4519         var fm = Roo.util.Format;
4520         var useF = this.disableFormats !== true;
4521         var sep = Roo.isGecko ? "+" : ",";
4522         var fn = function(m, name, format, args){
4523             if(format && useF){
4524                 args = args ? ',' + args : "";
4525                 if(format.substr(0, 5) != "this."){
4526                     format = "fm." + format + '(';
4527                 }else{
4528                     format = 'this.call("'+ format.substr(5) + '", ';
4529                     args = ", values";
4530                 }
4531             }else{
4532                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4533             }
4534             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4535         };
4536         var body;
4537         // branched to use + in gecko and [].join() in others
4538         if(Roo.isGecko){
4539             body = "this.compiled = function(values){ return '" +
4540                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4541                     "';};";
4542         }else{
4543             body = ["this.compiled = function(values){ return ['"];
4544             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4545             body.push("'].join('');};");
4546             body = body.join('');
4547         }
4548         /**
4549          * eval:var:values
4550          * eval:var:fm
4551          */
4552         eval(body);
4553         return this;
4554     },
4555     
4556     // private function used to call members
4557     call : function(fnName, value, allValues){
4558         return this[fnName](value, allValues);
4559     },
4560     
4561     /**
4562      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4563      * @param {String/HTMLElement/Roo.Element} el The context element
4564      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4565      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4566      * @return {HTMLElement/Roo.Element} The new node or Element
4567      */
4568     insertFirst: function(el, values, returnElement){
4569         return this.doInsert('afterBegin', el, values, returnElement);
4570     },
4571
4572     /**
4573      * Applies the supplied values to the template and inserts the new node(s) before el.
4574      * @param {String/HTMLElement/Roo.Element} el The context element
4575      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4576      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4577      * @return {HTMLElement/Roo.Element} The new node or Element
4578      */
4579     insertBefore: function(el, values, returnElement){
4580         return this.doInsert('beforeBegin', el, values, returnElement);
4581     },
4582
4583     /**
4584      * Applies the supplied values to the template and inserts the new node(s) after el.
4585      * @param {String/HTMLElement/Roo.Element} el The context element
4586      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4587      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4588      * @return {HTMLElement/Roo.Element} The new node or Element
4589      */
4590     insertAfter : function(el, values, returnElement){
4591         return this.doInsert('afterEnd', el, values, returnElement);
4592     },
4593     
4594     /**
4595      * Applies the supplied values to the template and appends the new node(s) to el.
4596      * @param {String/HTMLElement/Roo.Element} el The context element
4597      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4598      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4599      * @return {HTMLElement/Roo.Element} The new node or Element
4600      */
4601     append : function(el, values, returnElement){
4602         return this.doInsert('beforeEnd', el, values, returnElement);
4603     },
4604
4605     doInsert : function(where, el, values, returnEl){
4606         el = Roo.getDom(el);
4607         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4608         return returnEl ? Roo.get(newNode, true) : newNode;
4609     },
4610
4611     /**
4612      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4613      * @param {String/HTMLElement/Roo.Element} el The context element
4614      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4615      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4616      * @return {HTMLElement/Roo.Element} The new node or Element
4617      */
4618     overwrite : function(el, values, returnElement){
4619         el = Roo.getDom(el);
4620         el.innerHTML = this.applyTemplate(values);
4621         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4622     }
4623 };
4624 /**
4625  * Alias for {@link #applyTemplate}
4626  * @method
4627  */
4628 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4629
4630 // backwards compat
4631 Roo.DomHelper.Template = Roo.Template;
4632
4633 /**
4634  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4635  * @param {String/HTMLElement} el A DOM element or its id
4636  * @returns {Roo.Template} The created template
4637  * @static
4638  */
4639 Roo.Template.from = function(el){
4640     el = Roo.getDom(el);
4641     return new Roo.Template(el.value || el.innerHTML);
4642 };/*
4643  * Based on:
4644  * Ext JS Library 1.1.1
4645  * Copyright(c) 2006-2007, Ext JS, LLC.
4646  *
4647  * Originally Released Under LGPL - original licence link has changed is not relivant.
4648  *
4649  * Fork - LGPL
4650  * <script type="text/javascript">
4651  */
4652  
4653
4654 /*
4655  * This is code is also distributed under MIT license for use
4656  * with jQuery and prototype JavaScript libraries.
4657  */
4658 /**
4659  * @class Roo.DomQuery
4660 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4661 <p>
4662 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4663
4664 <p>
4665 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4666 </p>
4667 <h4>Element Selectors:</h4>
4668 <ul class="list">
4669     <li> <b>*</b> any element</li>
4670     <li> <b>E</b> an element with the tag E</li>
4671     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4672     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4673     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4674     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4675 </ul>
4676 <h4>Attribute Selectors:</h4>
4677 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4678 <ul class="list">
4679     <li> <b>E[foo]</b> has an attribute "foo"</li>
4680     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4681     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4682     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4683     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4684     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4685     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4686 </ul>
4687 <h4>Pseudo Classes:</h4>
4688 <ul class="list">
4689     <li> <b>E:first-child</b> E is the first child of its parent</li>
4690     <li> <b>E:last-child</b> E is the last child of its parent</li>
4691     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4692     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4693     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4694     <li> <b>E:only-child</b> E is the only child of its parent</li>
4695     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4696     <li> <b>E:first</b> the first E in the resultset</li>
4697     <li> <b>E:last</b> the last E in the resultset</li>
4698     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4699     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4700     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4701     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4702     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4703     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4704     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4705     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4706     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4707 </ul>
4708 <h4>CSS Value Selectors:</h4>
4709 <ul class="list">
4710     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4711     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4712     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4713     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4714     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4715     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4716 </ul>
4717  * @singleton
4718  */
4719 Roo.DomQuery = function(){
4720     var cache = {}, simpleCache = {}, valueCache = {};
4721     var nonSpace = /\S/;
4722     var trimRe = /^\s+|\s+$/g;
4723     var tplRe = /\{(\d+)\}/g;
4724     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4725     var tagTokenRe = /^(#)?([\w-\*]+)/;
4726     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4727
4728     function child(p, index){
4729         var i = 0;
4730         var n = p.firstChild;
4731         while(n){
4732             if(n.nodeType == 1){
4733                if(++i == index){
4734                    return n;
4735                }
4736             }
4737             n = n.nextSibling;
4738         }
4739         return null;
4740     };
4741
4742     function next(n){
4743         while((n = n.nextSibling) && n.nodeType != 1);
4744         return n;
4745     };
4746
4747     function prev(n){
4748         while((n = n.previousSibling) && n.nodeType != 1);
4749         return n;
4750     };
4751
4752     function children(d){
4753         var n = d.firstChild, ni = -1;
4754             while(n){
4755                 var nx = n.nextSibling;
4756                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4757                     d.removeChild(n);
4758                 }else{
4759                     n.nodeIndex = ++ni;
4760                 }
4761                 n = nx;
4762             }
4763             return this;
4764         };
4765
4766     function byClassName(c, a, v){
4767         if(!v){
4768             return c;
4769         }
4770         var r = [], ri = -1, cn;
4771         for(var i = 0, ci; ci = c[i]; i++){
4772             if((' '+ci.className+' ').indexOf(v) != -1){
4773                 r[++ri] = ci;
4774             }
4775         }
4776         return r;
4777     };
4778
4779     function attrValue(n, attr){
4780         if(!n.tagName && typeof n.length != "undefined"){
4781             n = n[0];
4782         }
4783         if(!n){
4784             return null;
4785         }
4786         if(attr == "for"){
4787             return n.htmlFor;
4788         }
4789         if(attr == "class" || attr == "className"){
4790             return n.className;
4791         }
4792         return n.getAttribute(attr) || n[attr];
4793
4794     };
4795
4796     function getNodes(ns, mode, tagName){
4797         var result = [], ri = -1, cs;
4798         if(!ns){
4799             return result;
4800         }
4801         tagName = tagName || "*";
4802         if(typeof ns.getElementsByTagName != "undefined"){
4803             ns = [ns];
4804         }
4805         if(!mode){
4806             for(var i = 0, ni; ni = ns[i]; i++){
4807                 cs = ni.getElementsByTagName(tagName);
4808                 for(var j = 0, ci; ci = cs[j]; j++){
4809                     result[++ri] = ci;
4810                 }
4811             }
4812         }else if(mode == "/" || mode == ">"){
4813             var utag = tagName.toUpperCase();
4814             for(var i = 0, ni, cn; ni = ns[i]; i++){
4815                 cn = ni.children || ni.childNodes;
4816                 for(var j = 0, cj; cj = cn[j]; j++){
4817                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4818                         result[++ri] = cj;
4819                     }
4820                 }
4821             }
4822         }else if(mode == "+"){
4823             var utag = tagName.toUpperCase();
4824             for(var i = 0, n; n = ns[i]; i++){
4825                 while((n = n.nextSibling) && n.nodeType != 1);
4826                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4827                     result[++ri] = n;
4828                 }
4829             }
4830         }else if(mode == "~"){
4831             for(var i = 0, n; n = ns[i]; i++){
4832                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4833                 if(n){
4834                     result[++ri] = n;
4835                 }
4836             }
4837         }
4838         return result;
4839     };
4840
4841     function concat(a, b){
4842         if(b.slice){
4843             return a.concat(b);
4844         }
4845         for(var i = 0, l = b.length; i < l; i++){
4846             a[a.length] = b[i];
4847         }
4848         return a;
4849     }
4850
4851     function byTag(cs, tagName){
4852         if(cs.tagName || cs == document){
4853             cs = [cs];
4854         }
4855         if(!tagName){
4856             return cs;
4857         }
4858         var r = [], ri = -1;
4859         tagName = tagName.toLowerCase();
4860         for(var i = 0, ci; ci = cs[i]; i++){
4861             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4862                 r[++ri] = ci;
4863             }
4864         }
4865         return r;
4866     };
4867
4868     function byId(cs, attr, id){
4869         if(cs.tagName || cs == document){
4870             cs = [cs];
4871         }
4872         if(!id){
4873             return cs;
4874         }
4875         var r = [], ri = -1;
4876         for(var i = 0,ci; ci = cs[i]; i++){
4877             if(ci && ci.id == id){
4878                 r[++ri] = ci;
4879                 return r;
4880             }
4881         }
4882         return r;
4883     };
4884
4885     function byAttribute(cs, attr, value, op, custom){
4886         var r = [], ri = -1, st = custom=="{";
4887         var f = Roo.DomQuery.operators[op];
4888         for(var i = 0, ci; ci = cs[i]; i++){
4889             var a;
4890             if(st){
4891                 a = Roo.DomQuery.getStyle(ci, attr);
4892             }
4893             else if(attr == "class" || attr == "className"){
4894                 a = ci.className;
4895             }else if(attr == "for"){
4896                 a = ci.htmlFor;
4897             }else if(attr == "href"){
4898                 a = ci.getAttribute("href", 2);
4899             }else{
4900                 a = ci.getAttribute(attr);
4901             }
4902             if((f && f(a, value)) || (!f && a)){
4903                 r[++ri] = ci;
4904             }
4905         }
4906         return r;
4907     };
4908
4909     function byPseudo(cs, name, value){
4910         return Roo.DomQuery.pseudos[name](cs, value);
4911     };
4912
4913     // This is for IE MSXML which does not support expandos.
4914     // IE runs the same speed using setAttribute, however FF slows way down
4915     // and Safari completely fails so they need to continue to use expandos.
4916     var isIE = window.ActiveXObject ? true : false;
4917
4918     // this eval is stop the compressor from
4919     // renaming the variable to something shorter
4920     
4921     /** eval:var:batch */
4922     var batch = 30803; 
4923
4924     var key = 30803;
4925
4926     function nodupIEXml(cs){
4927         var d = ++key;
4928         cs[0].setAttribute("_nodup", d);
4929         var r = [cs[0]];
4930         for(var i = 1, len = cs.length; i < len; i++){
4931             var c = cs[i];
4932             if(!c.getAttribute("_nodup") != d){
4933                 c.setAttribute("_nodup", d);
4934                 r[r.length] = c;
4935             }
4936         }
4937         for(var i = 0, len = cs.length; i < len; i++){
4938             cs[i].removeAttribute("_nodup");
4939         }
4940         return r;
4941     }
4942
4943     function nodup(cs){
4944         if(!cs){
4945             return [];
4946         }
4947         var len = cs.length, c, i, r = cs, cj, ri = -1;
4948         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4949             return cs;
4950         }
4951         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4952             return nodupIEXml(cs);
4953         }
4954         var d = ++key;
4955         cs[0]._nodup = d;
4956         for(i = 1; c = cs[i]; i++){
4957             if(c._nodup != d){
4958                 c._nodup = d;
4959             }else{
4960                 r = [];
4961                 for(var j = 0; j < i; j++){
4962                     r[++ri] = cs[j];
4963                 }
4964                 for(j = i+1; cj = cs[j]; j++){
4965                     if(cj._nodup != d){
4966                         cj._nodup = d;
4967                         r[++ri] = cj;
4968                     }
4969                 }
4970                 return r;
4971             }
4972         }
4973         return r;
4974     }
4975
4976     function quickDiffIEXml(c1, c2){
4977         var d = ++key;
4978         for(var i = 0, len = c1.length; i < len; i++){
4979             c1[i].setAttribute("_qdiff", d);
4980         }
4981         var r = [];
4982         for(var i = 0, len = c2.length; i < len; i++){
4983             if(c2[i].getAttribute("_qdiff") != d){
4984                 r[r.length] = c2[i];
4985             }
4986         }
4987         for(var i = 0, len = c1.length; i < len; i++){
4988            c1[i].removeAttribute("_qdiff");
4989         }
4990         return r;
4991     }
4992
4993     function quickDiff(c1, c2){
4994         var len1 = c1.length;
4995         if(!len1){
4996             return c2;
4997         }
4998         if(isIE && c1[0].selectSingleNode){
4999             return quickDiffIEXml(c1, c2);
5000         }
5001         var d = ++key;
5002         for(var i = 0; i < len1; i++){
5003             c1[i]._qdiff = d;
5004         }
5005         var r = [];
5006         for(var i = 0, len = c2.length; i < len; i++){
5007             if(c2[i]._qdiff != d){
5008                 r[r.length] = c2[i];
5009             }
5010         }
5011         return r;
5012     }
5013
5014     function quickId(ns, mode, root, id){
5015         if(ns == root){
5016            var d = root.ownerDocument || root;
5017            return d.getElementById(id);
5018         }
5019         ns = getNodes(ns, mode, "*");
5020         return byId(ns, null, id);
5021     }
5022
5023     return {
5024         getStyle : function(el, name){
5025             return Roo.fly(el).getStyle(name);
5026         },
5027         /**
5028          * Compiles a selector/xpath query into a reusable function. The returned function
5029          * takes one parameter "root" (optional), which is the context node from where the query should start.
5030          * @param {String} selector The selector/xpath query
5031          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5032          * @return {Function}
5033          */
5034         compile : function(path, type){
5035             type = type || "select";
5036             
5037             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5038             var q = path, mode, lq;
5039             var tk = Roo.DomQuery.matchers;
5040             var tklen = tk.length;
5041             var mm;
5042
5043             // accept leading mode switch
5044             var lmode = q.match(modeRe);
5045             if(lmode && lmode[1]){
5046                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5047                 q = q.replace(lmode[1], "");
5048             }
5049             // strip leading slashes
5050             while(path.substr(0, 1)=="/"){
5051                 path = path.substr(1);
5052             }
5053
5054             while(q && lq != q){
5055                 lq = q;
5056                 var tm = q.match(tagTokenRe);
5057                 if(type == "select"){
5058                     if(tm){
5059                         if(tm[1] == "#"){
5060                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5061                         }else{
5062                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5063                         }
5064                         q = q.replace(tm[0], "");
5065                     }else if(q.substr(0, 1) != '@'){
5066                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5067                     }
5068                 }else{
5069                     if(tm){
5070                         if(tm[1] == "#"){
5071                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5072                         }else{
5073                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5074                         }
5075                         q = q.replace(tm[0], "");
5076                     }
5077                 }
5078                 while(!(mm = q.match(modeRe))){
5079                     var matched = false;
5080                     for(var j = 0; j < tklen; j++){
5081                         var t = tk[j];
5082                         var m = q.match(t.re);
5083                         if(m){
5084                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5085                                                     return m[i];
5086                                                 });
5087                             q = q.replace(m[0], "");
5088                             matched = true;
5089                             break;
5090                         }
5091                     }
5092                     // prevent infinite loop on bad selector
5093                     if(!matched){
5094                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5095                     }
5096                 }
5097                 if(mm[1]){
5098                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5099                     q = q.replace(mm[1], "");
5100                 }
5101             }
5102             fn[fn.length] = "return nodup(n);\n}";
5103             
5104              /** 
5105               * list of variables that need from compression as they are used by eval.
5106              *  eval:var:batch 
5107              *  eval:var:nodup
5108              *  eval:var:byTag
5109              *  eval:var:ById
5110              *  eval:var:getNodes
5111              *  eval:var:quickId
5112              *  eval:var:mode
5113              *  eval:var:root
5114              *  eval:var:n
5115              *  eval:var:byClassName
5116              *  eval:var:byPseudo
5117              *  eval:var:byAttribute
5118              *  eval:var:attrValue
5119              * 
5120              **/ 
5121             eval(fn.join(""));
5122             return f;
5123         },
5124
5125         /**
5126          * Selects a group of elements.
5127          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5128          * @param {Node} root (optional) The start of the query (defaults to document).
5129          * @return {Array}
5130          */
5131         select : function(path, root, type){
5132             if(!root || root == document){
5133                 root = document;
5134             }
5135             if(typeof root == "string"){
5136                 root = document.getElementById(root);
5137             }
5138             var paths = path.split(",");
5139             var results = [];
5140             for(var i = 0, len = paths.length; i < len; i++){
5141                 var p = paths[i].replace(trimRe, "");
5142                 if(!cache[p]){
5143                     cache[p] = Roo.DomQuery.compile(p);
5144                     if(!cache[p]){
5145                         throw p + " is not a valid selector";
5146                     }
5147                 }
5148                 var result = cache[p](root);
5149                 if(result && result != document){
5150                     results = results.concat(result);
5151                 }
5152             }
5153             if(paths.length > 1){
5154                 return nodup(results);
5155             }
5156             return results;
5157         },
5158
5159         /**
5160          * Selects a single element.
5161          * @param {String} selector The selector/xpath query
5162          * @param {Node} root (optional) The start of the query (defaults to document).
5163          * @return {Element}
5164          */
5165         selectNode : function(path, root){
5166             return Roo.DomQuery.select(path, root)[0];
5167         },
5168
5169         /**
5170          * Selects the value of a node, optionally replacing null with the defaultValue.
5171          * @param {String} selector The selector/xpath query
5172          * @param {Node} root (optional) The start of the query (defaults to document).
5173          * @param {String} defaultValue
5174          */
5175         selectValue : function(path, root, defaultValue){
5176             path = path.replace(trimRe, "");
5177             if(!valueCache[path]){
5178                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5179             }
5180             var n = valueCache[path](root);
5181             n = n[0] ? n[0] : n;
5182             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5183             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5184         },
5185
5186         /**
5187          * Selects the value of a node, parsing integers and floats.
5188          * @param {String} selector The selector/xpath query
5189          * @param {Node} root (optional) The start of the query (defaults to document).
5190          * @param {Number} defaultValue
5191          * @return {Number}
5192          */
5193         selectNumber : function(path, root, defaultValue){
5194             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5195             return parseFloat(v);
5196         },
5197
5198         /**
5199          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5200          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5201          * @param {String} selector The simple selector to test
5202          * @return {Boolean}
5203          */
5204         is : function(el, ss){
5205             if(typeof el == "string"){
5206                 el = document.getElementById(el);
5207             }
5208             var isArray = (el instanceof Array);
5209             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5210             return isArray ? (result.length == el.length) : (result.length > 0);
5211         },
5212
5213         /**
5214          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5215          * @param {Array} el An array of elements to filter
5216          * @param {String} selector The simple selector to test
5217          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5218          * the selector instead of the ones that match
5219          * @return {Array}
5220          */
5221         filter : function(els, ss, nonMatches){
5222             ss = ss.replace(trimRe, "");
5223             if(!simpleCache[ss]){
5224                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5225             }
5226             var result = simpleCache[ss](els);
5227             return nonMatches ? quickDiff(result, els) : result;
5228         },
5229
5230         /**
5231          * Collection of matching regular expressions and code snippets.
5232          */
5233         matchers : [{
5234                 re: /^\.([\w-]+)/,
5235                 select: 'n = byClassName(n, null, " {1} ");'
5236             }, {
5237                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5238                 select: 'n = byPseudo(n, "{1}", "{2}");'
5239             },{
5240                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5241                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5242             }, {
5243                 re: /^#([\w-]+)/,
5244                 select: 'n = byId(n, null, "{1}");'
5245             },{
5246                 re: /^@([\w-]+)/,
5247                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5248             }
5249         ],
5250
5251         /**
5252          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5253          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5254          */
5255         operators : {
5256             "=" : function(a, v){
5257                 return a == v;
5258             },
5259             "!=" : function(a, v){
5260                 return a != v;
5261             },
5262             "^=" : function(a, v){
5263                 return a && a.substr(0, v.length) == v;
5264             },
5265             "$=" : function(a, v){
5266                 return a && a.substr(a.length-v.length) == v;
5267             },
5268             "*=" : function(a, v){
5269                 return a && a.indexOf(v) !== -1;
5270             },
5271             "%=" : function(a, v){
5272                 return (a % v) == 0;
5273             },
5274             "|=" : function(a, v){
5275                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5276             },
5277             "~=" : function(a, v){
5278                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5279             }
5280         },
5281
5282         /**
5283          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5284          * and the argument (if any) supplied in the selector.
5285          */
5286         pseudos : {
5287             "first-child" : function(c){
5288                 var r = [], ri = -1, n;
5289                 for(var i = 0, ci; ci = n = c[i]; i++){
5290                     while((n = n.previousSibling) && n.nodeType != 1);
5291                     if(!n){
5292                         r[++ri] = ci;
5293                     }
5294                 }
5295                 return r;
5296             },
5297
5298             "last-child" : function(c){
5299                 var r = [], ri = -1, n;
5300                 for(var i = 0, ci; ci = n = c[i]; i++){
5301                     while((n = n.nextSibling) && n.nodeType != 1);
5302                     if(!n){
5303                         r[++ri] = ci;
5304                     }
5305                 }
5306                 return r;
5307             },
5308
5309             "nth-child" : function(c, a) {
5310                 var r = [], ri = -1;
5311                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5312                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5313                 for(var i = 0, n; n = c[i]; i++){
5314                     var pn = n.parentNode;
5315                     if (batch != pn._batch) {
5316                         var j = 0;
5317                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5318                             if(cn.nodeType == 1){
5319                                cn.nodeIndex = ++j;
5320                             }
5321                         }
5322                         pn._batch = batch;
5323                     }
5324                     if (f == 1) {
5325                         if (l == 0 || n.nodeIndex == l){
5326                             r[++ri] = n;
5327                         }
5328                     } else if ((n.nodeIndex + l) % f == 0){
5329                         r[++ri] = n;
5330                     }
5331                 }
5332
5333                 return r;
5334             },
5335
5336             "only-child" : function(c){
5337                 var r = [], ri = -1;;
5338                 for(var i = 0, ci; ci = c[i]; i++){
5339                     if(!prev(ci) && !next(ci)){
5340                         r[++ri] = ci;
5341                     }
5342                 }
5343                 return r;
5344             },
5345
5346             "empty" : function(c){
5347                 var r = [], ri = -1;
5348                 for(var i = 0, ci; ci = c[i]; i++){
5349                     var cns = ci.childNodes, j = 0, cn, empty = true;
5350                     while(cn = cns[j]){
5351                         ++j;
5352                         if(cn.nodeType == 1 || cn.nodeType == 3){
5353                             empty = false;
5354                             break;
5355                         }
5356                     }
5357                     if(empty){
5358                         r[++ri] = ci;
5359                     }
5360                 }
5361                 return r;
5362             },
5363
5364             "contains" : function(c, v){
5365                 var r = [], ri = -1;
5366                 for(var i = 0, ci; ci = c[i]; i++){
5367                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5368                         r[++ri] = ci;
5369                     }
5370                 }
5371                 return r;
5372             },
5373
5374             "nodeValue" : function(c, v){
5375                 var r = [], ri = -1;
5376                 for(var i = 0, ci; ci = c[i]; i++){
5377                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5378                         r[++ri] = ci;
5379                     }
5380                 }
5381                 return r;
5382             },
5383
5384             "checked" : function(c){
5385                 var r = [], ri = -1;
5386                 for(var i = 0, ci; ci = c[i]; i++){
5387                     if(ci.checked == true){
5388                         r[++ri] = ci;
5389                     }
5390                 }
5391                 return r;
5392             },
5393
5394             "not" : function(c, ss){
5395                 return Roo.DomQuery.filter(c, ss, true);
5396             },
5397
5398             "odd" : function(c){
5399                 return this["nth-child"](c, "odd");
5400             },
5401
5402             "even" : function(c){
5403                 return this["nth-child"](c, "even");
5404             },
5405
5406             "nth" : function(c, a){
5407                 return c[a-1] || [];
5408             },
5409
5410             "first" : function(c){
5411                 return c[0] || [];
5412             },
5413
5414             "last" : function(c){
5415                 return c[c.length-1] || [];
5416             },
5417
5418             "has" : function(c, ss){
5419                 var s = Roo.DomQuery.select;
5420                 var r = [], ri = -1;
5421                 for(var i = 0, ci; ci = c[i]; i++){
5422                     if(s(ss, ci).length > 0){
5423                         r[++ri] = ci;
5424                     }
5425                 }
5426                 return r;
5427             },
5428
5429             "next" : function(c, ss){
5430                 var is = Roo.DomQuery.is;
5431                 var r = [], ri = -1;
5432                 for(var i = 0, ci; ci = c[i]; i++){
5433                     var n = next(ci);
5434                     if(n && is(n, ss)){
5435                         r[++ri] = ci;
5436                     }
5437                 }
5438                 return r;
5439             },
5440
5441             "prev" : function(c, ss){
5442                 var is = Roo.DomQuery.is;
5443                 var r = [], ri = -1;
5444                 for(var i = 0, ci; ci = c[i]; i++){
5445                     var n = prev(ci);
5446                     if(n && is(n, ss)){
5447                         r[++ri] = ci;
5448                     }
5449                 }
5450                 return r;
5451             }
5452         }
5453     };
5454 }();
5455
5456 /**
5457  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5458  * @param {String} path The selector/xpath query
5459  * @param {Node} root (optional) The start of the query (defaults to document).
5460  * @return {Array}
5461  * @member Roo
5462  * @method query
5463  */
5464 Roo.query = Roo.DomQuery.select;
5465 /*
5466  * Based on:
5467  * Ext JS Library 1.1.1
5468  * Copyright(c) 2006-2007, Ext JS, LLC.
5469  *
5470  * Originally Released Under LGPL - original licence link has changed is not relivant.
5471  *
5472  * Fork - LGPL
5473  * <script type="text/javascript">
5474  */
5475
5476 /**
5477  * @class Roo.util.Observable
5478  * Base class that provides a common interface for publishing events. Subclasses are expected to
5479  * to have a property "events" with all the events defined.<br>
5480  * For example:
5481  * <pre><code>
5482  Employee = function(name){
5483     this.name = name;
5484     this.addEvents({
5485         "fired" : true,
5486         "quit" : true
5487     });
5488  }
5489  Roo.extend(Employee, Roo.util.Observable);
5490 </code></pre>
5491  * @param {Object} config properties to use (incuding events / listeners)
5492  */
5493
5494 Roo.util.Observable = function(cfg){
5495     
5496     cfg = cfg|| {};
5497     this.addEvents(cfg.events || {});
5498     if (cfg.events) {
5499         delete cfg.events; // make sure
5500     }
5501      
5502     Roo.apply(this, cfg);
5503     
5504     if(this.listeners){
5505         this.on(this.listeners);
5506         delete this.listeners;
5507     }
5508 };
5509 Roo.util.Observable.prototype = {
5510     /** 
5511  * @cfg {Object} listeners  list of events and functions to call for this object, 
5512  * For example :
5513  * <pre><code>
5514     listeners :  { 
5515        'click' : function(e) {
5516            ..... 
5517         } ,
5518         .... 
5519     } 
5520   </code></pre>
5521  */
5522     
5523     
5524     /**
5525      * Fires the specified event with the passed parameters (minus the event name).
5526      * @param {String} eventName
5527      * @param {Object...} args Variable number of parameters are passed to handlers
5528      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5529      */
5530     fireEvent : function(){
5531         var ce = this.events[arguments[0].toLowerCase()];
5532         if(typeof ce == "object"){
5533             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5534         }else{
5535             return true;
5536         }
5537     },
5538
5539     // private
5540     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5541
5542     /**
5543      * Appends an event handler to this component
5544      * @param {String}   eventName The type of event to listen for
5545      * @param {Function} handler The method the event invokes
5546      * @param {Object}   scope (optional) The scope in which to execute the handler
5547      * function. The handler function's "this" context.
5548      * @param {Object}   options (optional) An object containing handler configuration
5549      * properties. This may contain any of the following properties:<ul>
5550      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5551      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5552      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5553      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5554      * by the specified number of milliseconds. If the event fires again within that time, the original
5555      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5556      * </ul><br>
5557      * <p>
5558      * <b>Combining Options</b><br>
5559      * Using the options argument, it is possible to combine different types of listeners:<br>
5560      * <br>
5561      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5562                 <pre><code>
5563                 el.on('click', this.onClick, this, {
5564                         single: true,
5565                 delay: 100,
5566                 forumId: 4
5567                 });
5568                 </code></pre>
5569      * <p>
5570      * <b>Attaching multiple handlers in 1 call</b><br>
5571      * The method also allows for a single argument to be passed which is a config object containing properties
5572      * which specify multiple handlers.
5573      * <pre><code>
5574                 el.on({
5575                         'click': {
5576                         fn: this.onClick,
5577                         scope: this,
5578                         delay: 100
5579                 }, 
5580                 'mouseover': {
5581                         fn: this.onMouseOver,
5582                         scope: this
5583                 },
5584                 'mouseout': {
5585                         fn: this.onMouseOut,
5586                         scope: this
5587                 }
5588                 });
5589                 </code></pre>
5590      * <p>
5591      * Or a shorthand syntax which passes the same scope object to all handlers:
5592         <pre><code>
5593                 el.on({
5594                         'click': this.onClick,
5595                 'mouseover': this.onMouseOver,
5596                 'mouseout': this.onMouseOut,
5597                 scope: this
5598                 });
5599                 </code></pre>
5600      */
5601     addListener : function(eventName, fn, scope, o){
5602         if(typeof eventName == "object"){
5603             o = eventName;
5604             for(var e in o){
5605                 if(this.filterOptRe.test(e)){
5606                     continue;
5607                 }
5608                 if(typeof o[e] == "function"){
5609                     // shared options
5610                     this.addListener(e, o[e], o.scope,  o);
5611                 }else{
5612                     // individual options
5613                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5614                 }
5615             }
5616             return;
5617         }
5618         o = (!o || typeof o == "boolean") ? {} : o;
5619         eventName = eventName.toLowerCase();
5620         var ce = this.events[eventName] || true;
5621         if(typeof ce == "boolean"){
5622             ce = new Roo.util.Event(this, eventName);
5623             this.events[eventName] = ce;
5624         }
5625         ce.addListener(fn, scope, o);
5626     },
5627
5628     /**
5629      * Removes a listener
5630      * @param {String}   eventName     The type of event to listen for
5631      * @param {Function} handler        The handler to remove
5632      * @param {Object}   scope  (optional) The scope (this object) for the handler
5633      */
5634     removeListener : function(eventName, fn, scope){
5635         var ce = this.events[eventName.toLowerCase()];
5636         if(typeof ce == "object"){
5637             ce.removeListener(fn, scope);
5638         }
5639     },
5640
5641     /**
5642      * Removes all listeners for this object
5643      */
5644     purgeListeners : function(){
5645         for(var evt in this.events){
5646             if(typeof this.events[evt] == "object"){
5647                  this.events[evt].clearListeners();
5648             }
5649         }
5650     },
5651
5652     relayEvents : function(o, events){
5653         var createHandler = function(ename){
5654             return function(){
5655                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5656             };
5657         };
5658         for(var i = 0, len = events.length; i < len; i++){
5659             var ename = events[i];
5660             if(!this.events[ename]){ this.events[ename] = true; };
5661             o.on(ename, createHandler(ename), this);
5662         }
5663     },
5664
5665     /**
5666      * Used to define events on this Observable
5667      * @param {Object} object The object with the events defined
5668      */
5669     addEvents : function(o){
5670         if(!this.events){
5671             this.events = {};
5672         }
5673         Roo.applyIf(this.events, o);
5674     },
5675
5676     /**
5677      * Checks to see if this object has any listeners for a specified event
5678      * @param {String} eventName The name of the event to check for
5679      * @return {Boolean} True if the event is being listened for, else false
5680      */
5681     hasListener : function(eventName){
5682         var e = this.events[eventName];
5683         return typeof e == "object" && e.listeners.length > 0;
5684     }
5685 };
5686 /**
5687  * Appends an event handler to this element (shorthand for addListener)
5688  * @param {String}   eventName     The type of event to listen for
5689  * @param {Function} handler        The method the event invokes
5690  * @param {Object}   scope (optional) The scope in which to execute the handler
5691  * function. The handler function's "this" context.
5692  * @param {Object}   options  (optional)
5693  * @method
5694  */
5695 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5696 /**
5697  * Removes a listener (shorthand for removeListener)
5698  * @param {String}   eventName     The type of event to listen for
5699  * @param {Function} handler        The handler to remove
5700  * @param {Object}   scope  (optional) The scope (this object) for the handler
5701  * @method
5702  */
5703 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5704
5705 /**
5706  * Starts capture on the specified Observable. All events will be passed
5707  * to the supplied function with the event name + standard signature of the event
5708  * <b>before</b> the event is fired. If the supplied function returns false,
5709  * the event will not fire.
5710  * @param {Observable} o The Observable to capture
5711  * @param {Function} fn The function to call
5712  * @param {Object} scope (optional) The scope (this object) for the fn
5713  * @static
5714  */
5715 Roo.util.Observable.capture = function(o, fn, scope){
5716     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5717 };
5718
5719 /**
5720  * Removes <b>all</b> added captures from the Observable.
5721  * @param {Observable} o The Observable to release
5722  * @static
5723  */
5724 Roo.util.Observable.releaseCapture = function(o){
5725     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5726 };
5727
5728 (function(){
5729
5730     var createBuffered = function(h, o, scope){
5731         var task = new Roo.util.DelayedTask();
5732         return function(){
5733             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5734         };
5735     };
5736
5737     var createSingle = function(h, e, fn, scope){
5738         return function(){
5739             e.removeListener(fn, scope);
5740             return h.apply(scope, arguments);
5741         };
5742     };
5743
5744     var createDelayed = function(h, o, scope){
5745         return function(){
5746             var args = Array.prototype.slice.call(arguments, 0);
5747             setTimeout(function(){
5748                 h.apply(scope, args);
5749             }, o.delay || 10);
5750         };
5751     };
5752
5753     Roo.util.Event = function(obj, name){
5754         this.name = name;
5755         this.obj = obj;
5756         this.listeners = [];
5757     };
5758
5759     Roo.util.Event.prototype = {
5760         addListener : function(fn, scope, options){
5761             var o = options || {};
5762             scope = scope || this.obj;
5763             if(!this.isListening(fn, scope)){
5764                 var l = {fn: fn, scope: scope, options: o};
5765                 var h = fn;
5766                 if(o.delay){
5767                     h = createDelayed(h, o, scope);
5768                 }
5769                 if(o.single){
5770                     h = createSingle(h, this, fn, scope);
5771                 }
5772                 if(o.buffer){
5773                     h = createBuffered(h, o, scope);
5774                 }
5775                 l.fireFn = h;
5776                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5777                     this.listeners.push(l);
5778                 }else{
5779                     this.listeners = this.listeners.slice(0);
5780                     this.listeners.push(l);
5781                 }
5782             }
5783         },
5784
5785         findListener : function(fn, scope){
5786             scope = scope || this.obj;
5787             var ls = this.listeners;
5788             for(var i = 0, len = ls.length; i < len; i++){
5789                 var l = ls[i];
5790                 if(l.fn == fn && l.scope == scope){
5791                     return i;
5792                 }
5793             }
5794             return -1;
5795         },
5796
5797         isListening : function(fn, scope){
5798             return this.findListener(fn, scope) != -1;
5799         },
5800
5801         removeListener : function(fn, scope){
5802             var index;
5803             if((index = this.findListener(fn, scope)) != -1){
5804                 if(!this.firing){
5805                     this.listeners.splice(index, 1);
5806                 }else{
5807                     this.listeners = this.listeners.slice(0);
5808                     this.listeners.splice(index, 1);
5809                 }
5810                 return true;
5811             }
5812             return false;
5813         },
5814
5815         clearListeners : function(){
5816             this.listeners = [];
5817         },
5818
5819         fire : function(){
5820             var ls = this.listeners, scope, len = ls.length;
5821             if(len > 0){
5822                 this.firing = true;
5823                 var args = Array.prototype.slice.call(arguments, 0);
5824                 for(var i = 0; i < len; i++){
5825                     var l = ls[i];
5826                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5827                         this.firing = false;
5828                         return false;
5829                     }
5830                 }
5831                 this.firing = false;
5832             }
5833             return true;
5834         }
5835     };
5836 })();/*
5837  * Based on:
5838  * Ext JS Library 1.1.1
5839  * Copyright(c) 2006-2007, Ext JS, LLC.
5840  *
5841  * Originally Released Under LGPL - original licence link has changed is not relivant.
5842  *
5843  * Fork - LGPL
5844  * <script type="text/javascript">
5845  */
5846
5847 /**
5848  * @class Roo.EventManager
5849  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5850  * several useful events directly.
5851  * See {@link Roo.EventObject} for more details on normalized event objects.
5852  * @singleton
5853  */
5854 Roo.EventManager = function(){
5855     var docReadyEvent, docReadyProcId, docReadyState = false;
5856     var resizeEvent, resizeTask, textEvent, textSize;
5857     var E = Roo.lib.Event;
5858     var D = Roo.lib.Dom;
5859
5860
5861     var fireDocReady = function(){
5862         if(!docReadyState){
5863             docReadyState = true;
5864             Roo.isReady = true;
5865             if(docReadyProcId){
5866                 clearInterval(docReadyProcId);
5867             }
5868             if(Roo.isGecko || Roo.isOpera) {
5869                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5870             }
5871             if(Roo.isIE){
5872                 var defer = document.getElementById("ie-deferred-loader");
5873                 if(defer){
5874                     defer.onreadystatechange = null;
5875                     defer.parentNode.removeChild(defer);
5876                 }
5877             }
5878             if(docReadyEvent){
5879                 docReadyEvent.fire();
5880                 docReadyEvent.clearListeners();
5881             }
5882         }
5883     };
5884     
5885     var initDocReady = function(){
5886         docReadyEvent = new Roo.util.Event();
5887         if(Roo.isGecko || Roo.isOpera) {
5888             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5889         }else if(Roo.isIE){
5890             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5891             var defer = document.getElementById("ie-deferred-loader");
5892             defer.onreadystatechange = function(){
5893                 if(this.readyState == "complete"){
5894                     fireDocReady();
5895                 }
5896             };
5897         }else if(Roo.isSafari){ 
5898             docReadyProcId = setInterval(function(){
5899                 var rs = document.readyState;
5900                 if(rs == "complete") {
5901                     fireDocReady();     
5902                  }
5903             }, 10);
5904         }
5905         // no matter what, make sure it fires on load
5906         E.on(window, "load", fireDocReady);
5907     };
5908
5909     var createBuffered = function(h, o){
5910         var task = new Roo.util.DelayedTask(h);
5911         return function(e){
5912             // create new event object impl so new events don't wipe out properties
5913             e = new Roo.EventObjectImpl(e);
5914             task.delay(o.buffer, h, null, [e]);
5915         };
5916     };
5917
5918     var createSingle = function(h, el, ename, fn){
5919         return function(e){
5920             Roo.EventManager.removeListener(el, ename, fn);
5921             h(e);
5922         };
5923     };
5924
5925     var createDelayed = function(h, o){
5926         return function(e){
5927             // create new event object impl so new events don't wipe out properties
5928             e = new Roo.EventObjectImpl(e);
5929             setTimeout(function(){
5930                 h(e);
5931             }, o.delay || 10);
5932         };
5933     };
5934
5935     var listen = function(element, ename, opt, fn, scope){
5936         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5937         fn = fn || o.fn; scope = scope || o.scope;
5938         var el = Roo.getDom(element);
5939         if(!el){
5940             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5941         }
5942         var h = function(e){
5943             e = Roo.EventObject.setEvent(e);
5944             var t;
5945             if(o.delegate){
5946                 t = e.getTarget(o.delegate, el);
5947                 if(!t){
5948                     return;
5949                 }
5950             }else{
5951                 t = e.target;
5952             }
5953             if(o.stopEvent === true){
5954                 e.stopEvent();
5955             }
5956             if(o.preventDefault === true){
5957                e.preventDefault();
5958             }
5959             if(o.stopPropagation === true){
5960                 e.stopPropagation();
5961             }
5962
5963             if(o.normalized === false){
5964                 e = e.browserEvent;
5965             }
5966
5967             fn.call(scope || el, e, t, o);
5968         };
5969         if(o.delay){
5970             h = createDelayed(h, o);
5971         }
5972         if(o.single){
5973             h = createSingle(h, el, ename, fn);
5974         }
5975         if(o.buffer){
5976             h = createBuffered(h, o);
5977         }
5978         fn._handlers = fn._handlers || [];
5979         fn._handlers.push([Roo.id(el), ename, h]);
5980
5981         E.on(el, ename, h);
5982         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5983             el.addEventListener("DOMMouseScroll", h, false);
5984             E.on(window, 'unload', function(){
5985                 el.removeEventListener("DOMMouseScroll", h, false);
5986             });
5987         }
5988         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
5989             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
5990         }
5991         return h;
5992     };
5993
5994     var stopListening = function(el, ename, fn){
5995         var id = Roo.id(el), hds = fn._handlers, hd = fn;
5996         if(hds){
5997             for(var i = 0, len = hds.length; i < len; i++){
5998                 var h = hds[i];
5999                 if(h[0] == id && h[1] == ename){
6000                     hd = h[2];
6001                     hds.splice(i, 1);
6002                     break;
6003                 }
6004             }
6005         }
6006         E.un(el, ename, hd);
6007         el = Roo.getDom(el);
6008         if(ename == "mousewheel" && el.addEventListener){
6009             el.removeEventListener("DOMMouseScroll", hd, false);
6010         }
6011         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6012             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6013         }
6014     };
6015
6016     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6017     
6018     var pub = {
6019         
6020         
6021         /** 
6022          * Fix for doc tools
6023          * @scope Roo.EventManager
6024          */
6025         
6026         
6027         /** 
6028          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6029          * object with a Roo.EventObject
6030          * @param {Function} fn        The method the event invokes
6031          * @param {Object}   scope    An object that becomes the scope of the handler
6032          * @param {boolean}  override If true, the obj passed in becomes
6033          *                             the execution scope of the listener
6034          * @return {Function} The wrapped function
6035          * @deprecated
6036          */
6037         wrap : function(fn, scope, override){
6038             return function(e){
6039                 Roo.EventObject.setEvent(e);
6040                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6041             };
6042         },
6043         
6044         /**
6045      * Appends an event handler to an element (shorthand for addListener)
6046      * @param {String/HTMLElement}   element        The html element or id to assign the
6047      * @param {String}   eventName The type of event to listen for
6048      * @param {Function} handler The method the event invokes
6049      * @param {Object}   scope (optional) The scope in which to execute the handler
6050      * function. The handler function's "this" context.
6051      * @param {Object}   options (optional) An object containing handler configuration
6052      * properties. This may contain any of the following properties:<ul>
6053      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6054      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6055      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6056      * <li>preventDefault {Boolean} True to prevent the default action</li>
6057      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6058      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6059      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6060      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6061      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6062      * by the specified number of milliseconds. If the event fires again within that time, the original
6063      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6064      * </ul><br>
6065      * <p>
6066      * <b>Combining Options</b><br>
6067      * Using the options argument, it is possible to combine different types of listeners:<br>
6068      * <br>
6069      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6070      * Code:<pre><code>
6071 el.on('click', this.onClick, this, {
6072     single: true,
6073     delay: 100,
6074     stopEvent : true,
6075     forumId: 4
6076 });</code></pre>
6077      * <p>
6078      * <b>Attaching multiple handlers in 1 call</b><br>
6079       * The method also allows for a single argument to be passed which is a config object containing properties
6080      * which specify multiple handlers.
6081      * <p>
6082      * Code:<pre><code>
6083 el.on({
6084     'click' : {
6085         fn: this.onClick
6086         scope: this,
6087         delay: 100
6088     },
6089     'mouseover' : {
6090         fn: this.onMouseOver
6091         scope: this
6092     },
6093     'mouseout' : {
6094         fn: this.onMouseOut
6095         scope: this
6096     }
6097 });</code></pre>
6098      * <p>
6099      * Or a shorthand syntax:<br>
6100      * Code:<pre><code>
6101 el.on({
6102     'click' : this.onClick,
6103     'mouseover' : this.onMouseOver,
6104     'mouseout' : this.onMouseOut
6105     scope: this
6106 });</code></pre>
6107      */
6108         addListener : function(element, eventName, fn, scope, options){
6109             if(typeof eventName == "object"){
6110                 var o = eventName;
6111                 for(var e in o){
6112                     if(propRe.test(e)){
6113                         continue;
6114                     }
6115                     if(typeof o[e] == "function"){
6116                         // shared options
6117                         listen(element, e, o, o[e], o.scope);
6118                     }else{
6119                         // individual options
6120                         listen(element, e, o[e]);
6121                     }
6122                 }
6123                 return;
6124             }
6125             return listen(element, eventName, options, fn, scope);
6126         },
6127         
6128         /**
6129          * Removes an event handler
6130          *
6131          * @param {String/HTMLElement}   element        The id or html element to remove the 
6132          *                             event from
6133          * @param {String}   eventName     The type of event
6134          * @param {Function} fn
6135          * @return {Boolean} True if a listener was actually removed
6136          */
6137         removeListener : function(element, eventName, fn){
6138             return stopListening(element, eventName, fn);
6139         },
6140         
6141         /**
6142          * Fires when the document is ready (before onload and before images are loaded). Can be 
6143          * accessed shorthanded Roo.onReady().
6144          * @param {Function} fn        The method the event invokes
6145          * @param {Object}   scope    An  object that becomes the scope of the handler
6146          * @param {boolean}  options
6147          */
6148         onDocumentReady : function(fn, scope, options){
6149             if(docReadyState){ // if it already fired
6150                 docReadyEvent.addListener(fn, scope, options);
6151                 docReadyEvent.fire();
6152                 docReadyEvent.clearListeners();
6153                 return;
6154             }
6155             if(!docReadyEvent){
6156                 initDocReady();
6157             }
6158             docReadyEvent.addListener(fn, scope, options);
6159         },
6160         
6161         /**
6162          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6163          * @param {Function} fn        The method the event invokes
6164          * @param {Object}   scope    An object that becomes the scope of the handler
6165          * @param {boolean}  options
6166          */
6167         onWindowResize : function(fn, scope, options){
6168             if(!resizeEvent){
6169                 resizeEvent = new Roo.util.Event();
6170                 resizeTask = new Roo.util.DelayedTask(function(){
6171                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6172                 });
6173                 E.on(window, "resize", function(){
6174                     if(Roo.isIE){
6175                         resizeTask.delay(50);
6176                     }else{
6177                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6178                     }
6179                 });
6180             }
6181             resizeEvent.addListener(fn, scope, options);
6182         },
6183
6184         /**
6185          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6186          * @param {Function} fn        The method the event invokes
6187          * @param {Object}   scope    An object that becomes the scope of the handler
6188          * @param {boolean}  options
6189          */
6190         onTextResize : function(fn, scope, options){
6191             if(!textEvent){
6192                 textEvent = new Roo.util.Event();
6193                 var textEl = new Roo.Element(document.createElement('div'));
6194                 textEl.dom.className = 'x-text-resize';
6195                 textEl.dom.innerHTML = 'X';
6196                 textEl.appendTo(document.body);
6197                 textSize = textEl.dom.offsetHeight;
6198                 setInterval(function(){
6199                     if(textEl.dom.offsetHeight != textSize){
6200                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6201                     }
6202                 }, this.textResizeInterval);
6203             }
6204             textEvent.addListener(fn, scope, options);
6205         },
6206
6207         /**
6208          * Removes the passed window resize listener.
6209          * @param {Function} fn        The method the event invokes
6210          * @param {Object}   scope    The scope of handler
6211          */
6212         removeResizeListener : function(fn, scope){
6213             if(resizeEvent){
6214                 resizeEvent.removeListener(fn, scope);
6215             }
6216         },
6217
6218         // private
6219         fireResize : function(){
6220             if(resizeEvent){
6221                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6222             }   
6223         },
6224         /**
6225          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6226          */
6227         ieDeferSrc : false,
6228         /**
6229          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6230          */
6231         textResizeInterval : 50
6232     };
6233     
6234     /**
6235      * Fix for doc tools
6236      * @scopeAlias pub=Roo.EventManager
6237      */
6238     
6239      /**
6240      * Appends an event handler to an element (shorthand for addListener)
6241      * @param {String/HTMLElement}   element        The html element or id to assign the
6242      * @param {String}   eventName The type of event to listen for
6243      * @param {Function} handler The method the event invokes
6244      * @param {Object}   scope (optional) The scope in which to execute the handler
6245      * function. The handler function's "this" context.
6246      * @param {Object}   options (optional) An object containing handler configuration
6247      * properties. This may contain any of the following properties:<ul>
6248      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6249      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6250      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6251      * <li>preventDefault {Boolean} True to prevent the default action</li>
6252      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6253      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6254      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6255      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6256      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6257      * by the specified number of milliseconds. If the event fires again within that time, the original
6258      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6259      * </ul><br>
6260      * <p>
6261      * <b>Combining Options</b><br>
6262      * Using the options argument, it is possible to combine different types of listeners:<br>
6263      * <br>
6264      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6265      * Code:<pre><code>
6266 el.on('click', this.onClick, this, {
6267     single: true,
6268     delay: 100,
6269     stopEvent : true,
6270     forumId: 4
6271 });</code></pre>
6272      * <p>
6273      * <b>Attaching multiple handlers in 1 call</b><br>
6274       * The method also allows for a single argument to be passed which is a config object containing properties
6275      * which specify multiple handlers.
6276      * <p>
6277      * Code:<pre><code>
6278 el.on({
6279     'click' : {
6280         fn: this.onClick
6281         scope: this,
6282         delay: 100
6283     },
6284     'mouseover' : {
6285         fn: this.onMouseOver
6286         scope: this
6287     },
6288     'mouseout' : {
6289         fn: this.onMouseOut
6290         scope: this
6291     }
6292 });</code></pre>
6293      * <p>
6294      * Or a shorthand syntax:<br>
6295      * Code:<pre><code>
6296 el.on({
6297     'click' : this.onClick,
6298     'mouseover' : this.onMouseOver,
6299     'mouseout' : this.onMouseOut
6300     scope: this
6301 });</code></pre>
6302      */
6303     pub.on = pub.addListener;
6304     pub.un = pub.removeListener;
6305
6306     pub.stoppedMouseDownEvent = new Roo.util.Event();
6307     return pub;
6308 }();
6309 /**
6310   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6311   * @param {Function} fn        The method the event invokes
6312   * @param {Object}   scope    An  object that becomes the scope of the handler
6313   * @param {boolean}  override If true, the obj passed in becomes
6314   *                             the execution scope of the listener
6315   * @member Roo
6316   * @method onReady
6317  */
6318 Roo.onReady = Roo.EventManager.onDocumentReady;
6319
6320 Roo.onReady(function(){
6321     var bd = Roo.get(document.body);
6322     if(!bd){ return; }
6323
6324     var cls = [
6325             Roo.isIE ? "roo-ie"
6326             : Roo.isGecko ? "roo-gecko"
6327             : Roo.isOpera ? "roo-opera"
6328             : Roo.isSafari ? "roo-safari" : ""];
6329
6330     if(Roo.isMac){
6331         cls.push("roo-mac");
6332     }
6333     if(Roo.isLinux){
6334         cls.push("roo-linux");
6335     }
6336     if(Roo.isBorderBox){
6337         cls.push('roo-border-box');
6338     }
6339     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6340         var p = bd.dom.parentNode;
6341         if(p){
6342             p.className += ' roo-strict';
6343         }
6344     }
6345     bd.addClass(cls.join(' '));
6346 });
6347
6348 /**
6349  * @class Roo.EventObject
6350  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6351  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6352  * Example:
6353  * <pre><code>
6354  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6355     e.preventDefault();
6356     var target = e.getTarget();
6357     ...
6358  }
6359  var myDiv = Roo.get("myDiv");
6360  myDiv.on("click", handleClick);
6361  //or
6362  Roo.EventManager.on("myDiv", 'click', handleClick);
6363  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6364  </code></pre>
6365  * @singleton
6366  */
6367 Roo.EventObject = function(){
6368     
6369     var E = Roo.lib.Event;
6370     
6371     // safari keypress events for special keys return bad keycodes
6372     var safariKeys = {
6373         63234 : 37, // left
6374         63235 : 39, // right
6375         63232 : 38, // up
6376         63233 : 40, // down
6377         63276 : 33, // page up
6378         63277 : 34, // page down
6379         63272 : 46, // delete
6380         63273 : 36, // home
6381         63275 : 35  // end
6382     };
6383
6384     // normalize button clicks
6385     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6386                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6387
6388     Roo.EventObjectImpl = function(e){
6389         if(e){
6390             this.setEvent(e.browserEvent || e);
6391         }
6392     };
6393     Roo.EventObjectImpl.prototype = {
6394         /**
6395          * Used to fix doc tools.
6396          * @scope Roo.EventObject.prototype
6397          */
6398             
6399
6400         
6401         
6402         /** The normal browser event */
6403         browserEvent : null,
6404         /** The button pressed in a mouse event */
6405         button : -1,
6406         /** True if the shift key was down during the event */
6407         shiftKey : false,
6408         /** True if the control key was down during the event */
6409         ctrlKey : false,
6410         /** True if the alt key was down during the event */
6411         altKey : false,
6412
6413         /** Key constant 
6414         * @type Number */
6415         BACKSPACE : 8,
6416         /** Key constant 
6417         * @type Number */
6418         TAB : 9,
6419         /** Key constant 
6420         * @type Number */
6421         RETURN : 13,
6422         /** Key constant 
6423         * @type Number */
6424         ENTER : 13,
6425         /** Key constant 
6426         * @type Number */
6427         SHIFT : 16,
6428         /** Key constant 
6429         * @type Number */
6430         CONTROL : 17,
6431         /** Key constant 
6432         * @type Number */
6433         ESC : 27,
6434         /** Key constant 
6435         * @type Number */
6436         SPACE : 32,
6437         /** Key constant 
6438         * @type Number */
6439         PAGEUP : 33,
6440         /** Key constant 
6441         * @type Number */
6442         PAGEDOWN : 34,
6443         /** Key constant 
6444         * @type Number */
6445         END : 35,
6446         /** Key constant 
6447         * @type Number */
6448         HOME : 36,
6449         /** Key constant 
6450         * @type Number */
6451         LEFT : 37,
6452         /** Key constant 
6453         * @type Number */
6454         UP : 38,
6455         /** Key constant 
6456         * @type Number */
6457         RIGHT : 39,
6458         /** Key constant 
6459         * @type Number */
6460         DOWN : 40,
6461         /** Key constant 
6462         * @type Number */
6463         DELETE : 46,
6464         /** Key constant 
6465         * @type Number */
6466         F5 : 116,
6467
6468            /** @private */
6469         setEvent : function(e){
6470             if(e == this || (e && e.browserEvent)){ // already wrapped
6471                 return e;
6472             }
6473             this.browserEvent = e;
6474             if(e){
6475                 // normalize buttons
6476                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6477                 if(e.type == 'click' && this.button == -1){
6478                     this.button = 0;
6479                 }
6480                 this.type = e.type;
6481                 this.shiftKey = e.shiftKey;
6482                 // mac metaKey behaves like ctrlKey
6483                 this.ctrlKey = e.ctrlKey || e.metaKey;
6484                 this.altKey = e.altKey;
6485                 // in getKey these will be normalized for the mac
6486                 this.keyCode = e.keyCode;
6487                 // keyup warnings on firefox.
6488                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6489                 // cache the target for the delayed and or buffered events
6490                 this.target = E.getTarget(e);
6491                 // same for XY
6492                 this.xy = E.getXY(e);
6493             }else{
6494                 this.button = -1;
6495                 this.shiftKey = false;
6496                 this.ctrlKey = false;
6497                 this.altKey = false;
6498                 this.keyCode = 0;
6499                 this.charCode =0;
6500                 this.target = null;
6501                 this.xy = [0, 0];
6502             }
6503             return this;
6504         },
6505
6506         /**
6507          * Stop the event (preventDefault and stopPropagation)
6508          */
6509         stopEvent : function(){
6510             if(this.browserEvent){
6511                 if(this.browserEvent.type == 'mousedown'){
6512                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6513                 }
6514                 E.stopEvent(this.browserEvent);
6515             }
6516         },
6517
6518         /**
6519          * Prevents the browsers default handling of the event.
6520          */
6521         preventDefault : function(){
6522             if(this.browserEvent){
6523                 E.preventDefault(this.browserEvent);
6524             }
6525         },
6526
6527         /** @private */
6528         isNavKeyPress : function(){
6529             var k = this.keyCode;
6530             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6531             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6532         },
6533
6534         isSpecialKey : function(){
6535             var k = this.keyCode;
6536             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6537             (k == 16) || (k == 17) ||
6538             (k >= 18 && k <= 20) ||
6539             (k >= 33 && k <= 35) ||
6540             (k >= 36 && k <= 39) ||
6541             (k >= 44 && k <= 45);
6542         },
6543         /**
6544          * Cancels bubbling of the event.
6545          */
6546         stopPropagation : function(){
6547             if(this.browserEvent){
6548                 if(this.type == 'mousedown'){
6549                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6550                 }
6551                 E.stopPropagation(this.browserEvent);
6552             }
6553         },
6554
6555         /**
6556          * Gets the key code for the event.
6557          * @return {Number}
6558          */
6559         getCharCode : function(){
6560             return this.charCode || this.keyCode;
6561         },
6562
6563         /**
6564          * Returns a normalized keyCode for the event.
6565          * @return {Number} The key code
6566          */
6567         getKey : function(){
6568             var k = this.keyCode || this.charCode;
6569             return Roo.isSafari ? (safariKeys[k] || k) : k;
6570         },
6571
6572         /**
6573          * Gets the x coordinate of the event.
6574          * @return {Number}
6575          */
6576         getPageX : function(){
6577             return this.xy[0];
6578         },
6579
6580         /**
6581          * Gets the y coordinate of the event.
6582          * @return {Number}
6583          */
6584         getPageY : function(){
6585             return this.xy[1];
6586         },
6587
6588         /**
6589          * Gets the time of the event.
6590          * @return {Number}
6591          */
6592         getTime : function(){
6593             if(this.browserEvent){
6594                 return E.getTime(this.browserEvent);
6595             }
6596             return null;
6597         },
6598
6599         /**
6600          * Gets the page coordinates of the event.
6601          * @return {Array} The xy values like [x, y]
6602          */
6603         getXY : function(){
6604             return this.xy;
6605         },
6606
6607         /**
6608          * Gets the target for the event.
6609          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6610          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6611                 search as a number or element (defaults to 10 || document.body)
6612          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6613          * @return {HTMLelement}
6614          */
6615         getTarget : function(selector, maxDepth, returnEl){
6616             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6617         },
6618         /**
6619          * Gets the related target.
6620          * @return {HTMLElement}
6621          */
6622         getRelatedTarget : function(){
6623             if(this.browserEvent){
6624                 return E.getRelatedTarget(this.browserEvent);
6625             }
6626             return null;
6627         },
6628
6629         /**
6630          * Normalizes mouse wheel delta across browsers
6631          * @return {Number} The delta
6632          */
6633         getWheelDelta : function(){
6634             var e = this.browserEvent;
6635             var delta = 0;
6636             if(e.wheelDelta){ /* IE/Opera. */
6637                 delta = e.wheelDelta/120;
6638             }else if(e.detail){ /* Mozilla case. */
6639                 delta = -e.detail/3;
6640             }
6641             return delta;
6642         },
6643
6644         /**
6645          * Returns true if the control, meta, shift or alt key was pressed during this event.
6646          * @return {Boolean}
6647          */
6648         hasModifier : function(){
6649             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6650         },
6651
6652         /**
6653          * Returns true if the target of this event equals el or is a child of el
6654          * @param {String/HTMLElement/Element} el
6655          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6656          * @return {Boolean}
6657          */
6658         within : function(el, related){
6659             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6660             return t && Roo.fly(el).contains(t);
6661         },
6662
6663         getPoint : function(){
6664             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6665         }
6666     };
6667
6668     return new Roo.EventObjectImpl();
6669 }();
6670             
6671     /*
6672  * Based on:
6673  * Ext JS Library 1.1.1
6674  * Copyright(c) 2006-2007, Ext JS, LLC.
6675  *
6676  * Originally Released Under LGPL - original licence link has changed is not relivant.
6677  *
6678  * Fork - LGPL
6679  * <script type="text/javascript">
6680  */
6681
6682  
6683 // was in Composite Element!??!?!
6684  
6685 (function(){
6686     var D = Roo.lib.Dom;
6687     var E = Roo.lib.Event;
6688     var A = Roo.lib.Anim;
6689
6690     // local style camelizing for speed
6691     var propCache = {};
6692     var camelRe = /(-[a-z])/gi;
6693     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6694     var view = document.defaultView;
6695
6696 /**
6697  * @class Roo.Element
6698  * Represents an Element in the DOM.<br><br>
6699  * Usage:<br>
6700 <pre><code>
6701 var el = Roo.get("my-div");
6702
6703 // or with getEl
6704 var el = getEl("my-div");
6705
6706 // or with a DOM element
6707 var el = Roo.get(myDivElement);
6708 </code></pre>
6709  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6710  * each call instead of constructing a new one.<br><br>
6711  * <b>Animations</b><br />
6712  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6713  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6714 <pre>
6715 Option    Default   Description
6716 --------- --------  ---------------------------------------------
6717 duration  .35       The duration of the animation in seconds
6718 easing    easeOut   The YUI easing method
6719 callback  none      A function to execute when the anim completes
6720 scope     this      The scope (this) of the callback function
6721 </pre>
6722 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6723 * manipulate the animation. Here's an example:
6724 <pre><code>
6725 var el = Roo.get("my-div");
6726
6727 // no animation
6728 el.setWidth(100);
6729
6730 // default animation
6731 el.setWidth(100, true);
6732
6733 // animation with some options set
6734 el.setWidth(100, {
6735     duration: 1,
6736     callback: this.foo,
6737     scope: this
6738 });
6739
6740 // using the "anim" property to get the Anim object
6741 var opt = {
6742     duration: 1,
6743     callback: this.foo,
6744     scope: this
6745 };
6746 el.setWidth(100, opt);
6747 ...
6748 if(opt.anim.isAnimated()){
6749     opt.anim.stop();
6750 }
6751 </code></pre>
6752 * <b> Composite (Collections of) Elements</b><br />
6753  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6754  * @constructor Create a new Element directly.
6755  * @param {String/HTMLElement} element
6756  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6757  */
6758     Roo.Element = function(element, forceNew){
6759         var dom = typeof element == "string" ?
6760                 document.getElementById(element) : element;
6761         if(!dom){ // invalid id/element
6762             return null;
6763         }
6764         var id = dom.id;
6765         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6766             return Roo.Element.cache[id];
6767         }
6768
6769         /**
6770          * The DOM element
6771          * @type HTMLElement
6772          */
6773         this.dom = dom;
6774
6775         /**
6776          * The DOM element ID
6777          * @type String
6778          */
6779         this.id = id || Roo.id(dom);
6780     };
6781
6782     var El = Roo.Element;
6783
6784     El.prototype = {
6785         /**
6786          * The element's default display mode  (defaults to "")
6787          * @type String
6788          */
6789         originalDisplay : "",
6790
6791         visibilityMode : 1,
6792         /**
6793          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6794          * @type String
6795          */
6796         defaultUnit : "px",
6797         /**
6798          * Sets the element's visibility mode. When setVisible() is called it
6799          * will use this to determine whether to set the visibility or the display property.
6800          * @param visMode Element.VISIBILITY or Element.DISPLAY
6801          * @return {Roo.Element} this
6802          */
6803         setVisibilityMode : function(visMode){
6804             this.visibilityMode = visMode;
6805             return this;
6806         },
6807         /**
6808          * Convenience method for setVisibilityMode(Element.DISPLAY)
6809          * @param {String} display (optional) What to set display to when visible
6810          * @return {Roo.Element} this
6811          */
6812         enableDisplayMode : function(display){
6813             this.setVisibilityMode(El.DISPLAY);
6814             if(typeof display != "undefined") this.originalDisplay = display;
6815             return this;
6816         },
6817
6818         /**
6819          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6820          * @param {String} selector The simple selector to test
6821          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6822                 search as a number or element (defaults to 10 || document.body)
6823          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6824          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6825          */
6826         findParent : function(simpleSelector, maxDepth, returnEl){
6827             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6828             maxDepth = maxDepth || 50;
6829             if(typeof maxDepth != "number"){
6830                 stopEl = Roo.getDom(maxDepth);
6831                 maxDepth = 10;
6832             }
6833             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6834                 if(dq.is(p, simpleSelector)){
6835                     return returnEl ? Roo.get(p) : p;
6836                 }
6837                 depth++;
6838                 p = p.parentNode;
6839             }
6840             return null;
6841         },
6842
6843
6844         /**
6845          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6846          * @param {String} selector The simple selector to test
6847          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6848                 search as a number or element (defaults to 10 || document.body)
6849          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6850          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6851          */
6852         findParentNode : function(simpleSelector, maxDepth, returnEl){
6853             var p = Roo.fly(this.dom.parentNode, '_internal');
6854             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6855         },
6856
6857         /**
6858          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6859          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6860          * @param {String} selector The simple selector to test
6861          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6862                 search as a number or element (defaults to 10 || document.body)
6863          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6864          */
6865         up : function(simpleSelector, maxDepth){
6866             return this.findParentNode(simpleSelector, maxDepth, true);
6867         },
6868
6869
6870
6871         /**
6872          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6873          * @param {String} selector The simple selector to test
6874          * @return {Boolean} True if this element matches the selector, else false
6875          */
6876         is : function(simpleSelector){
6877             return Roo.DomQuery.is(this.dom, simpleSelector);
6878         },
6879
6880         /**
6881          * Perform animation on this element.
6882          * @param {Object} args The YUI animation control args
6883          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6884          * @param {Function} onComplete (optional) Function to call when animation completes
6885          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6886          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6887          * @return {Roo.Element} this
6888          */
6889         animate : function(args, duration, onComplete, easing, animType){
6890             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6891             return this;
6892         },
6893
6894         /*
6895          * @private Internal animation call
6896          */
6897         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6898             animType = animType || 'run';
6899             opt = opt || {};
6900             var anim = Roo.lib.Anim[animType](
6901                 this.dom, args,
6902                 (opt.duration || defaultDur) || .35,
6903                 (opt.easing || defaultEase) || 'easeOut',
6904                 function(){
6905                     Roo.callback(cb, this);
6906                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6907                 },
6908                 this
6909             );
6910             opt.anim = anim;
6911             return anim;
6912         },
6913
6914         // private legacy anim prep
6915         preanim : function(a, i){
6916             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6917         },
6918
6919         /**
6920          * Removes worthless text nodes
6921          * @param {Boolean} forceReclean (optional) By default the element
6922          * keeps track if it has been cleaned already so
6923          * you can call this over and over. However, if you update the element and
6924          * need to force a reclean, you can pass true.
6925          */
6926         clean : function(forceReclean){
6927             if(this.isCleaned && forceReclean !== true){
6928                 return this;
6929             }
6930             var ns = /\S/;
6931             var d = this.dom, n = d.firstChild, ni = -1;
6932             while(n){
6933                 var nx = n.nextSibling;
6934                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6935                     d.removeChild(n);
6936                 }else{
6937                     n.nodeIndex = ++ni;
6938                 }
6939                 n = nx;
6940             }
6941             this.isCleaned = true;
6942             return this;
6943         },
6944
6945         // private
6946         calcOffsetsTo : function(el){
6947             el = Roo.get(el);
6948             var d = el.dom;
6949             var restorePos = false;
6950             if(el.getStyle('position') == 'static'){
6951                 el.position('relative');
6952                 restorePos = true;
6953             }
6954             var x = 0, y =0;
6955             var op = this.dom;
6956             while(op && op != d && op.tagName != 'HTML'){
6957                 x+= op.offsetLeft;
6958                 y+= op.offsetTop;
6959                 op = op.offsetParent;
6960             }
6961             if(restorePos){
6962                 el.position('static');
6963             }
6964             return [x, y];
6965         },
6966
6967         /**
6968          * Scrolls this element into view within the passed container.
6969          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6970          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6971          * @return {Roo.Element} this
6972          */
6973         scrollIntoView : function(container, hscroll){
6974             var c = Roo.getDom(container) || document.body;
6975             var el = this.dom;
6976
6977             var o = this.calcOffsetsTo(c),
6978                 l = o[0],
6979                 t = o[1],
6980                 b = t+el.offsetHeight,
6981                 r = l+el.offsetWidth;
6982
6983             var ch = c.clientHeight;
6984             var ct = parseInt(c.scrollTop, 10);
6985             var cl = parseInt(c.scrollLeft, 10);
6986             var cb = ct + ch;
6987             var cr = cl + c.clientWidth;
6988
6989             if(t < ct){
6990                 c.scrollTop = t;
6991             }else if(b > cb){
6992                 c.scrollTop = b-ch;
6993             }
6994
6995             if(hscroll !== false){
6996                 if(l < cl){
6997                     c.scrollLeft = l;
6998                 }else if(r > cr){
6999                     c.scrollLeft = r-c.clientWidth;
7000                 }
7001             }
7002             return this;
7003         },
7004
7005         // private
7006         scrollChildIntoView : function(child, hscroll){
7007             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7008         },
7009
7010         /**
7011          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7012          * the new height may not be available immediately.
7013          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7014          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7015          * @param {Function} onComplete (optional) Function to call when animation completes
7016          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7017          * @return {Roo.Element} this
7018          */
7019         autoHeight : function(animate, duration, onComplete, easing){
7020             var oldHeight = this.getHeight();
7021             this.clip();
7022             this.setHeight(1); // force clipping
7023             setTimeout(function(){
7024                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7025                 if(!animate){
7026                     this.setHeight(height);
7027                     this.unclip();
7028                     if(typeof onComplete == "function"){
7029                         onComplete();
7030                     }
7031                 }else{
7032                     this.setHeight(oldHeight); // restore original height
7033                     this.setHeight(height, animate, duration, function(){
7034                         this.unclip();
7035                         if(typeof onComplete == "function") onComplete();
7036                     }.createDelegate(this), easing);
7037                 }
7038             }.createDelegate(this), 0);
7039             return this;
7040         },
7041
7042         /**
7043          * Returns true if this element is an ancestor of the passed element
7044          * @param {HTMLElement/String} el The element to check
7045          * @return {Boolean} True if this element is an ancestor of el, else false
7046          */
7047         contains : function(el){
7048             if(!el){return false;}
7049             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7050         },
7051
7052         /**
7053          * Checks whether the element is currently visible using both visibility and display properties.
7054          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7055          * @return {Boolean} True if the element is currently visible, else false
7056          */
7057         isVisible : function(deep) {
7058             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7059             if(deep !== true || !vis){
7060                 return vis;
7061             }
7062             var p = this.dom.parentNode;
7063             while(p && p.tagName.toLowerCase() != "body"){
7064                 if(!Roo.fly(p, '_isVisible').isVisible()){
7065                     return false;
7066                 }
7067                 p = p.parentNode;
7068             }
7069             return true;
7070         },
7071
7072         /**
7073          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7074          * @param {String} selector The CSS selector
7075          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7076          * @return {CompositeElement/CompositeElementLite} The composite element
7077          */
7078         select : function(selector, unique){
7079             return El.select(selector, unique, this.dom);
7080         },
7081
7082         /**
7083          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7084          * @param {String} selector The CSS selector
7085          * @return {Array} An array of the matched nodes
7086          */
7087         query : function(selector, unique){
7088             return Roo.DomQuery.select(selector, this.dom);
7089         },
7090
7091         /**
7092          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7093          * @param {String} selector The CSS selector
7094          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7095          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7096          */
7097         child : function(selector, returnDom){
7098             var n = Roo.DomQuery.selectNode(selector, this.dom);
7099             return returnDom ? n : Roo.get(n);
7100         },
7101
7102         /**
7103          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7104          * @param {String} selector The CSS selector
7105          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7106          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7107          */
7108         down : function(selector, returnDom){
7109             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7110             return returnDom ? n : Roo.get(n);
7111         },
7112
7113         /**
7114          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7115          * @param {String} group The group the DD object is member of
7116          * @param {Object} config The DD config object
7117          * @param {Object} overrides An object containing methods to override/implement on the DD object
7118          * @return {Roo.dd.DD} The DD object
7119          */
7120         initDD : function(group, config, overrides){
7121             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7122             return Roo.apply(dd, overrides);
7123         },
7124
7125         /**
7126          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7127          * @param {String} group The group the DDProxy object is member of
7128          * @param {Object} config The DDProxy config object
7129          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7130          * @return {Roo.dd.DDProxy} The DDProxy object
7131          */
7132         initDDProxy : function(group, config, overrides){
7133             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7134             return Roo.apply(dd, overrides);
7135         },
7136
7137         /**
7138          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7139          * @param {String} group The group the DDTarget object is member of
7140          * @param {Object} config The DDTarget config object
7141          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7142          * @return {Roo.dd.DDTarget} The DDTarget object
7143          */
7144         initDDTarget : function(group, config, overrides){
7145             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7146             return Roo.apply(dd, overrides);
7147         },
7148
7149         /**
7150          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7151          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7152          * @param {Boolean} visible Whether the element is visible
7153          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7154          * @return {Roo.Element} this
7155          */
7156          setVisible : function(visible, animate){
7157             if(!animate || !A){
7158                 if(this.visibilityMode == El.DISPLAY){
7159                     this.setDisplayed(visible);
7160                 }else{
7161                     this.fixDisplay();
7162                     this.dom.style.visibility = visible ? "visible" : "hidden";
7163                 }
7164             }else{
7165                 // closure for composites
7166                 var dom = this.dom;
7167                 var visMode = this.visibilityMode;
7168                 if(visible){
7169                     this.setOpacity(.01);
7170                     this.setVisible(true);
7171                 }
7172                 this.anim({opacity: { to: (visible?1:0) }},
7173                       this.preanim(arguments, 1),
7174                       null, .35, 'easeIn', function(){
7175                          if(!visible){
7176                              if(visMode == El.DISPLAY){
7177                                  dom.style.display = "none";
7178                              }else{
7179                                  dom.style.visibility = "hidden";
7180                              }
7181                              Roo.get(dom).setOpacity(1);
7182                          }
7183                      });
7184             }
7185             return this;
7186         },
7187
7188         /**
7189          * Returns true if display is not "none"
7190          * @return {Boolean}
7191          */
7192         isDisplayed : function() {
7193             return this.getStyle("display") != "none";
7194         },
7195
7196         /**
7197          * Toggles the element's visibility or display, depending on visibility mode.
7198          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7199          * @return {Roo.Element} this
7200          */
7201         toggle : function(animate){
7202             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7203             return this;
7204         },
7205
7206         /**
7207          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7208          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7209          * @return {Roo.Element} this
7210          */
7211         setDisplayed : function(value) {
7212             if(typeof value == "boolean"){
7213                value = value ? this.originalDisplay : "none";
7214             }
7215             this.setStyle("display", value);
7216             return this;
7217         },
7218
7219         /**
7220          * Tries to focus the element. Any exceptions are caught and ignored.
7221          * @return {Roo.Element} this
7222          */
7223         focus : function() {
7224             try{
7225                 this.dom.focus();
7226             }catch(e){}
7227             return this;
7228         },
7229
7230         /**
7231          * Tries to blur the element. Any exceptions are caught and ignored.
7232          * @return {Roo.Element} this
7233          */
7234         blur : function() {
7235             try{
7236                 this.dom.blur();
7237             }catch(e){}
7238             return this;
7239         },
7240
7241         /**
7242          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7243          * @param {String/Array} className The CSS class to add, or an array of classes
7244          * @return {Roo.Element} this
7245          */
7246         addClass : function(className){
7247             if(className instanceof Array){
7248                 for(var i = 0, len = className.length; i < len; i++) {
7249                     this.addClass(className[i]);
7250                 }
7251             }else{
7252                 if(className && !this.hasClass(className)){
7253                     this.dom.className = this.dom.className + " " + className;
7254                 }
7255             }
7256             return this;
7257         },
7258
7259         /**
7260          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7261          * @param {String/Array} className The CSS class to add, or an array of classes
7262          * @return {Roo.Element} this
7263          */
7264         radioClass : function(className){
7265             var siblings = this.dom.parentNode.childNodes;
7266             for(var i = 0; i < siblings.length; i++) {
7267                 var s = siblings[i];
7268                 if(s.nodeType == 1){
7269                     Roo.get(s).removeClass(className);
7270                 }
7271             }
7272             this.addClass(className);
7273             return this;
7274         },
7275
7276         /**
7277          * Removes one or more CSS classes from the element.
7278          * @param {String/Array} className The CSS class to remove, or an array of classes
7279          * @return {Roo.Element} this
7280          */
7281         removeClass : function(className){
7282             if(!className || !this.dom.className){
7283                 return this;
7284             }
7285             if(className instanceof Array){
7286                 for(var i = 0, len = className.length; i < len; i++) {
7287                     this.removeClass(className[i]);
7288                 }
7289             }else{
7290                 if(this.hasClass(className)){
7291                     var re = this.classReCache[className];
7292                     if (!re) {
7293                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7294                        this.classReCache[className] = re;
7295                     }
7296                     this.dom.className =
7297                         this.dom.className.replace(re, " ");
7298                 }
7299             }
7300             return this;
7301         },
7302
7303         // private
7304         classReCache: {},
7305
7306         /**
7307          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7308          * @param {String} className The CSS class to toggle
7309          * @return {Roo.Element} this
7310          */
7311         toggleClass : function(className){
7312             if(this.hasClass(className)){
7313                 this.removeClass(className);
7314             }else{
7315                 this.addClass(className);
7316             }
7317             return this;
7318         },
7319
7320         /**
7321          * Checks if the specified CSS class exists on this element's DOM node.
7322          * @param {String} className The CSS class to check for
7323          * @return {Boolean} True if the class exists, else false
7324          */
7325         hasClass : function(className){
7326             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7327         },
7328
7329         /**
7330          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7331          * @param {String} oldClassName The CSS class to replace
7332          * @param {String} newClassName The replacement CSS class
7333          * @return {Roo.Element} this
7334          */
7335         replaceClass : function(oldClassName, newClassName){
7336             this.removeClass(oldClassName);
7337             this.addClass(newClassName);
7338             return this;
7339         },
7340
7341         /**
7342          * Returns an object with properties matching the styles requested.
7343          * For example, el.getStyles('color', 'font-size', 'width') might return
7344          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7345          * @param {String} style1 A style name
7346          * @param {String} style2 A style name
7347          * @param {String} etc.
7348          * @return {Object} The style object
7349          */
7350         getStyles : function(){
7351             var a = arguments, len = a.length, r = {};
7352             for(var i = 0; i < len; i++){
7353                 r[a[i]] = this.getStyle(a[i]);
7354             }
7355             return r;
7356         },
7357
7358         /**
7359          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7360          * @param {String} property The style property whose value is returned.
7361          * @return {String} The current value of the style property for this element.
7362          */
7363         getStyle : function(){
7364             return view && view.getComputedStyle ?
7365                 function(prop){
7366                     var el = this.dom, v, cs, camel;
7367                     if(prop == 'float'){
7368                         prop = "cssFloat";
7369                     }
7370                     if(el.style && (v = el.style[prop])){
7371                         return v;
7372                     }
7373                     if(cs = view.getComputedStyle(el, "")){
7374                         if(!(camel = propCache[prop])){
7375                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7376                         }
7377                         return cs[camel];
7378                     }
7379                     return null;
7380                 } :
7381                 function(prop){
7382                     var el = this.dom, v, cs, camel;
7383                     if(prop == 'opacity'){
7384                         if(typeof el.style.filter == 'string'){
7385                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7386                             if(m){
7387                                 var fv = parseFloat(m[1]);
7388                                 if(!isNaN(fv)){
7389                                     return fv ? fv / 100 : 0;
7390                                 }
7391                             }
7392                         }
7393                         return 1;
7394                     }else if(prop == 'float'){
7395                         prop = "styleFloat";
7396                     }
7397                     if(!(camel = propCache[prop])){
7398                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7399                     }
7400                     if(v = el.style[camel]){
7401                         return v;
7402                     }
7403                     if(cs = el.currentStyle){
7404                         return cs[camel];
7405                     }
7406                     return null;
7407                 };
7408         }(),
7409
7410         /**
7411          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7412          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7413          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7414          * @return {Roo.Element} this
7415          */
7416         setStyle : function(prop, value){
7417             if(typeof prop == "string"){
7418                 
7419                 if (prop == 'float') {
7420                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7421                     return this;
7422                 }
7423                 
7424                 var camel;
7425                 if(!(camel = propCache[prop])){
7426                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7427                 }
7428                 
7429                 if(camel == 'opacity') {
7430                     this.setOpacity(value);
7431                 }else{
7432                     this.dom.style[camel] = value;
7433                 }
7434             }else{
7435                 for(var style in prop){
7436                     if(typeof prop[style] != "function"){
7437                        this.setStyle(style, prop[style]);
7438                     }
7439                 }
7440             }
7441             return this;
7442         },
7443
7444         /**
7445          * More flexible version of {@link #setStyle} for setting style properties.
7446          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7447          * a function which returns such a specification.
7448          * @return {Roo.Element} this
7449          */
7450         applyStyles : function(style){
7451             Roo.DomHelper.applyStyles(this.dom, style);
7452             return this;
7453         },
7454
7455         /**
7456           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7457           * @return {Number} The X position of the element
7458           */
7459         getX : function(){
7460             return D.getX(this.dom);
7461         },
7462
7463         /**
7464           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7465           * @return {Number} The Y position of the element
7466           */
7467         getY : function(){
7468             return D.getY(this.dom);
7469         },
7470
7471         /**
7472           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7473           * @return {Array} The XY position of the element
7474           */
7475         getXY : function(){
7476             return D.getXY(this.dom);
7477         },
7478
7479         /**
7480          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7481          * @param {Number} The X position of the element
7482          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7483          * @return {Roo.Element} this
7484          */
7485         setX : function(x, animate){
7486             if(!animate || !A){
7487                 D.setX(this.dom, x);
7488             }else{
7489                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7490             }
7491             return this;
7492         },
7493
7494         /**
7495          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7496          * @param {Number} The Y position of the element
7497          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7498          * @return {Roo.Element} this
7499          */
7500         setY : function(y, animate){
7501             if(!animate || !A){
7502                 D.setY(this.dom, y);
7503             }else{
7504                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7505             }
7506             return this;
7507         },
7508
7509         /**
7510          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7511          * @param {String} left The left CSS property value
7512          * @return {Roo.Element} this
7513          */
7514         setLeft : function(left){
7515             this.setStyle("left", this.addUnits(left));
7516             return this;
7517         },
7518
7519         /**
7520          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7521          * @param {String} top The top CSS property value
7522          * @return {Roo.Element} this
7523          */
7524         setTop : function(top){
7525             this.setStyle("top", this.addUnits(top));
7526             return this;
7527         },
7528
7529         /**
7530          * Sets the element's CSS right style.
7531          * @param {String} right The right CSS property value
7532          * @return {Roo.Element} this
7533          */
7534         setRight : function(right){
7535             this.setStyle("right", this.addUnits(right));
7536             return this;
7537         },
7538
7539         /**
7540          * Sets the element's CSS bottom style.
7541          * @param {String} bottom The bottom CSS property value
7542          * @return {Roo.Element} this
7543          */
7544         setBottom : function(bottom){
7545             this.setStyle("bottom", this.addUnits(bottom));
7546             return this;
7547         },
7548
7549         /**
7550          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7551          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7552          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7553          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7554          * @return {Roo.Element} this
7555          */
7556         setXY : function(pos, animate){
7557             if(!animate || !A){
7558                 D.setXY(this.dom, pos);
7559             }else{
7560                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7561             }
7562             return this;
7563         },
7564
7565         /**
7566          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7567          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7568          * @param {Number} x X value for new position (coordinates are page-based)
7569          * @param {Number} y Y value for new position (coordinates are page-based)
7570          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7571          * @return {Roo.Element} this
7572          */
7573         setLocation : function(x, y, animate){
7574             this.setXY([x, y], this.preanim(arguments, 2));
7575             return this;
7576         },
7577
7578         /**
7579          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7580          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7581          * @param {Number} x X value for new position (coordinates are page-based)
7582          * @param {Number} y Y value for new position (coordinates are page-based)
7583          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7584          * @return {Roo.Element} this
7585          */
7586         moveTo : function(x, y, animate){
7587             this.setXY([x, y], this.preanim(arguments, 2));
7588             return this;
7589         },
7590
7591         /**
7592          * Returns the region of the given element.
7593          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7594          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7595          */
7596         getRegion : function(){
7597             return D.getRegion(this.dom);
7598         },
7599
7600         /**
7601          * Returns the offset height of the element
7602          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7603          * @return {Number} The element's height
7604          */
7605         getHeight : function(contentHeight){
7606             var h = this.dom.offsetHeight || 0;
7607             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7608         },
7609
7610         /**
7611          * Returns the offset width of the element
7612          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7613          * @return {Number} The element's width
7614          */
7615         getWidth : function(contentWidth){
7616             var w = this.dom.offsetWidth || 0;
7617             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7618         },
7619
7620         /**
7621          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7622          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7623          * if a height has not been set using CSS.
7624          * @return {Number}
7625          */
7626         getComputedHeight : function(){
7627             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7628             if(!h){
7629                 h = parseInt(this.getStyle('height'), 10) || 0;
7630                 if(!this.isBorderBox()){
7631                     h += this.getFrameWidth('tb');
7632                 }
7633             }
7634             return h;
7635         },
7636
7637         /**
7638          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7639          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7640          * if a width has not been set using CSS.
7641          * @return {Number}
7642          */
7643         getComputedWidth : function(){
7644             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7645             if(!w){
7646                 w = parseInt(this.getStyle('width'), 10) || 0;
7647                 if(!this.isBorderBox()){
7648                     w += this.getFrameWidth('lr');
7649                 }
7650             }
7651             return w;
7652         },
7653
7654         /**
7655          * Returns the size of the element.
7656          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7657          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7658          */
7659         getSize : function(contentSize){
7660             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7661         },
7662
7663         /**
7664          * Returns the width and height of the viewport.
7665          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7666          */
7667         getViewSize : function(){
7668             var d = this.dom, doc = document, aw = 0, ah = 0;
7669             if(d == doc || d == doc.body){
7670                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7671             }else{
7672                 return {
7673                     width : d.clientWidth,
7674                     height: d.clientHeight
7675                 };
7676             }
7677         },
7678
7679         /**
7680          * Returns the value of the "value" attribute
7681          * @param {Boolean} asNumber true to parse the value as a number
7682          * @return {String/Number}
7683          */
7684         getValue : function(asNumber){
7685             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7686         },
7687
7688         // private
7689         adjustWidth : function(width){
7690             if(typeof width == "number"){
7691                 if(this.autoBoxAdjust && !this.isBorderBox()){
7692                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7693                 }
7694                 if(width < 0){
7695                     width = 0;
7696                 }
7697             }
7698             return width;
7699         },
7700
7701         // private
7702         adjustHeight : function(height){
7703             if(typeof height == "number"){
7704                if(this.autoBoxAdjust && !this.isBorderBox()){
7705                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7706                }
7707                if(height < 0){
7708                    height = 0;
7709                }
7710             }
7711             return height;
7712         },
7713
7714         /**
7715          * Set the width of the element
7716          * @param {Number} width The new width
7717          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7718          * @return {Roo.Element} this
7719          */
7720         setWidth : function(width, animate){
7721             width = this.adjustWidth(width);
7722             if(!animate || !A){
7723                 this.dom.style.width = this.addUnits(width);
7724             }else{
7725                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7726             }
7727             return this;
7728         },
7729
7730         /**
7731          * Set the height of the element
7732          * @param {Number} height The new height
7733          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7734          * @return {Roo.Element} this
7735          */
7736          setHeight : function(height, animate){
7737             height = this.adjustHeight(height);
7738             if(!animate || !A){
7739                 this.dom.style.height = this.addUnits(height);
7740             }else{
7741                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7748          * @param {Number} width The new width
7749          * @param {Number} height The new height
7750          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7751          * @return {Roo.Element} this
7752          */
7753          setSize : function(width, height, animate){
7754             if(typeof width == "object"){ // in case of object from getSize()
7755                 height = width.height; width = width.width;
7756             }
7757             width = this.adjustWidth(width); height = this.adjustHeight(height);
7758             if(!animate || !A){
7759                 this.dom.style.width = this.addUnits(width);
7760                 this.dom.style.height = this.addUnits(height);
7761             }else{
7762                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7763             }
7764             return this;
7765         },
7766
7767         /**
7768          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7769          * @param {Number} x X value for new position (coordinates are page-based)
7770          * @param {Number} y Y value for new position (coordinates are page-based)
7771          * @param {Number} width The new width
7772          * @param {Number} height The new height
7773          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7774          * @return {Roo.Element} this
7775          */
7776         setBounds : function(x, y, width, height, animate){
7777             if(!animate || !A){
7778                 this.setSize(width, height);
7779                 this.setLocation(x, y);
7780             }else{
7781                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7782                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7783                               this.preanim(arguments, 4), 'motion');
7784             }
7785             return this;
7786         },
7787
7788         /**
7789          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
7790          * @param {Roo.lib.Region} region The region to fill
7791          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7792          * @return {Roo.Element} this
7793          */
7794         setRegion : function(region, animate){
7795             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7796             return this;
7797         },
7798
7799         /**
7800          * Appends an event handler
7801          *
7802          * @param {String}   eventName     The type of event to append
7803          * @param {Function} fn        The method the event invokes
7804          * @param {Object} scope       (optional) The scope (this object) of the fn
7805          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7806          */
7807         addListener : function(eventName, fn, scope, options){
7808             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7809         },
7810
7811         /**
7812          * Removes an event handler from this element
7813          * @param {String} eventName the type of event to remove
7814          * @param {Function} fn the method the event invokes
7815          * @return {Roo.Element} this
7816          */
7817         removeListener : function(eventName, fn){
7818             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7819             return this;
7820         },
7821
7822         /**
7823          * Removes all previous added listeners from this element
7824          * @return {Roo.Element} this
7825          */
7826         removeAllListeners : function(){
7827             E.purgeElement(this.dom);
7828             return this;
7829         },
7830
7831         relayEvent : function(eventName, observable){
7832             this.on(eventName, function(e){
7833                 observable.fireEvent(eventName, e);
7834             });
7835         },
7836
7837         /**
7838          * Set the opacity of the element
7839          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7840          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7841          * @return {Roo.Element} this
7842          */
7843          setOpacity : function(opacity, animate){
7844             if(!animate || !A){
7845                 var s = this.dom.style;
7846                 if(Roo.isIE){
7847                     s.zoom = 1;
7848                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7849                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7850                 }else{
7851                     s.opacity = opacity;
7852                 }
7853             }else{
7854                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7855             }
7856             return this;
7857         },
7858
7859         /**
7860          * Gets the left X coordinate
7861          * @param {Boolean} local True to get the local css position instead of page coordinate
7862          * @return {Number}
7863          */
7864         getLeft : function(local){
7865             if(!local){
7866                 return this.getX();
7867             }else{
7868                 return parseInt(this.getStyle("left"), 10) || 0;
7869             }
7870         },
7871
7872         /**
7873          * Gets the right X coordinate of the element (element X position + element width)
7874          * @param {Boolean} local True to get the local css position instead of page coordinate
7875          * @return {Number}
7876          */
7877         getRight : function(local){
7878             if(!local){
7879                 return this.getX() + this.getWidth();
7880             }else{
7881                 return (this.getLeft(true) + this.getWidth()) || 0;
7882             }
7883         },
7884
7885         /**
7886          * Gets the top Y coordinate
7887          * @param {Boolean} local True to get the local css position instead of page coordinate
7888          * @return {Number}
7889          */
7890         getTop : function(local) {
7891             if(!local){
7892                 return this.getY();
7893             }else{
7894                 return parseInt(this.getStyle("top"), 10) || 0;
7895             }
7896         },
7897
7898         /**
7899          * Gets the bottom Y coordinate of the element (element Y position + element height)
7900          * @param {Boolean} local True to get the local css position instead of page coordinate
7901          * @return {Number}
7902          */
7903         getBottom : function(local){
7904             if(!local){
7905                 return this.getY() + this.getHeight();
7906             }else{
7907                 return (this.getTop(true) + this.getHeight()) || 0;
7908             }
7909         },
7910
7911         /**
7912         * Initializes positioning on this element. If a desired position is not passed, it will make the
7913         * the element positioned relative IF it is not already positioned.
7914         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7915         * @param {Number} zIndex (optional) The zIndex to apply
7916         * @param {Number} x (optional) Set the page X position
7917         * @param {Number} y (optional) Set the page Y position
7918         */
7919         position : function(pos, zIndex, x, y){
7920             if(!pos){
7921                if(this.getStyle('position') == 'static'){
7922                    this.setStyle('position', 'relative');
7923                }
7924             }else{
7925                 this.setStyle("position", pos);
7926             }
7927             if(zIndex){
7928                 this.setStyle("z-index", zIndex);
7929             }
7930             if(x !== undefined && y !== undefined){
7931                 this.setXY([x, y]);
7932             }else if(x !== undefined){
7933                 this.setX(x);
7934             }else if(y !== undefined){
7935                 this.setY(y);
7936             }
7937         },
7938
7939         /**
7940         * Clear positioning back to the default when the document was loaded
7941         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7942         * @return {Roo.Element} this
7943          */
7944         clearPositioning : function(value){
7945             value = value ||'';
7946             this.setStyle({
7947                 "left": value,
7948                 "right": value,
7949                 "top": value,
7950                 "bottom": value,
7951                 "z-index": "",
7952                 "position" : "static"
7953             });
7954             return this;
7955         },
7956
7957         /**
7958         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7959         * snapshot before performing an update and then restoring the element.
7960         * @return {Object}
7961         */
7962         getPositioning : function(){
7963             var l = this.getStyle("left");
7964             var t = this.getStyle("top");
7965             return {
7966                 "position" : this.getStyle("position"),
7967                 "left" : l,
7968                 "right" : l ? "" : this.getStyle("right"),
7969                 "top" : t,
7970                 "bottom" : t ? "" : this.getStyle("bottom"),
7971                 "z-index" : this.getStyle("z-index")
7972             };
7973         },
7974
7975         /**
7976          * Gets the width of the border(s) for the specified side(s)
7977          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7978          * passing lr would get the border (l)eft width + the border (r)ight width.
7979          * @return {Number} The width of the sides passed added together
7980          */
7981         getBorderWidth : function(side){
7982             return this.addStyles(side, El.borders);
7983         },
7984
7985         /**
7986          * Gets the width of the padding(s) for the specified side(s)
7987          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7988          * passing lr would get the padding (l)eft + the padding (r)ight.
7989          * @return {Number} The padding of the sides passed added together
7990          */
7991         getPadding : function(side){
7992             return this.addStyles(side, El.paddings);
7993         },
7994
7995         /**
7996         * Set positioning with an object returned by getPositioning().
7997         * @param {Object} posCfg
7998         * @return {Roo.Element} this
7999          */
8000         setPositioning : function(pc){
8001             this.applyStyles(pc);
8002             if(pc.right == "auto"){
8003                 this.dom.style.right = "";
8004             }
8005             if(pc.bottom == "auto"){
8006                 this.dom.style.bottom = "";
8007             }
8008             return this;
8009         },
8010
8011         // private
8012         fixDisplay : function(){
8013             if(this.getStyle("display") == "none"){
8014                 this.setStyle("visibility", "hidden");
8015                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8016                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8017                     this.setStyle("display", "block");
8018                 }
8019             }
8020         },
8021
8022         /**
8023          * Quick set left and top adding default units
8024          * @param {String} left The left CSS property value
8025          * @param {String} top The top CSS property value
8026          * @return {Roo.Element} this
8027          */
8028          setLeftTop : function(left, top){
8029             this.dom.style.left = this.addUnits(left);
8030             this.dom.style.top = this.addUnits(top);
8031             return this;
8032         },
8033
8034         /**
8035          * Move this element relative to its current position.
8036          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8037          * @param {Number} distance How far to move the element in pixels
8038          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8039          * @return {Roo.Element} this
8040          */
8041          move : function(direction, distance, animate){
8042             var xy = this.getXY();
8043             direction = direction.toLowerCase();
8044             switch(direction){
8045                 case "l":
8046                 case "left":
8047                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8048                     break;
8049                case "r":
8050                case "right":
8051                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8052                     break;
8053                case "t":
8054                case "top":
8055                case "up":
8056                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8057                     break;
8058                case "b":
8059                case "bottom":
8060                case "down":
8061                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8062                     break;
8063             }
8064             return this;
8065         },
8066
8067         /**
8068          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8069          * @return {Roo.Element} this
8070          */
8071         clip : function(){
8072             if(!this.isClipped){
8073                this.isClipped = true;
8074                this.originalClip = {
8075                    "o": this.getStyle("overflow"),
8076                    "x": this.getStyle("overflow-x"),
8077                    "y": this.getStyle("overflow-y")
8078                };
8079                this.setStyle("overflow", "hidden");
8080                this.setStyle("overflow-x", "hidden");
8081                this.setStyle("overflow-y", "hidden");
8082             }
8083             return this;
8084         },
8085
8086         /**
8087          *  Return clipping (overflow) to original clipping before clip() was called
8088          * @return {Roo.Element} this
8089          */
8090         unclip : function(){
8091             if(this.isClipped){
8092                 this.isClipped = false;
8093                 var o = this.originalClip;
8094                 if(o.o){this.setStyle("overflow", o.o);}
8095                 if(o.x){this.setStyle("overflow-x", o.x);}
8096                 if(o.y){this.setStyle("overflow-y", o.y);}
8097             }
8098             return this;
8099         },
8100
8101
8102         /**
8103          * Gets the x,y coordinates specified by the anchor position on the element.
8104          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8105          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8106          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8107          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8108          * @return {Array} [x, y] An array containing the element's x and y coordinates
8109          */
8110         getAnchorXY : function(anchor, local, s){
8111             //Passing a different size is useful for pre-calculating anchors,
8112             //especially for anchored animations that change the el size.
8113
8114             var w, h, vp = false;
8115             if(!s){
8116                 var d = this.dom;
8117                 if(d == document.body || d == document){
8118                     vp = true;
8119                     w = D.getViewWidth(); h = D.getViewHeight();
8120                 }else{
8121                     w = this.getWidth(); h = this.getHeight();
8122                 }
8123             }else{
8124                 w = s.width;  h = s.height;
8125             }
8126             var x = 0, y = 0, r = Math.round;
8127             switch((anchor || "tl").toLowerCase()){
8128                 case "c":
8129                     x = r(w*.5);
8130                     y = r(h*.5);
8131                 break;
8132                 case "t":
8133                     x = r(w*.5);
8134                     y = 0;
8135                 break;
8136                 case "l":
8137                     x = 0;
8138                     y = r(h*.5);
8139                 break;
8140                 case "r":
8141                     x = w;
8142                     y = r(h*.5);
8143                 break;
8144                 case "b":
8145                     x = r(w*.5);
8146                     y = h;
8147                 break;
8148                 case "tl":
8149                     x = 0;
8150                     y = 0;
8151                 break;
8152                 case "bl":
8153                     x = 0;
8154                     y = h;
8155                 break;
8156                 case "br":
8157                     x = w;
8158                     y = h;
8159                 break;
8160                 case "tr":
8161                     x = w;
8162                     y = 0;
8163                 break;
8164             }
8165             if(local === true){
8166                 return [x, y];
8167             }
8168             if(vp){
8169                 var sc = this.getScroll();
8170                 return [x + sc.left, y + sc.top];
8171             }
8172             //Add the element's offset xy
8173             var o = this.getXY();
8174             return [x+o[0], y+o[1]];
8175         },
8176
8177         /**
8178          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8179          * supported position values.
8180          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8181          * @param {String} position The position to align to.
8182          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8183          * @return {Array} [x, y]
8184          */
8185         getAlignToXY : function(el, p, o){
8186             el = Roo.get(el);
8187             var d = this.dom;
8188             if(!el.dom){
8189                 throw "Element.alignTo with an element that doesn't exist";
8190             }
8191             var c = false; //constrain to viewport
8192             var p1 = "", p2 = "";
8193             o = o || [0,0];
8194
8195             if(!p){
8196                 p = "tl-bl";
8197             }else if(p == "?"){
8198                 p = "tl-bl?";
8199             }else if(p.indexOf("-") == -1){
8200                 p = "tl-" + p;
8201             }
8202             p = p.toLowerCase();
8203             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8204             if(!m){
8205                throw "Element.alignTo with an invalid alignment " + p;
8206             }
8207             p1 = m[1]; p2 = m[2]; c = !!m[3];
8208
8209             //Subtract the aligned el's internal xy from the target's offset xy
8210             //plus custom offset to get the aligned el's new offset xy
8211             var a1 = this.getAnchorXY(p1, true);
8212             var a2 = el.getAnchorXY(p2, false);
8213             var x = a2[0] - a1[0] + o[0];
8214             var y = a2[1] - a1[1] + o[1];
8215             if(c){
8216                 //constrain the aligned el to viewport if necessary
8217                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8218                 // 5px of margin for ie
8219                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8220
8221                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8222                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8223                 //otherwise swap the aligned el to the opposite border of the target.
8224                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8225                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8226                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8227                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8228
8229                var doc = document;
8230                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8231                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8232
8233                if((x+w) > dw + scrollX){
8234                     x = swapX ? r.left-w : dw+scrollX-w;
8235                 }
8236                if(x < scrollX){
8237                    x = swapX ? r.right : scrollX;
8238                }
8239                if((y+h) > dh + scrollY){
8240                     y = swapY ? r.top-h : dh+scrollY-h;
8241                 }
8242                if (y < scrollY){
8243                    y = swapY ? r.bottom : scrollY;
8244                }
8245             }
8246             return [x,y];
8247         },
8248
8249         // private
8250         getConstrainToXY : function(){
8251             var os = {top:0, left:0, bottom:0, right: 0};
8252
8253             return function(el, local, offsets, proposedXY){
8254                 el = Roo.get(el);
8255                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8256
8257                 var vw, vh, vx = 0, vy = 0;
8258                 if(el.dom == document.body || el.dom == document){
8259                     vw = Roo.lib.Dom.getViewWidth();
8260                     vh = Roo.lib.Dom.getViewHeight();
8261                 }else{
8262                     vw = el.dom.clientWidth;
8263                     vh = el.dom.clientHeight;
8264                     if(!local){
8265                         var vxy = el.getXY();
8266                         vx = vxy[0];
8267                         vy = vxy[1];
8268                     }
8269                 }
8270
8271                 var s = el.getScroll();
8272
8273                 vx += offsets.left + s.left;
8274                 vy += offsets.top + s.top;
8275
8276                 vw -= offsets.right;
8277                 vh -= offsets.bottom;
8278
8279                 var vr = vx+vw;
8280                 var vb = vy+vh;
8281
8282                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8283                 var x = xy[0], y = xy[1];
8284                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8285
8286                 // only move it if it needs it
8287                 var moved = false;
8288
8289                 // first validate right/bottom
8290                 if((x + w) > vr){
8291                     x = vr - w;
8292                     moved = true;
8293                 }
8294                 if((y + h) > vb){
8295                     y = vb - h;
8296                     moved = true;
8297                 }
8298                 // then make sure top/left isn't negative
8299                 if(x < vx){
8300                     x = vx;
8301                     moved = true;
8302                 }
8303                 if(y < vy){
8304                     y = vy;
8305                     moved = true;
8306                 }
8307                 return moved ? [x, y] : false;
8308             };
8309         }(),
8310
8311         // private
8312         adjustForConstraints : function(xy, parent, offsets){
8313             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8314         },
8315
8316         /**
8317          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8318          * document it aligns it to the viewport.
8319          * The position parameter is optional, and can be specified in any one of the following formats:
8320          * <ul>
8321          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8322          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8323          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8324          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8325          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8326          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8327          * </ul>
8328          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8329          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8330          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8331          * that specified in order to enforce the viewport constraints.
8332          * Following are all of the supported anchor positions:
8333     <pre>
8334     Value  Description
8335     -----  -----------------------------
8336     tl     The top left corner (default)
8337     t      The center of the top edge
8338     tr     The top right corner
8339     l      The center of the left edge
8340     c      In the center of the element
8341     r      The center of the right edge
8342     bl     The bottom left corner
8343     b      The center of the bottom edge
8344     br     The bottom right corner
8345     </pre>
8346     Example Usage:
8347     <pre><code>
8348     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8349     el.alignTo("other-el");
8350
8351     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8352     el.alignTo("other-el", "tr?");
8353
8354     // align the bottom right corner of el with the center left edge of other-el
8355     el.alignTo("other-el", "br-l?");
8356
8357     // align the center of el with the bottom left corner of other-el and
8358     // adjust the x position by -6 pixels (and the y position by 0)
8359     el.alignTo("other-el", "c-bl", [-6, 0]);
8360     </code></pre>
8361          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8362          * @param {String} position The position to align to.
8363          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8364          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8365          * @return {Roo.Element} this
8366          */
8367         alignTo : function(element, position, offsets, animate){
8368             var xy = this.getAlignToXY(element, position, offsets);
8369             this.setXY(xy, this.preanim(arguments, 3));
8370             return this;
8371         },
8372
8373         /**
8374          * Anchors an element to another element and realigns it when the window is resized.
8375          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8376          * @param {String} position The position to align to.
8377          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8378          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8379          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8380          * is a number, it is used as the buffer delay (defaults to 50ms).
8381          * @param {Function} callback The function to call after the animation finishes
8382          * @return {Roo.Element} this
8383          */
8384         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8385             var action = function(){
8386                 this.alignTo(el, alignment, offsets, animate);
8387                 Roo.callback(callback, this);
8388             };
8389             Roo.EventManager.onWindowResize(action, this);
8390             var tm = typeof monitorScroll;
8391             if(tm != 'undefined'){
8392                 Roo.EventManager.on(window, 'scroll', action, this,
8393                     {buffer: tm == 'number' ? monitorScroll : 50});
8394             }
8395             action.call(this); // align immediately
8396             return this;
8397         },
8398         /**
8399          * Clears any opacity settings from this element. Required in some cases for IE.
8400          * @return {Roo.Element} this
8401          */
8402         clearOpacity : function(){
8403             if (window.ActiveXObject) {
8404                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8405                     this.dom.style.filter = "";
8406                 }
8407             } else {
8408                 this.dom.style.opacity = "";
8409                 this.dom.style["-moz-opacity"] = "";
8410                 this.dom.style["-khtml-opacity"] = "";
8411             }
8412             return this;
8413         },
8414
8415         /**
8416          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8417          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8418          * @return {Roo.Element} this
8419          */
8420         hide : function(animate){
8421             this.setVisible(false, this.preanim(arguments, 0));
8422             return this;
8423         },
8424
8425         /**
8426         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8427         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8428          * @return {Roo.Element} this
8429          */
8430         show : function(animate){
8431             this.setVisible(true, this.preanim(arguments, 0));
8432             return this;
8433         },
8434
8435         /**
8436          * @private Test if size has a unit, otherwise appends the default
8437          */
8438         addUnits : function(size){
8439             return Roo.Element.addUnits(size, this.defaultUnit);
8440         },
8441
8442         /**
8443          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8444          * @return {Roo.Element} this
8445          */
8446         beginMeasure : function(){
8447             var el = this.dom;
8448             if(el.offsetWidth || el.offsetHeight){
8449                 return this; // offsets work already
8450             }
8451             var changed = [];
8452             var p = this.dom, b = document.body; // start with this element
8453             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8454                 var pe = Roo.get(p);
8455                 if(pe.getStyle('display') == 'none'){
8456                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8457                     p.style.visibility = "hidden";
8458                     p.style.display = "block";
8459                 }
8460                 p = p.parentNode;
8461             }
8462             this._measureChanged = changed;
8463             return this;
8464
8465         },
8466
8467         /**
8468          * Restores displays to before beginMeasure was called
8469          * @return {Roo.Element} this
8470          */
8471         endMeasure : function(){
8472             var changed = this._measureChanged;
8473             if(changed){
8474                 for(var i = 0, len = changed.length; i < len; i++) {
8475                     var r = changed[i];
8476                     r.el.style.visibility = r.visibility;
8477                     r.el.style.display = "none";
8478                 }
8479                 this._measureChanged = null;
8480             }
8481             return this;
8482         },
8483
8484         /**
8485         * Update the innerHTML of this element, optionally searching for and processing scripts
8486         * @param {String} html The new HTML
8487         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8488         * @param {Function} callback For async script loading you can be noticed when the update completes
8489         * @return {Roo.Element} this
8490          */
8491         update : function(html, loadScripts, callback){
8492             if(typeof html == "undefined"){
8493                 html = "";
8494             }
8495             if(loadScripts !== true){
8496                 this.dom.innerHTML = html;
8497                 if(typeof callback == "function"){
8498                     callback();
8499                 }
8500                 return this;
8501             }
8502             var id = Roo.id();
8503             var dom = this.dom;
8504
8505             html += '<span id="' + id + '"></span>';
8506
8507             E.onAvailable(id, function(){
8508                 var hd = document.getElementsByTagName("head")[0];
8509                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8510                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8511                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8512
8513                 var match;
8514                 while(match = re.exec(html)){
8515                     var attrs = match[1];
8516                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8517                     if(srcMatch && srcMatch[2]){
8518                        var s = document.createElement("script");
8519                        s.src = srcMatch[2];
8520                        var typeMatch = attrs.match(typeRe);
8521                        if(typeMatch && typeMatch[2]){
8522                            s.type = typeMatch[2];
8523                        }
8524                        hd.appendChild(s);
8525                     }else if(match[2] && match[2].length > 0){
8526                         if(window.execScript) {
8527                            window.execScript(match[2]);
8528                         } else {
8529                             /**
8530                              * eval:var:id
8531                              * eval:var:dom
8532                              * eval:var:html
8533                              * 
8534                              */
8535                            window.eval(match[2]);
8536                         }
8537                     }
8538                 }
8539                 var el = document.getElementById(id);
8540                 if(el){el.parentNode.removeChild(el);}
8541                 if(typeof callback == "function"){
8542                     callback();
8543                 }
8544             });
8545             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8546             return this;
8547         },
8548
8549         /**
8550          * Direct access to the UpdateManager update() method (takes the same parameters).
8551          * @param {String/Function} url The url for this request or a function to call to get the url
8552          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8553          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8554          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8555          * @return {Roo.Element} this
8556          */
8557         load : function(){
8558             var um = this.getUpdateManager();
8559             um.update.apply(um, arguments);
8560             return this;
8561         },
8562
8563         /**
8564         * Gets this element's UpdateManager
8565         * @return {Roo.UpdateManager} The UpdateManager
8566         */
8567         getUpdateManager : function(){
8568             if(!this.updateManager){
8569                 this.updateManager = new Roo.UpdateManager(this);
8570             }
8571             return this.updateManager;
8572         },
8573
8574         /**
8575          * Disables text selection for this element (normalized across browsers)
8576          * @return {Roo.Element} this
8577          */
8578         unselectable : function(){
8579             this.dom.unselectable = "on";
8580             this.swallowEvent("selectstart", true);
8581             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8582             this.addClass("x-unselectable");
8583             return this;
8584         },
8585
8586         /**
8587         * Calculates the x, y to center this element on the screen
8588         * @return {Array} The x, y values [x, y]
8589         */
8590         getCenterXY : function(){
8591             return this.getAlignToXY(document, 'c-c');
8592         },
8593
8594         /**
8595         * Centers the Element in either the viewport, or another Element.
8596         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8597         */
8598         center : function(centerIn){
8599             this.alignTo(centerIn || document, 'c-c');
8600             return this;
8601         },
8602
8603         /**
8604          * Tests various css rules/browsers to determine if this element uses a border box
8605          * @return {Boolean}
8606          */
8607         isBorderBox : function(){
8608             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8609         },
8610
8611         /**
8612          * Return a box {x, y, width, height} that can be used to set another elements
8613          * size/location to match this element.
8614          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8615          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8616          * @return {Object} box An object in the format {x, y, width, height}
8617          */
8618         getBox : function(contentBox, local){
8619             var xy;
8620             if(!local){
8621                 xy = this.getXY();
8622             }else{
8623                 var left = parseInt(this.getStyle("left"), 10) || 0;
8624                 var top = parseInt(this.getStyle("top"), 10) || 0;
8625                 xy = [left, top];
8626             }
8627             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8628             if(!contentBox){
8629                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8630             }else{
8631                 var l = this.getBorderWidth("l")+this.getPadding("l");
8632                 var r = this.getBorderWidth("r")+this.getPadding("r");
8633                 var t = this.getBorderWidth("t")+this.getPadding("t");
8634                 var b = this.getBorderWidth("b")+this.getPadding("b");
8635                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8636             }
8637             bx.right = bx.x + bx.width;
8638             bx.bottom = bx.y + bx.height;
8639             return bx;
8640         },
8641
8642         /**
8643          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8644          for more information about the sides.
8645          * @param {String} sides
8646          * @return {Number}
8647          */
8648         getFrameWidth : function(sides, onlyContentBox){
8649             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8650         },
8651
8652         /**
8653          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8654          * @param {Object} box The box to fill {x, y, width, height}
8655          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8656          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8657          * @return {Roo.Element} this
8658          */
8659         setBox : function(box, adjust, animate){
8660             var w = box.width, h = box.height;
8661             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8662                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8663                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8664             }
8665             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8666             return this;
8667         },
8668
8669         /**
8670          * Forces the browser to repaint this element
8671          * @return {Roo.Element} this
8672          */
8673          repaint : function(){
8674             var dom = this.dom;
8675             this.addClass("x-repaint");
8676             setTimeout(function(){
8677                 Roo.get(dom).removeClass("x-repaint");
8678             }, 1);
8679             return this;
8680         },
8681
8682         /**
8683          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8684          * then it returns the calculated width of the sides (see getPadding)
8685          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8686          * @return {Object/Number}
8687          */
8688         getMargins : function(side){
8689             if(!side){
8690                 return {
8691                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8692                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8693                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8694                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8695                 };
8696             }else{
8697                 return this.addStyles(side, El.margins);
8698              }
8699         },
8700
8701         // private
8702         addStyles : function(sides, styles){
8703             var val = 0, v, w;
8704             for(var i = 0, len = sides.length; i < len; i++){
8705                 v = this.getStyle(styles[sides.charAt(i)]);
8706                 if(v){
8707                      w = parseInt(v, 10);
8708                      if(w){ val += w; }
8709                 }
8710             }
8711             return val;
8712         },
8713
8714         /**
8715          * Creates a proxy element of this element
8716          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8717          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8718          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8719          * @return {Roo.Element} The new proxy element
8720          */
8721         createProxy : function(config, renderTo, matchBox){
8722             if(renderTo){
8723                 renderTo = Roo.getDom(renderTo);
8724             }else{
8725                 renderTo = document.body;
8726             }
8727             config = typeof config == "object" ?
8728                 config : {tag : "div", cls: config};
8729             var proxy = Roo.DomHelper.append(renderTo, config, true);
8730             if(matchBox){
8731                proxy.setBox(this.getBox());
8732             }
8733             return proxy;
8734         },
8735
8736         /**
8737          * Puts a mask over this element to disable user interaction. Requires core.css.
8738          * This method can only be applied to elements which accept child nodes.
8739          * @param {String} msg (optional) A message to display in the mask
8740          * @param {String} msgCls (optional) A css class to apply to the msg element
8741          * @return {Element} The mask  element
8742          */
8743         mask : function(msg, msgCls){
8744             if(this.getStyle("position") == "static"){
8745                 this.setStyle("position", "relative");
8746             }
8747             if(!this._mask){
8748                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8749             }
8750             this.addClass("x-masked");
8751             this._mask.setDisplayed(true);
8752             if(typeof msg == 'string'){
8753                 if(!this._maskMsg){
8754                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8755                 }
8756                 var mm = this._maskMsg;
8757                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8758                 mm.dom.firstChild.innerHTML = msg;
8759                 mm.setDisplayed(true);
8760                 mm.center(this);
8761             }
8762             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8763                 this._mask.setHeight(this.getHeight());
8764             }
8765             return this._mask;
8766         },
8767
8768         /**
8769          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8770          * it is cached for reuse.
8771          */
8772         unmask : function(removeEl){
8773             if(this._mask){
8774                 if(removeEl === true){
8775                     this._mask.remove();
8776                     delete this._mask;
8777                     if(this._maskMsg){
8778                         this._maskMsg.remove();
8779                         delete this._maskMsg;
8780                     }
8781                 }else{
8782                     this._mask.setDisplayed(false);
8783                     if(this._maskMsg){
8784                         this._maskMsg.setDisplayed(false);
8785                     }
8786                 }
8787             }
8788             this.removeClass("x-masked");
8789         },
8790
8791         /**
8792          * Returns true if this element is masked
8793          * @return {Boolean}
8794          */
8795         isMasked : function(){
8796             return this._mask && this._mask.isVisible();
8797         },
8798
8799         /**
8800          * Creates an iframe shim for this element to keep selects and other windowed objects from
8801          * showing through.
8802          * @return {Roo.Element} The new shim element
8803          */
8804         createShim : function(){
8805             var el = document.createElement('iframe');
8806             el.frameBorder = 'no';
8807             el.className = 'roo-shim';
8808             if(Roo.isIE && Roo.isSecure){
8809                 el.src = Roo.SSL_SECURE_URL;
8810             }
8811             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8812             shim.autoBoxAdjust = false;
8813             return shim;
8814         },
8815
8816         /**
8817          * Removes this element from the DOM and deletes it from the cache
8818          */
8819         remove : function(){
8820             if(this.dom.parentNode){
8821                 this.dom.parentNode.removeChild(this.dom);
8822             }
8823             delete El.cache[this.dom.id];
8824         },
8825
8826         /**
8827          * Sets up event handlers to add and remove a css class when the mouse is over this element
8828          * @param {String} className
8829          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8830          * mouseout events for children elements
8831          * @return {Roo.Element} this
8832          */
8833         addClassOnOver : function(className, preventFlicker){
8834             this.on("mouseover", function(){
8835                 Roo.fly(this, '_internal').addClass(className);
8836             }, this.dom);
8837             var removeFn = function(e){
8838                 if(preventFlicker !== true || !e.within(this, true)){
8839                     Roo.fly(this, '_internal').removeClass(className);
8840                 }
8841             };
8842             this.on("mouseout", removeFn, this.dom);
8843             return this;
8844         },
8845
8846         /**
8847          * Sets up event handlers to add and remove a css class when this element has the focus
8848          * @param {String} className
8849          * @return {Roo.Element} this
8850          */
8851         addClassOnFocus : function(className){
8852             this.on("focus", function(){
8853                 Roo.fly(this, '_internal').addClass(className);
8854             }, this.dom);
8855             this.on("blur", function(){
8856                 Roo.fly(this, '_internal').removeClass(className);
8857             }, this.dom);
8858             return this;
8859         },
8860         /**
8861          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
8862          * @param {String} className
8863          * @return {Roo.Element} this
8864          */
8865         addClassOnClick : function(className){
8866             var dom = this.dom;
8867             this.on("mousedown", function(){
8868                 Roo.fly(dom, '_internal').addClass(className);
8869                 var d = Roo.get(document);
8870                 var fn = function(){
8871                     Roo.fly(dom, '_internal').removeClass(className);
8872                     d.removeListener("mouseup", fn);
8873                 };
8874                 d.on("mouseup", fn);
8875             });
8876             return this;
8877         },
8878
8879         /**
8880          * Stops the specified event from bubbling and optionally prevents the default action
8881          * @param {String} eventName
8882          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8883          * @return {Roo.Element} this
8884          */
8885         swallowEvent : function(eventName, preventDefault){
8886             var fn = function(e){
8887                 e.stopPropagation();
8888                 if(preventDefault){
8889                     e.preventDefault();
8890                 }
8891             };
8892             if(eventName instanceof Array){
8893                 for(var i = 0, len = eventName.length; i < len; i++){
8894                      this.on(eventName[i], fn);
8895                 }
8896                 return this;
8897             }
8898             this.on(eventName, fn);
8899             return this;
8900         },
8901
8902         /**
8903          * @private
8904          */
8905       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8906
8907         /**
8908          * Sizes this element to its parent element's dimensions performing
8909          * neccessary box adjustments.
8910          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8911          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8912          * @return {Roo.Element} this
8913          */
8914         fitToParent : function(monitorResize, targetParent) {
8915           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8916           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8917           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8918             return;
8919           }
8920           var p = Roo.get(targetParent || this.dom.parentNode);
8921           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8922           if (monitorResize === true) {
8923             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8924             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8925           }
8926           return this;
8927         },
8928
8929         /**
8930          * Gets the next sibling, skipping text nodes
8931          * @return {HTMLElement} The next sibling or null
8932          */
8933         getNextSibling : function(){
8934             var n = this.dom.nextSibling;
8935             while(n && n.nodeType != 1){
8936                 n = n.nextSibling;
8937             }
8938             return n;
8939         },
8940
8941         /**
8942          * Gets the previous sibling, skipping text nodes
8943          * @return {HTMLElement} The previous sibling or null
8944          */
8945         getPrevSibling : function(){
8946             var n = this.dom.previousSibling;
8947             while(n && n.nodeType != 1){
8948                 n = n.previousSibling;
8949             }
8950             return n;
8951         },
8952
8953
8954         /**
8955          * Appends the passed element(s) to this element
8956          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8957          * @return {Roo.Element} this
8958          */
8959         appendChild: function(el){
8960             el = Roo.get(el);
8961             el.appendTo(this);
8962             return this;
8963         },
8964
8965         /**
8966          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8967          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8968          * automatically generated with the specified attributes.
8969          * @param {HTMLElement} insertBefore (optional) a child element of this element
8970          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8971          * @return {Roo.Element} The new child element
8972          */
8973         createChild: function(config, insertBefore, returnDom){
8974             config = config || {tag:'div'};
8975             if(insertBefore){
8976                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8977             }
8978             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8979         },
8980
8981         /**
8982          * Appends this element to the passed element
8983          * @param {String/HTMLElement/Element} el The new parent element
8984          * @return {Roo.Element} this
8985          */
8986         appendTo: function(el){
8987             el = Roo.getDom(el);
8988             el.appendChild(this.dom);
8989             return this;
8990         },
8991
8992         /**
8993          * Inserts this element before the passed element in the DOM
8994          * @param {String/HTMLElement/Element} el The element to insert before
8995          * @return {Roo.Element} this
8996          */
8997         insertBefore: function(el){
8998             el = Roo.getDom(el);
8999             el.parentNode.insertBefore(this.dom, el);
9000             return this;
9001         },
9002
9003         /**
9004          * Inserts this element after the passed element in the DOM
9005          * @param {String/HTMLElement/Element} el The element to insert after
9006          * @return {Roo.Element} this
9007          */
9008         insertAfter: function(el){
9009             el = Roo.getDom(el);
9010             el.parentNode.insertBefore(this.dom, el.nextSibling);
9011             return this;
9012         },
9013
9014         /**
9015          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9016          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9017          * @return {Roo.Element} The new child
9018          */
9019         insertFirst: function(el, returnDom){
9020             el = el || {};
9021             if(typeof el == 'object' && !el.nodeType){ // dh config
9022                 return this.createChild(el, this.dom.firstChild, returnDom);
9023             }else{
9024                 el = Roo.getDom(el);
9025                 this.dom.insertBefore(el, this.dom.firstChild);
9026                 return !returnDom ? Roo.get(el) : el;
9027             }
9028         },
9029
9030         /**
9031          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9032          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9033          * @param {String} where (optional) 'before' or 'after' defaults to before
9034          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9035          * @return {Roo.Element} the inserted Element
9036          */
9037         insertSibling: function(el, where, returnDom){
9038             where = where ? where.toLowerCase() : 'before';
9039             el = el || {};
9040             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9041
9042             if(typeof el == 'object' && !el.nodeType){ // dh config
9043                 if(where == 'after' && !this.dom.nextSibling){
9044                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9045                 }else{
9046                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9047                 }
9048
9049             }else{
9050                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9051                             where == 'before' ? this.dom : this.dom.nextSibling);
9052                 if(!returnDom){
9053                     rt = Roo.get(rt);
9054                 }
9055             }
9056             return rt;
9057         },
9058
9059         /**
9060          * Creates and wraps this element with another element
9061          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9062          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9063          * @return {HTMLElement/Element} The newly created wrapper element
9064          */
9065         wrap: function(config, returnDom){
9066             if(!config){
9067                 config = {tag: "div"};
9068             }
9069             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9070             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9071             return newEl;
9072         },
9073
9074         /**
9075          * Replaces the passed element with this element
9076          * @param {String/HTMLElement/Element} el The element to replace
9077          * @return {Roo.Element} this
9078          */
9079         replace: function(el){
9080             el = Roo.get(el);
9081             this.insertBefore(el);
9082             el.remove();
9083             return this;
9084         },
9085
9086         /**
9087          * Inserts an html fragment into this element
9088          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9089          * @param {String} html The HTML fragment
9090          * @param {Boolean} returnEl True to return an Roo.Element
9091          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9092          */
9093         insertHtml : function(where, html, returnEl){
9094             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9095             return returnEl ? Roo.get(el) : el;
9096         },
9097
9098         /**
9099          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9100          * @param {Object} o The object with the attributes
9101          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9102          * @return {Roo.Element} this
9103          */
9104         set : function(o, useSet){
9105             var el = this.dom;
9106             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9107             for(var attr in o){
9108                 if(attr == "style" || typeof o[attr] == "function") continue;
9109                 if(attr=="cls"){
9110                     el.className = o["cls"];
9111                 }else{
9112                     if(useSet) el.setAttribute(attr, o[attr]);
9113                     else el[attr] = o[attr];
9114                 }
9115             }
9116             if(o.style){
9117                 Roo.DomHelper.applyStyles(el, o.style);
9118             }
9119             return this;
9120         },
9121
9122         /**
9123          * Convenience method for constructing a KeyMap
9124          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9125          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9126          * @param {Function} fn The function to call
9127          * @param {Object} scope (optional) The scope of the function
9128          * @return {Roo.KeyMap} The KeyMap created
9129          */
9130         addKeyListener : function(key, fn, scope){
9131             var config;
9132             if(typeof key != "object" || key instanceof Array){
9133                 config = {
9134                     key: key,
9135                     fn: fn,
9136                     scope: scope
9137                 };
9138             }else{
9139                 config = {
9140                     key : key.key,
9141                     shift : key.shift,
9142                     ctrl : key.ctrl,
9143                     alt : key.alt,
9144                     fn: fn,
9145                     scope: scope
9146                 };
9147             }
9148             return new Roo.KeyMap(this, config);
9149         },
9150
9151         /**
9152          * Creates a KeyMap for this element
9153          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9154          * @return {Roo.KeyMap} The KeyMap created
9155          */
9156         addKeyMap : function(config){
9157             return new Roo.KeyMap(this, config);
9158         },
9159
9160         /**
9161          * Returns true if this element is scrollable.
9162          * @return {Boolean}
9163          */
9164          isScrollable : function(){
9165             var dom = this.dom;
9166             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9167         },
9168
9169         /**
9170          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9171          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9172          * @param {Number} value The new scroll value
9173          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9174          * @return {Element} this
9175          */
9176
9177         scrollTo : function(side, value, animate){
9178             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9179             if(!animate || !A){
9180                 this.dom[prop] = value;
9181             }else{
9182                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9183                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9184             }
9185             return this;
9186         },
9187
9188         /**
9189          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9190          * within this element's scrollable range.
9191          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9192          * @param {Number} distance How far to scroll the element in pixels
9193          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9194          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9195          * was scrolled as far as it could go.
9196          */
9197          scroll : function(direction, distance, animate){
9198              if(!this.isScrollable()){
9199                  return;
9200              }
9201              var el = this.dom;
9202              var l = el.scrollLeft, t = el.scrollTop;
9203              var w = el.scrollWidth, h = el.scrollHeight;
9204              var cw = el.clientWidth, ch = el.clientHeight;
9205              direction = direction.toLowerCase();
9206              var scrolled = false;
9207              var a = this.preanim(arguments, 2);
9208              switch(direction){
9209                  case "l":
9210                  case "left":
9211                      if(w - l > cw){
9212                          var v = Math.min(l + distance, w-cw);
9213                          this.scrollTo("left", v, a);
9214                          scrolled = true;
9215                      }
9216                      break;
9217                 case "r":
9218                 case "right":
9219                      if(l > 0){
9220                          var v = Math.max(l - distance, 0);
9221                          this.scrollTo("left", v, a);
9222                          scrolled = true;
9223                      }
9224                      break;
9225                 case "t":
9226                 case "top":
9227                 case "up":
9228                      if(t > 0){
9229                          var v = Math.max(t - distance, 0);
9230                          this.scrollTo("top", v, a);
9231                          scrolled = true;
9232                      }
9233                      break;
9234                 case "b":
9235                 case "bottom":
9236                 case "down":
9237                      if(h - t > ch){
9238                          var v = Math.min(t + distance, h-ch);
9239                          this.scrollTo("top", v, a);
9240                          scrolled = true;
9241                      }
9242                      break;
9243              }
9244              return scrolled;
9245         },
9246
9247         /**
9248          * Translates the passed page coordinates into left/top css values for this element
9249          * @param {Number/Array} x The page x or an array containing [x, y]
9250          * @param {Number} y The page y
9251          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9252          */
9253         translatePoints : function(x, y){
9254             if(typeof x == 'object' || x instanceof Array){
9255                 y = x[1]; x = x[0];
9256             }
9257             var p = this.getStyle('position');
9258             var o = this.getXY();
9259
9260             var l = parseInt(this.getStyle('left'), 10);
9261             var t = parseInt(this.getStyle('top'), 10);
9262
9263             if(isNaN(l)){
9264                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9265             }
9266             if(isNaN(t)){
9267                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9268             }
9269
9270             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9271         },
9272
9273         /**
9274          * Returns the current scroll position of the element.
9275          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9276          */
9277         getScroll : function(){
9278             var d = this.dom, doc = document;
9279             if(d == doc || d == doc.body){
9280                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9281                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9282                 return {left: l, top: t};
9283             }else{
9284                 return {left: d.scrollLeft, top: d.scrollTop};
9285             }
9286         },
9287
9288         /**
9289          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9290          * are convert to standard 6 digit hex color.
9291          * @param {String} attr The css attribute
9292          * @param {String} defaultValue The default value to use when a valid color isn't found
9293          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9294          * YUI color anims.
9295          */
9296         getColor : function(attr, defaultValue, prefix){
9297             var v = this.getStyle(attr);
9298             if(!v || v == "transparent" || v == "inherit") {
9299                 return defaultValue;
9300             }
9301             var color = typeof prefix == "undefined" ? "#" : prefix;
9302             if(v.substr(0, 4) == "rgb("){
9303                 var rvs = v.slice(4, v.length -1).split(",");
9304                 for(var i = 0; i < 3; i++){
9305                     var h = parseInt(rvs[i]).toString(16);
9306                     if(h < 16){
9307                         h = "0" + h;
9308                     }
9309                     color += h;
9310                 }
9311             } else {
9312                 if(v.substr(0, 1) == "#"){
9313                     if(v.length == 4) {
9314                         for(var i = 1; i < 4; i++){
9315                             var c = v.charAt(i);
9316                             color +=  c + c;
9317                         }
9318                     }else if(v.length == 7){
9319                         color += v.substr(1);
9320                     }
9321                 }
9322             }
9323             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9324         },
9325
9326         /**
9327          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9328          * gradient background, rounded corners and a 4-way shadow.
9329          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9330          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9331          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9332          * @return {Roo.Element} this
9333          */
9334         boxWrap : function(cls){
9335             cls = cls || 'x-box';
9336             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9337             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9338             return el;
9339         },
9340
9341         /**
9342          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9343          * @param {String} namespace The namespace in which to look for the attribute
9344          * @param {String} name The attribute name
9345          * @return {String} The attribute value
9346          */
9347         getAttributeNS : Roo.isIE ? function(ns, name){
9348             var d = this.dom;
9349             var type = typeof d[ns+":"+name];
9350             if(type != 'undefined' && type != 'unknown'){
9351                 return d[ns+":"+name];
9352             }
9353             return d[name];
9354         } : function(ns, name){
9355             var d = this.dom;
9356             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9357         }
9358     };
9359
9360     var ep = El.prototype;
9361
9362     /**
9363      * Appends an event handler (Shorthand for addListener)
9364      * @param {String}   eventName     The type of event to append
9365      * @param {Function} fn        The method the event invokes
9366      * @param {Object} scope       (optional) The scope (this object) of the fn
9367      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9368      * @method
9369      */
9370     ep.on = ep.addListener;
9371         // backwards compat
9372     ep.mon = ep.addListener;
9373
9374     /**
9375      * Removes an event handler from this element (shorthand for removeListener)
9376      * @param {String} eventName the type of event to remove
9377      * @param {Function} fn the method the event invokes
9378      * @return {Roo.Element} this
9379      * @method
9380      */
9381     ep.un = ep.removeListener;
9382
9383     /**
9384      * true to automatically adjust width and height settings for box-model issues (default to true)
9385      */
9386     ep.autoBoxAdjust = true;
9387
9388     // private
9389     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9390
9391     // private
9392     El.addUnits = function(v, defaultUnit){
9393         if(v === "" || v == "auto"){
9394             return v;
9395         }
9396         if(v === undefined){
9397             return '';
9398         }
9399         if(typeof v == "number" || !El.unitPattern.test(v)){
9400             return v + (defaultUnit || 'px');
9401         }
9402         return v;
9403     };
9404
9405     // special markup used throughout Roo when box wrapping elements
9406     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9407     /**
9408      * Visibility mode constant - Use visibility to hide element
9409      * @static
9410      * @type Number
9411      */
9412     El.VISIBILITY = 1;
9413     /**
9414      * Visibility mode constant - Use display to hide element
9415      * @static
9416      * @type Number
9417      */
9418     El.DISPLAY = 2;
9419
9420     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9421     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9422     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9423
9424
9425
9426     /**
9427      * @private
9428      */
9429     El.cache = {};
9430
9431     var docEl;
9432
9433     /**
9434      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9435      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9436      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9437      * @return {Element} The Element object
9438      * @static
9439      */
9440     El.get = function(el){
9441         var ex, elm, id;
9442         if(!el){ return null; }
9443         if(typeof el == "string"){ // element id
9444             if(!(elm = document.getElementById(el))){
9445                 return null;
9446             }
9447             if(ex = El.cache[el]){
9448                 ex.dom = elm;
9449             }else{
9450                 ex = El.cache[el] = new El(elm);
9451             }
9452             return ex;
9453         }else if(el.tagName){ // dom element
9454             if(!(id = el.id)){
9455                 id = Roo.id(el);
9456             }
9457             if(ex = El.cache[id]){
9458                 ex.dom = el;
9459             }else{
9460                 ex = El.cache[id] = new El(el);
9461             }
9462             return ex;
9463         }else if(el instanceof El){
9464             if(el != docEl){
9465                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9466                                                               // catch case where it hasn't been appended
9467                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9468             }
9469             return el;
9470         }else if(el.isComposite){
9471             return el;
9472         }else if(el instanceof Array){
9473             return El.select(el);
9474         }else if(el == document){
9475             // create a bogus element object representing the document object
9476             if(!docEl){
9477                 var f = function(){};
9478                 f.prototype = El.prototype;
9479                 docEl = new f();
9480                 docEl.dom = document;
9481             }
9482             return docEl;
9483         }
9484         return null;
9485     };
9486
9487     // private
9488     El.uncache = function(el){
9489         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9490             if(a[i]){
9491                 delete El.cache[a[i].id || a[i]];
9492             }
9493         }
9494     };
9495
9496     // private
9497     // Garbage collection - uncache elements/purge listeners on orphaned elements
9498     // so we don't hold a reference and cause the browser to retain them
9499     El.garbageCollect = function(){
9500         if(!Roo.enableGarbageCollector){
9501             clearInterval(El.collectorThread);
9502             return;
9503         }
9504         for(var eid in El.cache){
9505             var el = El.cache[eid], d = el.dom;
9506             // -------------------------------------------------------
9507             // Determining what is garbage:
9508             // -------------------------------------------------------
9509             // !d
9510             // dom node is null, definitely garbage
9511             // -------------------------------------------------------
9512             // !d.parentNode
9513             // no parentNode == direct orphan, definitely garbage
9514             // -------------------------------------------------------
9515             // !d.offsetParent && !document.getElementById(eid)
9516             // display none elements have no offsetParent so we will
9517             // also try to look it up by it's id. However, check
9518             // offsetParent first so we don't do unneeded lookups.
9519             // This enables collection of elements that are not orphans
9520             // directly, but somewhere up the line they have an orphan
9521             // parent.
9522             // -------------------------------------------------------
9523             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9524                 delete El.cache[eid];
9525                 if(d && Roo.enableListenerCollection){
9526                     E.purgeElement(d);
9527                 }
9528             }
9529         }
9530     }
9531     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9532
9533
9534     // dom is optional
9535     El.Flyweight = function(dom){
9536         this.dom = dom;
9537     };
9538     El.Flyweight.prototype = El.prototype;
9539
9540     El._flyweights = {};
9541     /**
9542      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9543      * the dom node can be overwritten by other code.
9544      * @param {String/HTMLElement} el The dom node or id
9545      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9546      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9547      * @static
9548      * @return {Element} The shared Element object
9549      */
9550     El.fly = function(el, named){
9551         named = named || '_global';
9552         el = Roo.getDom(el);
9553         if(!el){
9554             return null;
9555         }
9556         if(!El._flyweights[named]){
9557             El._flyweights[named] = new El.Flyweight();
9558         }
9559         El._flyweights[named].dom = el;
9560         return El._flyweights[named];
9561     };
9562
9563     /**
9564      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9565      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9566      * Shorthand of {@link Roo.Element#get}
9567      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9568      * @return {Element} The Element object
9569      * @member Roo
9570      * @method get
9571      */
9572     Roo.get = El.get;
9573     /**
9574      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9575      * the dom node can be overwritten by other code.
9576      * Shorthand of {@link Roo.Element#fly}
9577      * @param {String/HTMLElement} el The dom node or id
9578      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9579      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9580      * @static
9581      * @return {Element} The shared Element object
9582      * @member Roo
9583      * @method fly
9584      */
9585     Roo.fly = El.fly;
9586
9587     // speedy lookup for elements never to box adjust
9588     var noBoxAdjust = Roo.isStrict ? {
9589         select:1
9590     } : {
9591         input:1, select:1, textarea:1
9592     };
9593     if(Roo.isIE || Roo.isGecko){
9594         noBoxAdjust['button'] = 1;
9595     }
9596
9597
9598     Roo.EventManager.on(window, 'unload', function(){
9599         delete El.cache;
9600         delete El._flyweights;
9601     });
9602 })();
9603
9604
9605
9606
9607 if(Roo.DomQuery){
9608     Roo.Element.selectorFunction = Roo.DomQuery.select;
9609 }
9610
9611 Roo.Element.select = function(selector, unique, root){
9612     var els;
9613     if(typeof selector == "string"){
9614         els = Roo.Element.selectorFunction(selector, root);
9615     }else if(selector.length !== undefined){
9616         els = selector;
9617     }else{
9618         throw "Invalid selector";
9619     }
9620     if(unique === true){
9621         return new Roo.CompositeElement(els);
9622     }else{
9623         return new Roo.CompositeElementLite(els);
9624     }
9625 };
9626 /**
9627  * Selects elements based on the passed CSS selector to enable working on them as 1.
9628  * @param {String/Array} selector The CSS selector or an array of elements
9629  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9630  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9631  * @return {CompositeElementLite/CompositeElement}
9632  * @member Roo
9633  * @method select
9634  */
9635 Roo.select = Roo.Element.select;
9636
9637
9638
9639
9640
9641
9642
9643
9644
9645
9646
9647
9648
9649
9650 /*
9651  * Based on:
9652  * Ext JS Library 1.1.1
9653  * Copyright(c) 2006-2007, Ext JS, LLC.
9654  *
9655  * Originally Released Under LGPL - original licence link has changed is not relivant.
9656  *
9657  * Fork - LGPL
9658  * <script type="text/javascript">
9659  */
9660
9661
9662
9663 //Notifies Element that fx methods are available
9664 Roo.enableFx = true;
9665
9666 /**
9667  * @class Roo.Fx
9668  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9669  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9670  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9671  * Element effects to work.</p><br/>
9672  *
9673  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9674  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9675  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9676  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9677  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9678  * expected results and should be done with care.</p><br/>
9679  *
9680  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9681  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9682 <pre>
9683 Value  Description
9684 -----  -----------------------------
9685 tl     The top left corner
9686 t      The center of the top edge
9687 tr     The top right corner
9688 l      The center of the left edge
9689 r      The center of the right edge
9690 bl     The bottom left corner
9691 b      The center of the bottom edge
9692 br     The bottom right corner
9693 </pre>
9694  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9695  * below are common options that can be passed to any Fx method.</b>
9696  * @cfg {Function} callback A function called when the effect is finished
9697  * @cfg {Object} scope The scope of the effect function
9698  * @cfg {String} easing A valid Easing value for the effect
9699  * @cfg {String} afterCls A css class to apply after the effect
9700  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9701  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9702  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9703  * effects that end with the element being visually hidden, ignored otherwise)
9704  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9705  * a function which returns such a specification that will be applied to the Element after the effect finishes
9706  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9707  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9708  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9709  */
9710 Roo.Fx = {
9711         /**
9712          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9713          * origin for the slide effect.  This function automatically handles wrapping the element with
9714          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9715          * Usage:
9716          *<pre><code>
9717 // default: slide the element in from the top
9718 el.slideIn();
9719
9720 // custom: slide the element in from the right with a 2-second duration
9721 el.slideIn('r', { duration: 2 });
9722
9723 // common config options shown with default values
9724 el.slideIn('t', {
9725     easing: 'easeOut',
9726     duration: .5
9727 });
9728 </code></pre>
9729          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9730          * @param {Object} options (optional) Object literal with any of the Fx config options
9731          * @return {Roo.Element} The Element
9732          */
9733     slideIn : function(anchor, o){
9734         var el = this.getFxEl();
9735         o = o || {};
9736
9737         el.queueFx(o, function(){
9738
9739             anchor = anchor || "t";
9740
9741             // fix display to visibility
9742             this.fixDisplay();
9743
9744             // restore values after effect
9745             var r = this.getFxRestore();
9746             var b = this.getBox();
9747             // fixed size for slide
9748             this.setSize(b);
9749
9750             // wrap if needed
9751             var wrap = this.fxWrap(r.pos, o, "hidden");
9752
9753             var st = this.dom.style;
9754             st.visibility = "visible";
9755             st.position = "absolute";
9756
9757             // clear out temp styles after slide and unwrap
9758             var after = function(){
9759                 el.fxUnwrap(wrap, r.pos, o);
9760                 st.width = r.width;
9761                 st.height = r.height;
9762                 el.afterFx(o);
9763             };
9764             // time to calc the positions
9765             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9766
9767             switch(anchor.toLowerCase()){
9768                 case "t":
9769                     wrap.setSize(b.width, 0);
9770                     st.left = st.bottom = "0";
9771                     a = {height: bh};
9772                 break;
9773                 case "l":
9774                     wrap.setSize(0, b.height);
9775                     st.right = st.top = "0";
9776                     a = {width: bw};
9777                 break;
9778                 case "r":
9779                     wrap.setSize(0, b.height);
9780                     wrap.setX(b.right);
9781                     st.left = st.top = "0";
9782                     a = {width: bw, points: pt};
9783                 break;
9784                 case "b":
9785                     wrap.setSize(b.width, 0);
9786                     wrap.setY(b.bottom);
9787                     st.left = st.top = "0";
9788                     a = {height: bh, points: pt};
9789                 break;
9790                 case "tl":
9791                     wrap.setSize(0, 0);
9792                     st.right = st.bottom = "0";
9793                     a = {width: bw, height: bh};
9794                 break;
9795                 case "bl":
9796                     wrap.setSize(0, 0);
9797                     wrap.setY(b.y+b.height);
9798                     st.right = st.top = "0";
9799                     a = {width: bw, height: bh, points: pt};
9800                 break;
9801                 case "br":
9802                     wrap.setSize(0, 0);
9803                     wrap.setXY([b.right, b.bottom]);
9804                     st.left = st.top = "0";
9805                     a = {width: bw, height: bh, points: pt};
9806                 break;
9807                 case "tr":
9808                     wrap.setSize(0, 0);
9809                     wrap.setX(b.x+b.width);
9810                     st.left = st.bottom = "0";
9811                     a = {width: bw, height: bh, points: pt};
9812                 break;
9813             }
9814             this.dom.style.visibility = "visible";
9815             wrap.show();
9816
9817             arguments.callee.anim = wrap.fxanim(a,
9818                 o,
9819                 'motion',
9820                 .5,
9821                 'easeOut', after);
9822         });
9823         return this;
9824     },
9825     
9826         /**
9827          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9828          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9829          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9830          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9831          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9832          * Usage:
9833          *<pre><code>
9834 // default: slide the element out to the top
9835 el.slideOut();
9836
9837 // custom: slide the element out to the right with a 2-second duration
9838 el.slideOut('r', { duration: 2 });
9839
9840 // common config options shown with default values
9841 el.slideOut('t', {
9842     easing: 'easeOut',
9843     duration: .5,
9844     remove: false,
9845     useDisplay: false
9846 });
9847 </code></pre>
9848          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9849          * @param {Object} options (optional) Object literal with any of the Fx config options
9850          * @return {Roo.Element} The Element
9851          */
9852     slideOut : function(anchor, o){
9853         var el = this.getFxEl();
9854         o = o || {};
9855
9856         el.queueFx(o, function(){
9857
9858             anchor = anchor || "t";
9859
9860             // restore values after effect
9861             var r = this.getFxRestore();
9862             
9863             var b = this.getBox();
9864             // fixed size for slide
9865             this.setSize(b);
9866
9867             // wrap if needed
9868             var wrap = this.fxWrap(r.pos, o, "visible");
9869
9870             var st = this.dom.style;
9871             st.visibility = "visible";
9872             st.position = "absolute";
9873
9874             wrap.setSize(b);
9875
9876             var after = function(){
9877                 if(o.useDisplay){
9878                     el.setDisplayed(false);
9879                 }else{
9880                     el.hide();
9881                 }
9882
9883                 el.fxUnwrap(wrap, r.pos, o);
9884
9885                 st.width = r.width;
9886                 st.height = r.height;
9887
9888                 el.afterFx(o);
9889             };
9890
9891             var a, zero = {to: 0};
9892             switch(anchor.toLowerCase()){
9893                 case "t":
9894                     st.left = st.bottom = "0";
9895                     a = {height: zero};
9896                 break;
9897                 case "l":
9898                     st.right = st.top = "0";
9899                     a = {width: zero};
9900                 break;
9901                 case "r":
9902                     st.left = st.top = "0";
9903                     a = {width: zero, points: {to:[b.right, b.y]}};
9904                 break;
9905                 case "b":
9906                     st.left = st.top = "0";
9907                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9908                 break;
9909                 case "tl":
9910                     st.right = st.bottom = "0";
9911                     a = {width: zero, height: zero};
9912                 break;
9913                 case "bl":
9914                     st.right = st.top = "0";
9915                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9916                 break;
9917                 case "br":
9918                     st.left = st.top = "0";
9919                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9920                 break;
9921                 case "tr":
9922                     st.left = st.bottom = "0";
9923                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9924                 break;
9925             }
9926
9927             arguments.callee.anim = wrap.fxanim(a,
9928                 o,
9929                 'motion',
9930                 .5,
9931                 "easeOut", after);
9932         });
9933         return this;
9934     },
9935
9936         /**
9937          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9938          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9939          * The element must be removed from the DOM using the 'remove' config option if desired.
9940          * Usage:
9941          *<pre><code>
9942 // default
9943 el.puff();
9944
9945 // common config options shown with default values
9946 el.puff({
9947     easing: 'easeOut',
9948     duration: .5,
9949     remove: false,
9950     useDisplay: false
9951 });
9952 </code></pre>
9953          * @param {Object} options (optional) Object literal with any of the Fx config options
9954          * @return {Roo.Element} The Element
9955          */
9956     puff : function(o){
9957         var el = this.getFxEl();
9958         o = o || {};
9959
9960         el.queueFx(o, function(){
9961             this.clearOpacity();
9962             this.show();
9963
9964             // restore values after effect
9965             var r = this.getFxRestore();
9966             var st = this.dom.style;
9967
9968             var after = function(){
9969                 if(o.useDisplay){
9970                     el.setDisplayed(false);
9971                 }else{
9972                     el.hide();
9973                 }
9974
9975                 el.clearOpacity();
9976
9977                 el.setPositioning(r.pos);
9978                 st.width = r.width;
9979                 st.height = r.height;
9980                 st.fontSize = '';
9981                 el.afterFx(o);
9982             };
9983
9984             var width = this.getWidth();
9985             var height = this.getHeight();
9986
9987             arguments.callee.anim = this.fxanim({
9988                     width : {to: this.adjustWidth(width * 2)},
9989                     height : {to: this.adjustHeight(height * 2)},
9990                     points : {by: [-(width * .5), -(height * .5)]},
9991                     opacity : {to: 0},
9992                     fontSize: {to:200, unit: "%"}
9993                 },
9994                 o,
9995                 'motion',
9996                 .5,
9997                 "easeOut", after);
9998         });
9999         return this;
10000     },
10001
10002         /**
10003          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10004          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10005          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10006          * Usage:
10007          *<pre><code>
10008 // default
10009 el.switchOff();
10010
10011 // all config options shown with default values
10012 el.switchOff({
10013     easing: 'easeIn',
10014     duration: .3,
10015     remove: false,
10016     useDisplay: false
10017 });
10018 </code></pre>
10019          * @param {Object} options (optional) Object literal with any of the Fx config options
10020          * @return {Roo.Element} The Element
10021          */
10022     switchOff : function(o){
10023         var el = this.getFxEl();
10024         o = o || {};
10025
10026         el.queueFx(o, function(){
10027             this.clearOpacity();
10028             this.clip();
10029
10030             // restore values after effect
10031             var r = this.getFxRestore();
10032             var st = this.dom.style;
10033
10034             var after = function(){
10035                 if(o.useDisplay){
10036                     el.setDisplayed(false);
10037                 }else{
10038                     el.hide();
10039                 }
10040
10041                 el.clearOpacity();
10042                 el.setPositioning(r.pos);
10043                 st.width = r.width;
10044                 st.height = r.height;
10045
10046                 el.afterFx(o);
10047             };
10048
10049             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10050                 this.clearOpacity();
10051                 (function(){
10052                     this.fxanim({
10053                         height:{to:1},
10054                         points:{by:[0, this.getHeight() * .5]}
10055                     }, o, 'motion', 0.3, 'easeIn', after);
10056                 }).defer(100, this);
10057             });
10058         });
10059         return this;
10060     },
10061
10062     /**
10063      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10064      * changed using the "attr" config option) and then fading back to the original color. If no original
10065      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10066      * Usage:
10067 <pre><code>
10068 // default: highlight background to yellow
10069 el.highlight();
10070
10071 // custom: highlight foreground text to blue for 2 seconds
10072 el.highlight("0000ff", { attr: 'color', duration: 2 });
10073
10074 // common config options shown with default values
10075 el.highlight("ffff9c", {
10076     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10077     endColor: (current color) or "ffffff",
10078     easing: 'easeIn',
10079     duration: 1
10080 });
10081 </code></pre>
10082      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10083      * @param {Object} options (optional) Object literal with any of the Fx config options
10084      * @return {Roo.Element} The Element
10085      */ 
10086     highlight : function(color, o){
10087         var el = this.getFxEl();
10088         o = o || {};
10089
10090         el.queueFx(o, function(){
10091             color = color || "ffff9c";
10092             attr = o.attr || "backgroundColor";
10093
10094             this.clearOpacity();
10095             this.show();
10096
10097             var origColor = this.getColor(attr);
10098             var restoreColor = this.dom.style[attr];
10099             endColor = (o.endColor || origColor) || "ffffff";
10100
10101             var after = function(){
10102                 el.dom.style[attr] = restoreColor;
10103                 el.afterFx(o);
10104             };
10105
10106             var a = {};
10107             a[attr] = {from: color, to: endColor};
10108             arguments.callee.anim = this.fxanim(a,
10109                 o,
10110                 'color',
10111                 1,
10112                 'easeIn', after);
10113         });
10114         return this;
10115     },
10116
10117    /**
10118     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10119     * Usage:
10120 <pre><code>
10121 // default: a single light blue ripple
10122 el.frame();
10123
10124 // custom: 3 red ripples lasting 3 seconds total
10125 el.frame("ff0000", 3, { duration: 3 });
10126
10127 // common config options shown with default values
10128 el.frame("C3DAF9", 1, {
10129     duration: 1 //duration of entire animation (not each individual ripple)
10130     // Note: Easing is not configurable and will be ignored if included
10131 });
10132 </code></pre>
10133     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10134     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10135     * @param {Object} options (optional) Object literal with any of the Fx config options
10136     * @return {Roo.Element} The Element
10137     */
10138     frame : function(color, count, o){
10139         var el = this.getFxEl();
10140         o = o || {};
10141
10142         el.queueFx(o, function(){
10143             color = color || "#C3DAF9";
10144             if(color.length == 6){
10145                 color = "#" + color;
10146             }
10147             count = count || 1;
10148             duration = o.duration || 1;
10149             this.show();
10150
10151             var b = this.getBox();
10152             var animFn = function(){
10153                 var proxy = this.createProxy({
10154
10155                      style:{
10156                         visbility:"hidden",
10157                         position:"absolute",
10158                         "z-index":"35000", // yee haw
10159                         border:"0px solid " + color
10160                      }
10161                   });
10162                 var scale = Roo.isBorderBox ? 2 : 1;
10163                 proxy.animate({
10164                     top:{from:b.y, to:b.y - 20},
10165                     left:{from:b.x, to:b.x - 20},
10166                     borderWidth:{from:0, to:10},
10167                     opacity:{from:1, to:0},
10168                     height:{from:b.height, to:(b.height + (20*scale))},
10169                     width:{from:b.width, to:(b.width + (20*scale))}
10170                 }, duration, function(){
10171                     proxy.remove();
10172                 });
10173                 if(--count > 0){
10174                      animFn.defer((duration/2)*1000, this);
10175                 }else{
10176                     el.afterFx(o);
10177                 }
10178             };
10179             animFn.call(this);
10180         });
10181         return this;
10182     },
10183
10184    /**
10185     * Creates a pause before any subsequent queued effects begin.  If there are
10186     * no effects queued after the pause it will have no effect.
10187     * Usage:
10188 <pre><code>
10189 el.pause(1);
10190 </code></pre>
10191     * @param {Number} seconds The length of time to pause (in seconds)
10192     * @return {Roo.Element} The Element
10193     */
10194     pause : function(seconds){
10195         var el = this.getFxEl();
10196         var o = {};
10197
10198         el.queueFx(o, function(){
10199             setTimeout(function(){
10200                 el.afterFx(o);
10201             }, seconds * 1000);
10202         });
10203         return this;
10204     },
10205
10206    /**
10207     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10208     * using the "endOpacity" config option.
10209     * Usage:
10210 <pre><code>
10211 // default: fade in from opacity 0 to 100%
10212 el.fadeIn();
10213
10214 // custom: fade in from opacity 0 to 75% over 2 seconds
10215 el.fadeIn({ endOpacity: .75, duration: 2});
10216
10217 // common config options shown with default values
10218 el.fadeIn({
10219     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10220     easing: 'easeOut',
10221     duration: .5
10222 });
10223 </code></pre>
10224     * @param {Object} options (optional) Object literal with any of the Fx config options
10225     * @return {Roo.Element} The Element
10226     */
10227     fadeIn : function(o){
10228         var el = this.getFxEl();
10229         o = o || {};
10230         el.queueFx(o, function(){
10231             this.setOpacity(0);
10232             this.fixDisplay();
10233             this.dom.style.visibility = 'visible';
10234             var to = o.endOpacity || 1;
10235             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10236                 o, null, .5, "easeOut", function(){
10237                 if(to == 1){
10238                     this.clearOpacity();
10239                 }
10240                 el.afterFx(o);
10241             });
10242         });
10243         return this;
10244     },
10245
10246    /**
10247     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10248     * using the "endOpacity" config option.
10249     * Usage:
10250 <pre><code>
10251 // default: fade out from the element's current opacity to 0
10252 el.fadeOut();
10253
10254 // custom: fade out from the element's current opacity to 25% over 2 seconds
10255 el.fadeOut({ endOpacity: .25, duration: 2});
10256
10257 // common config options shown with default values
10258 el.fadeOut({
10259     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10260     easing: 'easeOut',
10261     duration: .5
10262     remove: false,
10263     useDisplay: false
10264 });
10265 </code></pre>
10266     * @param {Object} options (optional) Object literal with any of the Fx config options
10267     * @return {Roo.Element} The Element
10268     */
10269     fadeOut : function(o){
10270         var el = this.getFxEl();
10271         o = o || {};
10272         el.queueFx(o, function(){
10273             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10274                 o, null, .5, "easeOut", function(){
10275                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10276                      this.dom.style.display = "none";
10277                 }else{
10278                      this.dom.style.visibility = "hidden";
10279                 }
10280                 this.clearOpacity();
10281                 el.afterFx(o);
10282             });
10283         });
10284         return this;
10285     },
10286
10287    /**
10288     * Animates the transition of an element's dimensions from a starting height/width
10289     * to an ending height/width.
10290     * Usage:
10291 <pre><code>
10292 // change height and width to 100x100 pixels
10293 el.scale(100, 100);
10294
10295 // common config options shown with default values.  The height and width will default to
10296 // the element's existing values if passed as null.
10297 el.scale(
10298     [element's width],
10299     [element's height], {
10300     easing: 'easeOut',
10301     duration: .35
10302 });
10303 </code></pre>
10304     * @param {Number} width  The new width (pass undefined to keep the original width)
10305     * @param {Number} height  The new height (pass undefined to keep the original height)
10306     * @param {Object} options (optional) Object literal with any of the Fx config options
10307     * @return {Roo.Element} The Element
10308     */
10309     scale : function(w, h, o){
10310         this.shift(Roo.apply({}, o, {
10311             width: w,
10312             height: h
10313         }));
10314         return this;
10315     },
10316
10317    /**
10318     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10319     * Any of these properties not specified in the config object will not be changed.  This effect 
10320     * requires that at least one new dimension, position or opacity setting must be passed in on
10321     * the config object in order for the function to have any effect.
10322     * Usage:
10323 <pre><code>
10324 // slide the element horizontally to x position 200 while changing the height and opacity
10325 el.shift({ x: 200, height: 50, opacity: .8 });
10326
10327 // common config options shown with default values.
10328 el.shift({
10329     width: [element's width],
10330     height: [element's height],
10331     x: [element's x position],
10332     y: [element's y position],
10333     opacity: [element's opacity],
10334     easing: 'easeOut',
10335     duration: .35
10336 });
10337 </code></pre>
10338     * @param {Object} options  Object literal with any of the Fx config options
10339     * @return {Roo.Element} The Element
10340     */
10341     shift : function(o){
10342         var el = this.getFxEl();
10343         o = o || {};
10344         el.queueFx(o, function(){
10345             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10346             if(w !== undefined){
10347                 a.width = {to: this.adjustWidth(w)};
10348             }
10349             if(h !== undefined){
10350                 a.height = {to: this.adjustHeight(h)};
10351             }
10352             if(x !== undefined || y !== undefined){
10353                 a.points = {to: [
10354                     x !== undefined ? x : this.getX(),
10355                     y !== undefined ? y : this.getY()
10356                 ]};
10357             }
10358             if(op !== undefined){
10359                 a.opacity = {to: op};
10360             }
10361             if(o.xy !== undefined){
10362                 a.points = {to: o.xy};
10363             }
10364             arguments.callee.anim = this.fxanim(a,
10365                 o, 'motion', .35, "easeOut", function(){
10366                 el.afterFx(o);
10367             });
10368         });
10369         return this;
10370     },
10371
10372         /**
10373          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10374          * ending point of the effect.
10375          * Usage:
10376          *<pre><code>
10377 // default: slide the element downward while fading out
10378 el.ghost();
10379
10380 // custom: slide the element out to the right with a 2-second duration
10381 el.ghost('r', { duration: 2 });
10382
10383 // common config options shown with default values
10384 el.ghost('b', {
10385     easing: 'easeOut',
10386     duration: .5
10387     remove: false,
10388     useDisplay: false
10389 });
10390 </code></pre>
10391          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10392          * @param {Object} options (optional) Object literal with any of the Fx config options
10393          * @return {Roo.Element} The Element
10394          */
10395     ghost : function(anchor, o){
10396         var el = this.getFxEl();
10397         o = o || {};
10398
10399         el.queueFx(o, function(){
10400             anchor = anchor || "b";
10401
10402             // restore values after effect
10403             var r = this.getFxRestore();
10404             var w = this.getWidth(),
10405                 h = this.getHeight();
10406
10407             var st = this.dom.style;
10408
10409             var after = function(){
10410                 if(o.useDisplay){
10411                     el.setDisplayed(false);
10412                 }else{
10413                     el.hide();
10414                 }
10415
10416                 el.clearOpacity();
10417                 el.setPositioning(r.pos);
10418                 st.width = r.width;
10419                 st.height = r.height;
10420
10421                 el.afterFx(o);
10422             };
10423
10424             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10425             switch(anchor.toLowerCase()){
10426                 case "t":
10427                     pt.by = [0, -h];
10428                 break;
10429                 case "l":
10430                     pt.by = [-w, 0];
10431                 break;
10432                 case "r":
10433                     pt.by = [w, 0];
10434                 break;
10435                 case "b":
10436                     pt.by = [0, h];
10437                 break;
10438                 case "tl":
10439                     pt.by = [-w, -h];
10440                 break;
10441                 case "bl":
10442                     pt.by = [-w, h];
10443                 break;
10444                 case "br":
10445                     pt.by = [w, h];
10446                 break;
10447                 case "tr":
10448                     pt.by = [w, -h];
10449                 break;
10450             }
10451
10452             arguments.callee.anim = this.fxanim(a,
10453                 o,
10454                 'motion',
10455                 .5,
10456                 "easeOut", after);
10457         });
10458         return this;
10459     },
10460
10461         /**
10462          * Ensures that all effects queued after syncFx is called on the element are
10463          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10464          * @return {Roo.Element} The Element
10465          */
10466     syncFx : function(){
10467         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10468             block : false,
10469             concurrent : true,
10470             stopFx : false
10471         });
10472         return this;
10473     },
10474
10475         /**
10476          * Ensures that all effects queued after sequenceFx is called on the element are
10477          * run in sequence.  This is the opposite of {@link #syncFx}.
10478          * @return {Roo.Element} The Element
10479          */
10480     sequenceFx : function(){
10481         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10482             block : false,
10483             concurrent : false,
10484             stopFx : false
10485         });
10486         return this;
10487     },
10488
10489         /* @private */
10490     nextFx : function(){
10491         var ef = this.fxQueue[0];
10492         if(ef){
10493             ef.call(this);
10494         }
10495     },
10496
10497         /**
10498          * Returns true if the element has any effects actively running or queued, else returns false.
10499          * @return {Boolean} True if element has active effects, else false
10500          */
10501     hasActiveFx : function(){
10502         return this.fxQueue && this.fxQueue[0];
10503     },
10504
10505         /**
10506          * Stops any running effects and clears the element's internal effects queue if it contains
10507          * any additional effects that haven't started yet.
10508          * @return {Roo.Element} The Element
10509          */
10510     stopFx : function(){
10511         if(this.hasActiveFx()){
10512             var cur = this.fxQueue[0];
10513             if(cur && cur.anim && cur.anim.isAnimated()){
10514                 this.fxQueue = [cur]; // clear out others
10515                 cur.anim.stop(true);
10516             }
10517         }
10518         return this;
10519     },
10520
10521         /* @private */
10522     beforeFx : function(o){
10523         if(this.hasActiveFx() && !o.concurrent){
10524            if(o.stopFx){
10525                this.stopFx();
10526                return true;
10527            }
10528            return false;
10529         }
10530         return true;
10531     },
10532
10533         /**
10534          * Returns true if the element is currently blocking so that no other effect can be queued
10535          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10536          * used to ensure that an effect initiated by a user action runs to completion prior to the
10537          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10538          * @return {Boolean} True if blocking, else false
10539          */
10540     hasFxBlock : function(){
10541         var q = this.fxQueue;
10542         return q && q[0] && q[0].block;
10543     },
10544
10545         /* @private */
10546     queueFx : function(o, fn){
10547         if(!this.fxQueue){
10548             this.fxQueue = [];
10549         }
10550         if(!this.hasFxBlock()){
10551             Roo.applyIf(o, this.fxDefaults);
10552             if(!o.concurrent){
10553                 var run = this.beforeFx(o);
10554                 fn.block = o.block;
10555                 this.fxQueue.push(fn);
10556                 if(run){
10557                     this.nextFx();
10558                 }
10559             }else{
10560                 fn.call(this);
10561             }
10562         }
10563         return this;
10564     },
10565
10566         /* @private */
10567     fxWrap : function(pos, o, vis){
10568         var wrap;
10569         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10570             var wrapXY;
10571             if(o.fixPosition){
10572                 wrapXY = this.getXY();
10573             }
10574             var div = document.createElement("div");
10575             div.style.visibility = vis;
10576             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10577             wrap.setPositioning(pos);
10578             if(wrap.getStyle("position") == "static"){
10579                 wrap.position("relative");
10580             }
10581             this.clearPositioning('auto');
10582             wrap.clip();
10583             wrap.dom.appendChild(this.dom);
10584             if(wrapXY){
10585                 wrap.setXY(wrapXY);
10586             }
10587         }
10588         return wrap;
10589     },
10590
10591         /* @private */
10592     fxUnwrap : function(wrap, pos, o){
10593         this.clearPositioning();
10594         this.setPositioning(pos);
10595         if(!o.wrap){
10596             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10597             wrap.remove();
10598         }
10599     },
10600
10601         /* @private */
10602     getFxRestore : function(){
10603         var st = this.dom.style;
10604         return {pos: this.getPositioning(), width: st.width, height : st.height};
10605     },
10606
10607         /* @private */
10608     afterFx : function(o){
10609         if(o.afterStyle){
10610             this.applyStyles(o.afterStyle);
10611         }
10612         if(o.afterCls){
10613             this.addClass(o.afterCls);
10614         }
10615         if(o.remove === true){
10616             this.remove();
10617         }
10618         Roo.callback(o.callback, o.scope, [this]);
10619         if(!o.concurrent){
10620             this.fxQueue.shift();
10621             this.nextFx();
10622         }
10623     },
10624
10625         /* @private */
10626     getFxEl : function(){ // support for composite element fx
10627         return Roo.get(this.dom);
10628     },
10629
10630         /* @private */
10631     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10632         animType = animType || 'run';
10633         opt = opt || {};
10634         var anim = Roo.lib.Anim[animType](
10635             this.dom, args,
10636             (opt.duration || defaultDur) || .35,
10637             (opt.easing || defaultEase) || 'easeOut',
10638             function(){
10639                 Roo.callback(cb, this);
10640             },
10641             this
10642         );
10643         opt.anim = anim;
10644         return anim;
10645     }
10646 };
10647
10648 // backwords compat
10649 Roo.Fx.resize = Roo.Fx.scale;
10650
10651 //When included, Roo.Fx is automatically applied to Element so that all basic
10652 //effects are available directly via the Element API
10653 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10654  * Based on:
10655  * Ext JS Library 1.1.1
10656  * Copyright(c) 2006-2007, Ext JS, LLC.
10657  *
10658  * Originally Released Under LGPL - original licence link has changed is not relivant.
10659  *
10660  * Fork - LGPL
10661  * <script type="text/javascript">
10662  */
10663
10664
10665 /**
10666  * @class Roo.CompositeElement
10667  * Standard composite class. Creates a Roo.Element for every element in the collection.
10668  * <br><br>
10669  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10670  * actions will be performed on all the elements in this collection.</b>
10671  * <br><br>
10672  * All methods return <i>this</i> and can be chained.
10673  <pre><code>
10674  var els = Roo.select("#some-el div.some-class", true);
10675  // or select directly from an existing element
10676  var el = Roo.get('some-el');
10677  el.select('div.some-class', true);
10678
10679  els.setWidth(100); // all elements become 100 width
10680  els.hide(true); // all elements fade out and hide
10681  // or
10682  els.setWidth(100).hide(true);
10683  </code></pre>
10684  */
10685 Roo.CompositeElement = function(els){
10686     this.elements = [];
10687     this.addElements(els);
10688 };
10689 Roo.CompositeElement.prototype = {
10690     isComposite: true,
10691     addElements : function(els){
10692         if(!els) return this;
10693         if(typeof els == "string"){
10694             els = Roo.Element.selectorFunction(els);
10695         }
10696         var yels = this.elements;
10697         var index = yels.length-1;
10698         for(var i = 0, len = els.length; i < len; i++) {
10699                 yels[++index] = Roo.get(els[i]);
10700         }
10701         return this;
10702     },
10703
10704     /**
10705     * Clears this composite and adds the elements returned by the passed selector.
10706     * @param {String/Array} els A string CSS selector, an array of elements or an element
10707     * @return {CompositeElement} this
10708     */
10709     fill : function(els){
10710         this.elements = [];
10711         this.add(els);
10712         return this;
10713     },
10714
10715     /**
10716     * Filters this composite to only elements that match the passed selector.
10717     * @param {String} selector A string CSS selector
10718     * @return {CompositeElement} this
10719     */
10720     filter : function(selector){
10721         var els = [];
10722         this.each(function(el){
10723             if(el.is(selector)){
10724                 els[els.length] = el.dom;
10725             }
10726         });
10727         this.fill(els);
10728         return this;
10729     },
10730
10731     invoke : function(fn, args){
10732         var els = this.elements;
10733         for(var i = 0, len = els.length; i < len; i++) {
10734                 Roo.Element.prototype[fn].apply(els[i], args);
10735         }
10736         return this;
10737     },
10738     /**
10739     * Adds elements to this composite.
10740     * @param {String/Array} els A string CSS selector, an array of elements or an element
10741     * @return {CompositeElement} this
10742     */
10743     add : function(els){
10744         if(typeof els == "string"){
10745             this.addElements(Roo.Element.selectorFunction(els));
10746         }else if(els.length !== undefined){
10747             this.addElements(els);
10748         }else{
10749             this.addElements([els]);
10750         }
10751         return this;
10752     },
10753     /**
10754     * Calls the passed function passing (el, this, index) for each element in this composite.
10755     * @param {Function} fn The function to call
10756     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10757     * @return {CompositeElement} this
10758     */
10759     each : function(fn, scope){
10760         var els = this.elements;
10761         for(var i = 0, len = els.length; i < len; i++){
10762             if(fn.call(scope || els[i], els[i], this, i) === false) {
10763                 break;
10764             }
10765         }
10766         return this;
10767     },
10768
10769     /**
10770      * Returns the Element object at the specified index
10771      * @param {Number} index
10772      * @return {Roo.Element}
10773      */
10774     item : function(index){
10775         return this.elements[index] || null;
10776     },
10777
10778     /**
10779      * Returns the first Element
10780      * @return {Roo.Element}
10781      */
10782     first : function(){
10783         return this.item(0);
10784     },
10785
10786     /**
10787      * Returns the last Element
10788      * @return {Roo.Element}
10789      */
10790     last : function(){
10791         return this.item(this.elements.length-1);
10792     },
10793
10794     /**
10795      * Returns the number of elements in this composite
10796      * @return Number
10797      */
10798     getCount : function(){
10799         return this.elements.length;
10800     },
10801
10802     /**
10803      * Returns true if this composite contains the passed element
10804      * @return Boolean
10805      */
10806     contains : function(el){
10807         return this.indexOf(el) !== -1;
10808     },
10809
10810     /**
10811      * Returns true if this composite contains the passed element
10812      * @return Boolean
10813      */
10814     indexOf : function(el){
10815         return this.elements.indexOf(Roo.get(el));
10816     },
10817
10818
10819     /**
10820     * Removes the specified element(s).
10821     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10822     * or an array of any of those.
10823     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10824     * @return {CompositeElement} this
10825     */
10826     removeElement : function(el, removeDom){
10827         if(el instanceof Array){
10828             for(var i = 0, len = el.length; i < len; i++){
10829                 this.removeElement(el[i]);
10830             }
10831             return this;
10832         }
10833         var index = typeof el == 'number' ? el : this.indexOf(el);
10834         if(index !== -1){
10835             if(removeDom){
10836                 var d = this.elements[index];
10837                 if(d.dom){
10838                     d.remove();
10839                 }else{
10840                     d.parentNode.removeChild(d);
10841                 }
10842             }
10843             this.elements.splice(index, 1);
10844         }
10845         return this;
10846     },
10847
10848     /**
10849     * Replaces the specified element with the passed element.
10850     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10851     * to replace.
10852     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10853     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10854     * @return {CompositeElement} this
10855     */
10856     replaceElement : function(el, replacement, domReplace){
10857         var index = typeof el == 'number' ? el : this.indexOf(el);
10858         if(index !== -1){
10859             if(domReplace){
10860                 this.elements[index].replaceWith(replacement);
10861             }else{
10862                 this.elements.splice(index, 1, Roo.get(replacement))
10863             }
10864         }
10865         return this;
10866     },
10867
10868     /**
10869      * Removes all elements.
10870      */
10871     clear : function(){
10872         this.elements = [];
10873     }
10874 };
10875 (function(){
10876     Roo.CompositeElement.createCall = function(proto, fnName){
10877         if(!proto[fnName]){
10878             proto[fnName] = function(){
10879                 return this.invoke(fnName, arguments);
10880             };
10881         }
10882     };
10883     for(var fnName in Roo.Element.prototype){
10884         if(typeof Roo.Element.prototype[fnName] == "function"){
10885             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10886         }
10887     };
10888 })();
10889 /*
10890  * Based on:
10891  * Ext JS Library 1.1.1
10892  * Copyright(c) 2006-2007, Ext JS, LLC.
10893  *
10894  * Originally Released Under LGPL - original licence link has changed is not relivant.
10895  *
10896  * Fork - LGPL
10897  * <script type="text/javascript">
10898  */
10899
10900 /**
10901  * @class Roo.CompositeElementLite
10902  * @extends Roo.CompositeElement
10903  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10904  <pre><code>
10905  var els = Roo.select("#some-el div.some-class");
10906  // or select directly from an existing element
10907  var el = Roo.get('some-el');
10908  el.select('div.some-class');
10909
10910  els.setWidth(100); // all elements become 100 width
10911  els.hide(true); // all elements fade out and hide
10912  // or
10913  els.setWidth(100).hide(true);
10914  </code></pre><br><br>
10915  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10916  * actions will be performed on all the elements in this collection.</b>
10917  */
10918 Roo.CompositeElementLite = function(els){
10919     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10920     this.el = new Roo.Element.Flyweight();
10921 };
10922 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10923     addElements : function(els){
10924         if(els){
10925             if(els instanceof Array){
10926                 this.elements = this.elements.concat(els);
10927             }else{
10928                 var yels = this.elements;
10929                 var index = yels.length-1;
10930                 for(var i = 0, len = els.length; i < len; i++) {
10931                     yels[++index] = els[i];
10932                 }
10933             }
10934         }
10935         return this;
10936     },
10937     invoke : function(fn, args){
10938         var els = this.elements;
10939         var el = this.el;
10940         for(var i = 0, len = els.length; i < len; i++) {
10941             el.dom = els[i];
10942                 Roo.Element.prototype[fn].apply(el, args);
10943         }
10944         return this;
10945     },
10946     /**
10947      * Returns a flyweight Element of the dom element object at the specified index
10948      * @param {Number} index
10949      * @return {Roo.Element}
10950      */
10951     item : function(index){
10952         if(!this.elements[index]){
10953             return null;
10954         }
10955         this.el.dom = this.elements[index];
10956         return this.el;
10957     },
10958
10959     // fixes scope with flyweight
10960     addListener : function(eventName, handler, scope, opt){
10961         var els = this.elements;
10962         for(var i = 0, len = els.length; i < len; i++) {
10963             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10964         }
10965         return this;
10966     },
10967
10968     /**
10969     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10970     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10971     * a reference to the dom node, use el.dom.</b>
10972     * @param {Function} fn The function to call
10973     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10974     * @return {CompositeElement} this
10975     */
10976     each : function(fn, scope){
10977         var els = this.elements;
10978         var el = this.el;
10979         for(var i = 0, len = els.length; i < len; i++){
10980             el.dom = els[i];
10981                 if(fn.call(scope || el, el, this, i) === false){
10982                 break;
10983             }
10984         }
10985         return this;
10986     },
10987
10988     indexOf : function(el){
10989         return this.elements.indexOf(Roo.getDom(el));
10990     },
10991
10992     replaceElement : function(el, replacement, domReplace){
10993         var index = typeof el == 'number' ? el : this.indexOf(el);
10994         if(index !== -1){
10995             replacement = Roo.getDom(replacement);
10996             if(domReplace){
10997                 var d = this.elements[index];
10998                 d.parentNode.insertBefore(replacement, d);
10999                 d.parentNode.removeChild(d);
11000             }
11001             this.elements.splice(index, 1, replacement);
11002         }
11003         return this;
11004     }
11005 });
11006 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11007
11008 /*
11009  * Based on:
11010  * Ext JS Library 1.1.1
11011  * Copyright(c) 2006-2007, Ext JS, LLC.
11012  *
11013  * Originally Released Under LGPL - original licence link has changed is not relivant.
11014  *
11015  * Fork - LGPL
11016  * <script type="text/javascript">
11017  */
11018
11019  
11020
11021 /**
11022  * @class Roo.data.Connection
11023  * @extends Roo.util.Observable
11024  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11025  * either to a configured URL, or to a URL specified at request time.<br><br>
11026  * <p>
11027  * Requests made by this class are asynchronous, and will return immediately. No data from
11028  * the server will be available to the statement immediately following the {@link #request} call.
11029  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11030  * <p>
11031  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11032  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11033  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11034  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11035  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11036  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11037  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11038  * standard DOM methods.
11039  * @constructor
11040  * @param {Object} config a configuration object.
11041  */
11042 Roo.data.Connection = function(config){
11043     Roo.apply(this, config);
11044     this.addEvents({
11045         /**
11046          * @event beforerequest
11047          * Fires before a network request is made to retrieve a data object.
11048          * @param {Connection} conn This Connection object.
11049          * @param {Object} options The options config object passed to the {@link #request} method.
11050          */
11051         "beforerequest" : true,
11052         /**
11053          * @event requestcomplete
11054          * Fires if the request was successfully completed.
11055          * @param {Connection} conn This Connection object.
11056          * @param {Object} response The XHR object containing the response data.
11057          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11058          * @param {Object} options The options config object passed to the {@link #request} method.
11059          */
11060         "requestcomplete" : true,
11061         /**
11062          * @event requestexception
11063          * Fires if an error HTTP status was returned from the server.
11064          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11065          * @param {Connection} conn This Connection object.
11066          * @param {Object} response The XHR object containing the response data.
11067          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11068          * @param {Object} options The options config object passed to the {@link #request} method.
11069          */
11070         "requestexception" : true
11071     });
11072     Roo.data.Connection.superclass.constructor.call(this);
11073 };
11074
11075 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11076     /**
11077      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11078      */
11079     /**
11080      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11081      * extra parameters to each request made by this object. (defaults to undefined)
11082      */
11083     /**
11084      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11085      *  to each request made by this object. (defaults to undefined)
11086      */
11087     /**
11088      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11089      */
11090     /**
11091      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11092      */
11093     timeout : 30000,
11094     /**
11095      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11096      * @type Boolean
11097      */
11098     autoAbort:false,
11099
11100     /**
11101      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11102      * @type Boolean
11103      */
11104     disableCaching: true,
11105
11106     /**
11107      * Sends an HTTP request to a remote server.
11108      * @param {Object} options An object which may contain the following properties:<ul>
11109      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11110      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11111      * request, a url encoded string or a function to call to get either.</li>
11112      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11113      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11114      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11115      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11116      * <li>options {Object} The parameter to the request call.</li>
11117      * <li>success {Boolean} True if the request succeeded.</li>
11118      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11119      * </ul></li>
11120      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11121      * The callback is passed the following parameters:<ul>
11122      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11123      * <li>options {Object} The parameter to the request call.</li>
11124      * </ul></li>
11125      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11126      * The callback is passed the following parameters:<ul>
11127      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11128      * <li>options {Object} The parameter to the request call.</li>
11129      * </ul></li>
11130      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11131      * for the callback function. Defaults to the browser window.</li>
11132      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11133      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11134      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11135      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11136      * params for the post data. Any params will be appended to the URL.</li>
11137      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11138      * </ul>
11139      * @return {Number} transactionId
11140      */
11141     request : function(o){
11142         if(this.fireEvent("beforerequest", this, o) !== false){
11143             var p = o.params;
11144
11145             if(typeof p == "function"){
11146                 p = p.call(o.scope||window, o);
11147             }
11148             if(typeof p == "object"){
11149                 p = Roo.urlEncode(o.params);
11150             }
11151             if(this.extraParams){
11152                 var extras = Roo.urlEncode(this.extraParams);
11153                 p = p ? (p + '&' + extras) : extras;
11154             }
11155
11156             var url = o.url || this.url;
11157             if(typeof url == 'function'){
11158                 url = url.call(o.scope||window, o);
11159             }
11160
11161             if(o.form){
11162                 var form = Roo.getDom(o.form);
11163                 url = url || form.action;
11164
11165                 var enctype = form.getAttribute("enctype");
11166                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11167                     return this.doFormUpload(o, p, url);
11168                 }
11169                 var f = Roo.lib.Ajax.serializeForm(form);
11170                 p = p ? (p + '&' + f) : f;
11171             }
11172
11173             var hs = o.headers;
11174             if(this.defaultHeaders){
11175                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11176                 if(!o.headers){
11177                     o.headers = hs;
11178                 }
11179             }
11180
11181             var cb = {
11182                 success: this.handleResponse,
11183                 failure: this.handleFailure,
11184                 scope: this,
11185                 argument: {options: o},
11186                 timeout : this.timeout
11187             };
11188
11189             var method = o.method||this.method||(p ? "POST" : "GET");
11190
11191             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11192                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11193             }
11194
11195             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11196                 if(o.autoAbort){
11197                     this.abort();
11198                 }
11199             }else if(this.autoAbort !== false){
11200                 this.abort();
11201             }
11202
11203             if((method == 'GET' && p) || o.xmlData){
11204                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11205                 p = '';
11206             }
11207             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11208             return this.transId;
11209         }else{
11210             Roo.callback(o.callback, o.scope, [o, null, null]);
11211             return null;
11212         }
11213     },
11214
11215     /**
11216      * Determine whether this object has a request outstanding.
11217      * @param {Number} transactionId (Optional) defaults to the last transaction
11218      * @return {Boolean} True if there is an outstanding request.
11219      */
11220     isLoading : function(transId){
11221         if(transId){
11222             return Roo.lib.Ajax.isCallInProgress(transId);
11223         }else{
11224             return this.transId ? true : false;
11225         }
11226     },
11227
11228     /**
11229      * Aborts any outstanding request.
11230      * @param {Number} transactionId (Optional) defaults to the last transaction
11231      */
11232     abort : function(transId){
11233         if(transId || this.isLoading()){
11234             Roo.lib.Ajax.abort(transId || this.transId);
11235         }
11236     },
11237
11238     // private
11239     handleResponse : function(response){
11240         this.transId = false;
11241         var options = response.argument.options;
11242         response.argument = options ? options.argument : null;
11243         this.fireEvent("requestcomplete", this, response, options);
11244         Roo.callback(options.success, options.scope, [response, options]);
11245         Roo.callback(options.callback, options.scope, [options, true, response]);
11246     },
11247
11248     // private
11249     handleFailure : function(response, e){
11250         this.transId = false;
11251         var options = response.argument.options;
11252         response.argument = options ? options.argument : null;
11253         this.fireEvent("requestexception", this, response, options, e);
11254         Roo.callback(options.failure, options.scope, [response, options]);
11255         Roo.callback(options.callback, options.scope, [options, false, response]);
11256     },
11257
11258     // private
11259     doFormUpload : function(o, ps, url){
11260         var id = Roo.id();
11261         var frame = document.createElement('iframe');
11262         frame.id = id;
11263         frame.name = id;
11264         frame.className = 'x-hidden';
11265         if(Roo.isIE){
11266             frame.src = Roo.SSL_SECURE_URL;
11267         }
11268         document.body.appendChild(frame);
11269
11270         if(Roo.isIE){
11271            document.frames[id].name = id;
11272         }
11273
11274         var form = Roo.getDom(o.form);
11275         form.target = id;
11276         form.method = 'POST';
11277         form.enctype = form.encoding = 'multipart/form-data';
11278         if(url){
11279             form.action = url;
11280         }
11281
11282         var hiddens, hd;
11283         if(ps){ // add dynamic params
11284             hiddens = [];
11285             ps = Roo.urlDecode(ps, false);
11286             for(var k in ps){
11287                 if(ps.hasOwnProperty(k)){
11288                     hd = document.createElement('input');
11289                     hd.type = 'hidden';
11290                     hd.name = k;
11291                     hd.value = ps[k];
11292                     form.appendChild(hd);
11293                     hiddens.push(hd);
11294                 }
11295             }
11296         }
11297
11298         function cb(){
11299             var r = {  // bogus response object
11300                 responseText : '',
11301                 responseXML : null
11302             };
11303
11304             r.argument = o ? o.argument : null;
11305
11306             try { //
11307                 var doc;
11308                 if(Roo.isIE){
11309                     doc = frame.contentWindow.document;
11310                 }else {
11311                     doc = (frame.contentDocument || window.frames[id].document);
11312                 }
11313                 if(doc && doc.body){
11314                     r.responseText = doc.body.innerHTML;
11315                 }
11316                 if(doc && doc.XMLDocument){
11317                     r.responseXML = doc.XMLDocument;
11318                 }else {
11319                     r.responseXML = doc;
11320                 }
11321             }
11322             catch(e) {
11323                 // ignore
11324             }
11325
11326             Roo.EventManager.removeListener(frame, 'load', cb, this);
11327
11328             this.fireEvent("requestcomplete", this, r, o);
11329             Roo.callback(o.success, o.scope, [r, o]);
11330             Roo.callback(o.callback, o.scope, [o, true, r]);
11331
11332             setTimeout(function(){document.body.removeChild(frame);}, 100);
11333         }
11334
11335         Roo.EventManager.on(frame, 'load', cb, this);
11336         form.submit();
11337
11338         if(hiddens){ // remove dynamic params
11339             for(var i = 0, len = hiddens.length; i < len; i++){
11340                 form.removeChild(hiddens[i]);
11341             }
11342         }
11343     }
11344 });
11345
11346 /**
11347  * @class Roo.Ajax
11348  * @extends Roo.data.Connection
11349  * Global Ajax request class.
11350  *
11351  * @singleton
11352  */
11353 Roo.Ajax = new Roo.data.Connection({
11354     // fix up the docs
11355    /**
11356      * @cfg {String} url @hide
11357      */
11358     /**
11359      * @cfg {Object} extraParams @hide
11360      */
11361     /**
11362      * @cfg {Object} defaultHeaders @hide
11363      */
11364     /**
11365      * @cfg {String} method (Optional) @hide
11366      */
11367     /**
11368      * @cfg {Number} timeout (Optional) @hide
11369      */
11370     /**
11371      * @cfg {Boolean} autoAbort (Optional) @hide
11372      */
11373
11374     /**
11375      * @cfg {Boolean} disableCaching (Optional) @hide
11376      */
11377
11378     /**
11379      * @property  disableCaching
11380      * True to add a unique cache-buster param to GET requests. (defaults to true)
11381      * @type Boolean
11382      */
11383     /**
11384      * @property  url
11385      * The default URL to be used for requests to the server. (defaults to undefined)
11386      * @type String
11387      */
11388     /**
11389      * @property  extraParams
11390      * An object containing properties which are used as
11391      * extra parameters to each request made by this object. (defaults to undefined)
11392      * @type Object
11393      */
11394     /**
11395      * @property  defaultHeaders
11396      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11397      * @type Object
11398      */
11399     /**
11400      * @property  method
11401      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11402      * @type String
11403      */
11404     /**
11405      * @property  timeout
11406      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11407      * @type Number
11408      */
11409
11410     /**
11411      * @property  autoAbort
11412      * Whether a new request should abort any pending requests. (defaults to false)
11413      * @type Boolean
11414      */
11415     autoAbort : false,
11416
11417     /**
11418      * Serialize the passed form into a url encoded string
11419      * @param {String/HTMLElement} form
11420      * @return {String}
11421      */
11422     serializeForm : function(form){
11423         return Roo.lib.Ajax.serializeForm(form);
11424     }
11425 });/*
11426  * Based on:
11427  * Ext JS Library 1.1.1
11428  * Copyright(c) 2006-2007, Ext JS, LLC.
11429  *
11430  * Originally Released Under LGPL - original licence link has changed is not relivant.
11431  *
11432  * Fork - LGPL
11433  * <script type="text/javascript">
11434  */
11435  
11436 /**
11437  * @class Roo.Ajax
11438  * @extends Roo.data.Connection
11439  * Global Ajax request class.
11440  *
11441  * @instanceOf  Roo.data.Connection
11442  */
11443 Roo.Ajax = new Roo.data.Connection({
11444     // fix up the docs
11445     
11446     /**
11447      * fix up scoping
11448      * @scope Roo.Ajax
11449      */
11450     
11451    /**
11452      * @cfg {String} url @hide
11453      */
11454     /**
11455      * @cfg {Object} extraParams @hide
11456      */
11457     /**
11458      * @cfg {Object} defaultHeaders @hide
11459      */
11460     /**
11461      * @cfg {String} method (Optional) @hide
11462      */
11463     /**
11464      * @cfg {Number} timeout (Optional) @hide
11465      */
11466     /**
11467      * @cfg {Boolean} autoAbort (Optional) @hide
11468      */
11469
11470     /**
11471      * @cfg {Boolean} disableCaching (Optional) @hide
11472      */
11473
11474     /**
11475      * @property  disableCaching
11476      * True to add a unique cache-buster param to GET requests. (defaults to true)
11477      * @type Boolean
11478      */
11479     /**
11480      * @property  url
11481      * The default URL to be used for requests to the server. (defaults to undefined)
11482      * @type String
11483      */
11484     /**
11485      * @property  extraParams
11486      * An object containing properties which are used as
11487      * extra parameters to each request made by this object. (defaults to undefined)
11488      * @type Object
11489      */
11490     /**
11491      * @property  defaultHeaders
11492      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11493      * @type Object
11494      */
11495     /**
11496      * @property  method
11497      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11498      * @type String
11499      */
11500     /**
11501      * @property  timeout
11502      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11503      * @type Number
11504      */
11505
11506     /**
11507      * @property  autoAbort
11508      * Whether a new request should abort any pending requests. (defaults to false)
11509      * @type Boolean
11510      */
11511     autoAbort : false,
11512
11513     /**
11514      * Serialize the passed form into a url encoded string
11515      * @param {String/HTMLElement} form
11516      * @return {String}
11517      */
11518     serializeForm : function(form){
11519         return Roo.lib.Ajax.serializeForm(form);
11520     }
11521 });/*
11522  * Based on:
11523  * Ext JS Library 1.1.1
11524  * Copyright(c) 2006-2007, Ext JS, LLC.
11525  *
11526  * Originally Released Under LGPL - original licence link has changed is not relivant.
11527  *
11528  * Fork - LGPL
11529  * <script type="text/javascript">
11530  */
11531
11532  
11533 /**
11534  * @class Roo.UpdateManager
11535  * @extends Roo.util.Observable
11536  * Provides AJAX-style update for Element object.<br><br>
11537  * Usage:<br>
11538  * <pre><code>
11539  * // Get it from a Roo.Element object
11540  * var el = Roo.get("foo");
11541  * var mgr = el.getUpdateManager();
11542  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11543  * ...
11544  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11545  * <br>
11546  * // or directly (returns the same UpdateManager instance)
11547  * var mgr = new Roo.UpdateManager("myElementId");
11548  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11549  * mgr.on("update", myFcnNeedsToKnow);
11550  * <br>
11551    // short handed call directly from the element object
11552    Roo.get("foo").load({
11553         url: "bar.php",
11554         scripts:true,
11555         params: "for=bar",
11556         text: "Loading Foo..."
11557    });
11558  * </code></pre>
11559  * @constructor
11560  * Create new UpdateManager directly.
11561  * @param {String/HTMLElement/Roo.Element} el The element to update
11562  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11563  */
11564 Roo.UpdateManager = function(el, forceNew){
11565     el = Roo.get(el);
11566     if(!forceNew && el.updateManager){
11567         return el.updateManager;
11568     }
11569     /**
11570      * The Element object
11571      * @type Roo.Element
11572      */
11573     this.el = el;
11574     /**
11575      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11576      * @type String
11577      */
11578     this.defaultUrl = null;
11579
11580     this.addEvents({
11581         /**
11582          * @event beforeupdate
11583          * Fired before an update is made, return false from your handler and the update is cancelled.
11584          * @param {Roo.Element} el
11585          * @param {String/Object/Function} url
11586          * @param {String/Object} params
11587          */
11588         "beforeupdate": true,
11589         /**
11590          * @event update
11591          * Fired after successful update is made.
11592          * @param {Roo.Element} el
11593          * @param {Object} oResponseObject The response Object
11594          */
11595         "update": true,
11596         /**
11597          * @event failure
11598          * Fired on update failure.
11599          * @param {Roo.Element} el
11600          * @param {Object} oResponseObject The response Object
11601          */
11602         "failure": true
11603     });
11604     var d = Roo.UpdateManager.defaults;
11605     /**
11606      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11607      * @type String
11608      */
11609     this.sslBlankUrl = d.sslBlankUrl;
11610     /**
11611      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11612      * @type Boolean
11613      */
11614     this.disableCaching = d.disableCaching;
11615     /**
11616      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11617      * @type String
11618      */
11619     this.indicatorText = d.indicatorText;
11620     /**
11621      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11622      * @type String
11623      */
11624     this.showLoadIndicator = d.showLoadIndicator;
11625     /**
11626      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11627      * @type Number
11628      */
11629     this.timeout = d.timeout;
11630
11631     /**
11632      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11633      * @type Boolean
11634      */
11635     this.loadScripts = d.loadScripts;
11636
11637     /**
11638      * Transaction object of current executing transaction
11639      */
11640     this.transaction = null;
11641
11642     /**
11643      * @private
11644      */
11645     this.autoRefreshProcId = null;
11646     /**
11647      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11648      * @type Function
11649      */
11650     this.refreshDelegate = this.refresh.createDelegate(this);
11651     /**
11652      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11653      * @type Function
11654      */
11655     this.updateDelegate = this.update.createDelegate(this);
11656     /**
11657      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11658      * @type Function
11659      */
11660     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11661     /**
11662      * @private
11663      */
11664     this.successDelegate = this.processSuccess.createDelegate(this);
11665     /**
11666      * @private
11667      */
11668     this.failureDelegate = this.processFailure.createDelegate(this);
11669
11670     if(!this.renderer){
11671      /**
11672       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11673       */
11674     this.renderer = new Roo.UpdateManager.BasicRenderer();
11675     }
11676     
11677     Roo.UpdateManager.superclass.constructor.call(this);
11678 };
11679
11680 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11681     /**
11682      * Get the Element this UpdateManager is bound to
11683      * @return {Roo.Element} The element
11684      */
11685     getEl : function(){
11686         return this.el;
11687     },
11688     /**
11689      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11690      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11691 <pre><code>
11692 um.update({<br/>
11693     url: "your-url.php",<br/>
11694     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11695     callback: yourFunction,<br/>
11696     scope: yourObject, //(optional scope)  <br/>
11697     discardUrl: false, <br/>
11698     nocache: false,<br/>
11699     text: "Loading...",<br/>
11700     timeout: 30,<br/>
11701     scripts: false<br/>
11702 });
11703 </code></pre>
11704      * The only required property is url. The optional properties nocache, text and scripts
11705      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11706      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11707      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11708      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11709      */
11710     update : function(url, params, callback, discardUrl){
11711         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11712             var method = this.method, cfg;
11713             if(typeof url == "object"){ // must be config object
11714                 cfg = url;
11715                 url = cfg.url;
11716                 params = params || cfg.params;
11717                 callback = callback || cfg.callback;
11718                 discardUrl = discardUrl || cfg.discardUrl;
11719                 if(callback && cfg.scope){
11720                     callback = callback.createDelegate(cfg.scope);
11721                 }
11722                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11723                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11724                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11725                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11726                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11727             }
11728             this.showLoading();
11729             if(!discardUrl){
11730                 this.defaultUrl = url;
11731             }
11732             if(typeof url == "function"){
11733                 url = url.call(this);
11734             }
11735
11736             method = method || (params ? "POST" : "GET");
11737             if(method == "GET"){
11738                 url = this.prepareUrl(url);
11739             }
11740
11741             var o = Roo.apply(cfg ||{}, {
11742                 url : url,
11743                 params: params,
11744                 success: this.successDelegate,
11745                 failure: this.failureDelegate,
11746                 callback: undefined,
11747                 timeout: (this.timeout*1000),
11748                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11749             });
11750
11751             this.transaction = Roo.Ajax.request(o);
11752         }
11753     },
11754
11755     /**
11756      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11757      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11758      * @param {String/HTMLElement} form The form Id or form element
11759      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11760      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11761      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11762      */
11763     formUpdate : function(form, url, reset, callback){
11764         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11765             if(typeof url == "function"){
11766                 url = url.call(this);
11767             }
11768             form = Roo.getDom(form);
11769             this.transaction = Roo.Ajax.request({
11770                 form: form,
11771                 url:url,
11772                 success: this.successDelegate,
11773                 failure: this.failureDelegate,
11774                 timeout: (this.timeout*1000),
11775                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11776             });
11777             this.showLoading.defer(1, this);
11778         }
11779     },
11780
11781     /**
11782      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11783      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11784      */
11785     refresh : function(callback){
11786         if(this.defaultUrl == null){
11787             return;
11788         }
11789         this.update(this.defaultUrl, null, callback, true);
11790     },
11791
11792     /**
11793      * Set this element to auto refresh.
11794      * @param {Number} interval How often to update (in seconds).
11795      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11796      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11797      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11798      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11799      */
11800     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11801         if(refreshNow){
11802             this.update(url || this.defaultUrl, params, callback, true);
11803         }
11804         if(this.autoRefreshProcId){
11805             clearInterval(this.autoRefreshProcId);
11806         }
11807         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11808     },
11809
11810     /**
11811      * Stop auto refresh on this element.
11812      */
11813      stopAutoRefresh : function(){
11814         if(this.autoRefreshProcId){
11815             clearInterval(this.autoRefreshProcId);
11816             delete this.autoRefreshProcId;
11817         }
11818     },
11819
11820     isAutoRefreshing : function(){
11821        return this.autoRefreshProcId ? true : false;
11822     },
11823     /**
11824      * Called to update the element to "Loading" state. Override to perform custom action.
11825      */
11826     showLoading : function(){
11827         if(this.showLoadIndicator){
11828             this.el.update(this.indicatorText);
11829         }
11830     },
11831
11832     /**
11833      * Adds unique parameter to query string if disableCaching = true
11834      * @private
11835      */
11836     prepareUrl : function(url){
11837         if(this.disableCaching){
11838             var append = "_dc=" + (new Date().getTime());
11839             if(url.indexOf("?") !== -1){
11840                 url += "&" + append;
11841             }else{
11842                 url += "?" + append;
11843             }
11844         }
11845         return url;
11846     },
11847
11848     /**
11849      * @private
11850      */
11851     processSuccess : function(response){
11852         this.transaction = null;
11853         if(response.argument.form && response.argument.reset){
11854             try{ // put in try/catch since some older FF releases had problems with this
11855                 response.argument.form.reset();
11856             }catch(e){}
11857         }
11858         if(this.loadScripts){
11859             this.renderer.render(this.el, response, this,
11860                 this.updateComplete.createDelegate(this, [response]));
11861         }else{
11862             this.renderer.render(this.el, response, this);
11863             this.updateComplete(response);
11864         }
11865     },
11866
11867     updateComplete : function(response){
11868         this.fireEvent("update", this.el, response);
11869         if(typeof response.argument.callback == "function"){
11870             response.argument.callback(this.el, true, response);
11871         }
11872     },
11873
11874     /**
11875      * @private
11876      */
11877     processFailure : function(response){
11878         this.transaction = null;
11879         this.fireEvent("failure", this.el, response);
11880         if(typeof response.argument.callback == "function"){
11881             response.argument.callback(this.el, false, response);
11882         }
11883     },
11884
11885     /**
11886      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11887      * @param {Object} renderer The object implementing the render() method
11888      */
11889     setRenderer : function(renderer){
11890         this.renderer = renderer;
11891     },
11892
11893     getRenderer : function(){
11894        return this.renderer;
11895     },
11896
11897     /**
11898      * Set the defaultUrl used for updates
11899      * @param {String/Function} defaultUrl The url or a function to call to get the url
11900      */
11901     setDefaultUrl : function(defaultUrl){
11902         this.defaultUrl = defaultUrl;
11903     },
11904
11905     /**
11906      * Aborts the executing transaction
11907      */
11908     abort : function(){
11909         if(this.transaction){
11910             Roo.Ajax.abort(this.transaction);
11911         }
11912     },
11913
11914     /**
11915      * Returns true if an update is in progress
11916      * @return {Boolean}
11917      */
11918     isUpdating : function(){
11919         if(this.transaction){
11920             return Roo.Ajax.isLoading(this.transaction);
11921         }
11922         return false;
11923     }
11924 });
11925
11926 /**
11927  * @class Roo.UpdateManager.defaults
11928  * @static (not really - but it helps the doc tool)
11929  * The defaults collection enables customizing the default properties of UpdateManager
11930  */
11931    Roo.UpdateManager.defaults = {
11932        /**
11933          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11934          * @type Number
11935          */
11936          timeout : 30,
11937
11938          /**
11939          * True to process scripts by default (Defaults to false).
11940          * @type Boolean
11941          */
11942         loadScripts : false,
11943
11944         /**
11945         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11946         * @type String
11947         */
11948         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11949         /**
11950          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11951          * @type Boolean
11952          */
11953         disableCaching : false,
11954         /**
11955          * Whether to show indicatorText when loading (Defaults to true).
11956          * @type Boolean
11957          */
11958         showLoadIndicator : true,
11959         /**
11960          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11961          * @type String
11962          */
11963         indicatorText : '<div class="loading-indicator">Loading...</div>'
11964    };
11965
11966 /**
11967  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11968  *Usage:
11969  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11970  * @param {String/HTMLElement/Roo.Element} el The element to update
11971  * @param {String} url The url
11972  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11973  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11974  * @static
11975  * @deprecated
11976  * @member Roo.UpdateManager
11977  */
11978 Roo.UpdateManager.updateElement = function(el, url, params, options){
11979     var um = Roo.get(el, true).getUpdateManager();
11980     Roo.apply(um, options);
11981     um.update(url, params, options ? options.callback : null);
11982 };
11983 // alias for backwards compat
11984 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
11985 /**
11986  * @class Roo.UpdateManager.BasicRenderer
11987  * Default Content renderer. Updates the elements innerHTML with the responseText.
11988  */
11989 Roo.UpdateManager.BasicRenderer = function(){};
11990
11991 Roo.UpdateManager.BasicRenderer.prototype = {
11992     /**
11993      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
11994      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
11995      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
11996      * @param {Roo.Element} el The element being rendered
11997      * @param {Object} response The YUI Connect response object
11998      * @param {UpdateManager} updateManager The calling update manager
11999      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12000      */
12001      render : function(el, response, updateManager, callback){
12002         el.update(response.responseText, updateManager.loadScripts, callback);
12003     }
12004 };
12005 /*
12006  * Based on:
12007  * Ext JS Library 1.1.1
12008  * Copyright(c) 2006-2007, Ext JS, LLC.
12009  *
12010  * Originally Released Under LGPL - original licence link has changed is not relivant.
12011  *
12012  * Fork - LGPL
12013  * <script type="text/javascript">
12014  */
12015
12016 /**
12017  * @class Roo.util.DelayedTask
12018  * Provides a convenient method of performing setTimeout where a new
12019  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12020  * You can use this class to buffer
12021  * the keypress events for a certain number of milliseconds, and perform only if they stop
12022  * for that amount of time.
12023  * @constructor The parameters to this constructor serve as defaults and are not required.
12024  * @param {Function} fn (optional) The default function to timeout
12025  * @param {Object} scope (optional) The default scope of that timeout
12026  * @param {Array} args (optional) The default Array of arguments
12027  */
12028 Roo.util.DelayedTask = function(fn, scope, args){
12029     var id = null, d, t;
12030
12031     var call = function(){
12032         var now = new Date().getTime();
12033         if(now - t >= d){
12034             clearInterval(id);
12035             id = null;
12036             fn.apply(scope, args || []);
12037         }
12038     };
12039     /**
12040      * Cancels any pending timeout and queues a new one
12041      * @param {Number} delay The milliseconds to delay
12042      * @param {Function} newFn (optional) Overrides function passed to constructor
12043      * @param {Object} newScope (optional) Overrides scope passed to constructor
12044      * @param {Array} newArgs (optional) Overrides args passed to constructor
12045      */
12046     this.delay = function(delay, newFn, newScope, newArgs){
12047         if(id && delay != d){
12048             this.cancel();
12049         }
12050         d = delay;
12051         t = new Date().getTime();
12052         fn = newFn || fn;
12053         scope = newScope || scope;
12054         args = newArgs || args;
12055         if(!id){
12056             id = setInterval(call, d);
12057         }
12058     };
12059
12060     /**
12061      * Cancel the last queued timeout
12062      */
12063     this.cancel = function(){
12064         if(id){
12065             clearInterval(id);
12066             id = null;
12067         }
12068     };
12069 };/*
12070  * Based on:
12071  * Ext JS Library 1.1.1
12072  * Copyright(c) 2006-2007, Ext JS, LLC.
12073  *
12074  * Originally Released Under LGPL - original licence link has changed is not relivant.
12075  *
12076  * Fork - LGPL
12077  * <script type="text/javascript">
12078  */
12079  
12080  
12081 Roo.util.TaskRunner = function(interval){
12082     interval = interval || 10;
12083     var tasks = [], removeQueue = [];
12084     var id = 0;
12085     var running = false;
12086
12087     var stopThread = function(){
12088         running = false;
12089         clearInterval(id);
12090         id = 0;
12091     };
12092
12093     var startThread = function(){
12094         if(!running){
12095             running = true;
12096             id = setInterval(runTasks, interval);
12097         }
12098     };
12099
12100     var removeTask = function(task){
12101         removeQueue.push(task);
12102         if(task.onStop){
12103             task.onStop();
12104         }
12105     };
12106
12107     var runTasks = function(){
12108         if(removeQueue.length > 0){
12109             for(var i = 0, len = removeQueue.length; i < len; i++){
12110                 tasks.remove(removeQueue[i]);
12111             }
12112             removeQueue = [];
12113             if(tasks.length < 1){
12114                 stopThread();
12115                 return;
12116             }
12117         }
12118         var now = new Date().getTime();
12119         for(var i = 0, len = tasks.length; i < len; ++i){
12120             var t = tasks[i];
12121             var itime = now - t.taskRunTime;
12122             if(t.interval <= itime){
12123                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12124                 t.taskRunTime = now;
12125                 if(rt === false || t.taskRunCount === t.repeat){
12126                     removeTask(t);
12127                     return;
12128                 }
12129             }
12130             if(t.duration && t.duration <= (now - t.taskStartTime)){
12131                 removeTask(t);
12132             }
12133         }
12134     };
12135
12136     /**
12137      * Queues a new task.
12138      * @param {Object} task
12139      */
12140     this.start = function(task){
12141         tasks.push(task);
12142         task.taskStartTime = new Date().getTime();
12143         task.taskRunTime = 0;
12144         task.taskRunCount = 0;
12145         startThread();
12146         return task;
12147     };
12148
12149     this.stop = function(task){
12150         removeTask(task);
12151         return task;
12152     };
12153
12154     this.stopAll = function(){
12155         stopThread();
12156         for(var i = 0, len = tasks.length; i < len; i++){
12157             if(tasks[i].onStop){
12158                 tasks[i].onStop();
12159             }
12160         }
12161         tasks = [];
12162         removeQueue = [];
12163     };
12164 };
12165
12166 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12167  * Based on:
12168  * Ext JS Library 1.1.1
12169  * Copyright(c) 2006-2007, Ext JS, LLC.
12170  *
12171  * Originally Released Under LGPL - original licence link has changed is not relivant.
12172  *
12173  * Fork - LGPL
12174  * <script type="text/javascript">
12175  */
12176
12177  
12178 /**
12179  * @class Roo.util.MixedCollection
12180  * @extends Roo.util.Observable
12181  * A Collection class that maintains both numeric indexes and keys and exposes events.
12182  * @constructor
12183  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12184  * collection (defaults to false)
12185  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12186  * and return the key value for that item.  This is used when available to look up the key on items that
12187  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12188  * equivalent to providing an implementation for the {@link #getKey} method.
12189  */
12190 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12191     this.items = [];
12192     this.map = {};
12193     this.keys = [];
12194     this.length = 0;
12195     this.addEvents({
12196         /**
12197          * @event clear
12198          * Fires when the collection is cleared.
12199          */
12200         "clear" : true,
12201         /**
12202          * @event add
12203          * Fires when an item is added to the collection.
12204          * @param {Number} index The index at which the item was added.
12205          * @param {Object} o The item added.
12206          * @param {String} key The key associated with the added item.
12207          */
12208         "add" : true,
12209         /**
12210          * @event replace
12211          * Fires when an item is replaced in the collection.
12212          * @param {String} key he key associated with the new added.
12213          * @param {Object} old The item being replaced.
12214          * @param {Object} new The new item.
12215          */
12216         "replace" : true,
12217         /**
12218          * @event remove
12219          * Fires when an item is removed from the collection.
12220          * @param {Object} o The item being removed.
12221          * @param {String} key (optional) The key associated with the removed item.
12222          */
12223         "remove" : true,
12224         "sort" : true
12225     });
12226     this.allowFunctions = allowFunctions === true;
12227     if(keyFn){
12228         this.getKey = keyFn;
12229     }
12230     Roo.util.MixedCollection.superclass.constructor.call(this);
12231 };
12232
12233 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12234     allowFunctions : false,
12235     
12236 /**
12237  * Adds an item to the collection.
12238  * @param {String} key The key to associate with the item
12239  * @param {Object} o The item to add.
12240  * @return {Object} The item added.
12241  */
12242     add : function(key, o){
12243         if(arguments.length == 1){
12244             o = arguments[0];
12245             key = this.getKey(o);
12246         }
12247         if(typeof key == "undefined" || key === null){
12248             this.length++;
12249             this.items.push(o);
12250             this.keys.push(null);
12251         }else{
12252             var old = this.map[key];
12253             if(old){
12254                 return this.replace(key, o);
12255             }
12256             this.length++;
12257             this.items.push(o);
12258             this.map[key] = o;
12259             this.keys.push(key);
12260         }
12261         this.fireEvent("add", this.length-1, o, key);
12262         return o;
12263     },
12264        
12265 /**
12266   * MixedCollection has a generic way to fetch keys if you implement getKey.
12267 <pre><code>
12268 // normal way
12269 var mc = new Roo.util.MixedCollection();
12270 mc.add(someEl.dom.id, someEl);
12271 mc.add(otherEl.dom.id, otherEl);
12272 //and so on
12273
12274 // using getKey
12275 var mc = new Roo.util.MixedCollection();
12276 mc.getKey = function(el){
12277    return el.dom.id;
12278 };
12279 mc.add(someEl);
12280 mc.add(otherEl);
12281
12282 // or via the constructor
12283 var mc = new Roo.util.MixedCollection(false, function(el){
12284    return el.dom.id;
12285 });
12286 mc.add(someEl);
12287 mc.add(otherEl);
12288 </code></pre>
12289  * @param o {Object} The item for which to find the key.
12290  * @return {Object} The key for the passed item.
12291  */
12292     getKey : function(o){
12293          return o.id; 
12294     },
12295    
12296 /**
12297  * Replaces an item in the collection.
12298  * @param {String} key The key associated with the item to replace, or the item to replace.
12299  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12300  * @return {Object}  The new item.
12301  */
12302     replace : function(key, o){
12303         if(arguments.length == 1){
12304             o = arguments[0];
12305             key = this.getKey(o);
12306         }
12307         var old = this.item(key);
12308         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12309              return this.add(key, o);
12310         }
12311         var index = this.indexOfKey(key);
12312         this.items[index] = o;
12313         this.map[key] = o;
12314         this.fireEvent("replace", key, old, o);
12315         return o;
12316     },
12317    
12318 /**
12319  * Adds all elements of an Array or an Object to the collection.
12320  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12321  * an Array of values, each of which are added to the collection.
12322  */
12323     addAll : function(objs){
12324         if(arguments.length > 1 || objs instanceof Array){
12325             var args = arguments.length > 1 ? arguments : objs;
12326             for(var i = 0, len = args.length; i < len; i++){
12327                 this.add(args[i]);
12328             }
12329         }else{
12330             for(var key in objs){
12331                 if(this.allowFunctions || typeof objs[key] != "function"){
12332                     this.add(key, objs[key]);
12333                 }
12334             }
12335         }
12336     },
12337    
12338 /**
12339  * Executes the specified function once for every item in the collection, passing each
12340  * item as the first and only parameter. returning false from the function will stop the iteration.
12341  * @param {Function} fn The function to execute for each item.
12342  * @param {Object} scope (optional) The scope in which to execute the function.
12343  */
12344     each : function(fn, scope){
12345         var items = [].concat(this.items); // each safe for removal
12346         for(var i = 0, len = items.length; i < len; i++){
12347             if(fn.call(scope || items[i], items[i], i, len) === false){
12348                 break;
12349             }
12350         }
12351     },
12352    
12353 /**
12354  * Executes the specified function once for every key in the collection, passing each
12355  * key, and its associated item as the first two parameters.
12356  * @param {Function} fn The function to execute for each item.
12357  * @param {Object} scope (optional) The scope in which to execute the function.
12358  */
12359     eachKey : function(fn, scope){
12360         for(var i = 0, len = this.keys.length; i < len; i++){
12361             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12362         }
12363     },
12364    
12365 /**
12366  * Returns the first item in the collection which elicits a true return value from the
12367  * passed selection function.
12368  * @param {Function} fn The selection function to execute for each item.
12369  * @param {Object} scope (optional) The scope in which to execute the function.
12370  * @return {Object} The first item in the collection which returned true from the selection function.
12371  */
12372     find : function(fn, scope){
12373         for(var i = 0, len = this.items.length; i < len; i++){
12374             if(fn.call(scope || window, this.items[i], this.keys[i])){
12375                 return this.items[i];
12376             }
12377         }
12378         return null;
12379     },
12380    
12381 /**
12382  * Inserts an item at the specified index in the collection.
12383  * @param {Number} index The index to insert the item at.
12384  * @param {String} key The key to associate with the new item, or the item itself.
12385  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12386  * @return {Object} The item inserted.
12387  */
12388     insert : function(index, key, o){
12389         if(arguments.length == 2){
12390             o = arguments[1];
12391             key = this.getKey(o);
12392         }
12393         if(index >= this.length){
12394             return this.add(key, o);
12395         }
12396         this.length++;
12397         this.items.splice(index, 0, o);
12398         if(typeof key != "undefined" && key != null){
12399             this.map[key] = o;
12400         }
12401         this.keys.splice(index, 0, key);
12402         this.fireEvent("add", index, o, key);
12403         return o;
12404     },
12405    
12406 /**
12407  * Removed an item from the collection.
12408  * @param {Object} o The item to remove.
12409  * @return {Object} The item removed.
12410  */
12411     remove : function(o){
12412         return this.removeAt(this.indexOf(o));
12413     },
12414    
12415 /**
12416  * Remove an item from a specified index in the collection.
12417  * @param {Number} index The index within the collection of the item to remove.
12418  */
12419     removeAt : function(index){
12420         if(index < this.length && index >= 0){
12421             this.length--;
12422             var o = this.items[index];
12423             this.items.splice(index, 1);
12424             var key = this.keys[index];
12425             if(typeof key != "undefined"){
12426                 delete this.map[key];
12427             }
12428             this.keys.splice(index, 1);
12429             this.fireEvent("remove", o, key);
12430         }
12431     },
12432    
12433 /**
12434  * Removed an item associated with the passed key fom the collection.
12435  * @param {String} key The key of the item to remove.
12436  */
12437     removeKey : function(key){
12438         return this.removeAt(this.indexOfKey(key));
12439     },
12440    
12441 /**
12442  * Returns the number of items in the collection.
12443  * @return {Number} the number of items in the collection.
12444  */
12445     getCount : function(){
12446         return this.length; 
12447     },
12448    
12449 /**
12450  * Returns index within the collection of the passed Object.
12451  * @param {Object} o The item to find the index of.
12452  * @return {Number} index of the item.
12453  */
12454     indexOf : function(o){
12455         if(!this.items.indexOf){
12456             for(var i = 0, len = this.items.length; i < len; i++){
12457                 if(this.items[i] == o) return i;
12458             }
12459             return -1;
12460         }else{
12461             return this.items.indexOf(o);
12462         }
12463     },
12464    
12465 /**
12466  * Returns index within the collection of the passed key.
12467  * @param {String} key The key to find the index of.
12468  * @return {Number} index of the key.
12469  */
12470     indexOfKey : function(key){
12471         if(!this.keys.indexOf){
12472             for(var i = 0, len = this.keys.length; i < len; i++){
12473                 if(this.keys[i] == key) return i;
12474             }
12475             return -1;
12476         }else{
12477             return this.keys.indexOf(key);
12478         }
12479     },
12480    
12481 /**
12482  * Returns the item associated with the passed key OR index. Key has priority over index.
12483  * @param {String/Number} key The key or index of the item.
12484  * @return {Object} The item associated with the passed key.
12485  */
12486     item : function(key){
12487         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12488         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12489     },
12490     
12491 /**
12492  * Returns the item at the specified index.
12493  * @param {Number} index The index of the item.
12494  * @return {Object}
12495  */
12496     itemAt : function(index){
12497         return this.items[index];
12498     },
12499     
12500 /**
12501  * Returns the item associated with the passed key.
12502  * @param {String/Number} key The key of the item.
12503  * @return {Object} The item associated with the passed key.
12504  */
12505     key : function(key){
12506         return this.map[key];
12507     },
12508    
12509 /**
12510  * Returns true if the collection contains the passed Object as an item.
12511  * @param {Object} o  The Object to look for in the collection.
12512  * @return {Boolean} True if the collection contains the Object as an item.
12513  */
12514     contains : function(o){
12515         return this.indexOf(o) != -1;
12516     },
12517    
12518 /**
12519  * Returns true if the collection contains the passed Object as a key.
12520  * @param {String} key The key to look for in the collection.
12521  * @return {Boolean} True if the collection contains the Object as a key.
12522  */
12523     containsKey : function(key){
12524         return typeof this.map[key] != "undefined";
12525     },
12526    
12527 /**
12528  * Removes all items from the collection.
12529  */
12530     clear : function(){
12531         this.length = 0;
12532         this.items = [];
12533         this.keys = [];
12534         this.map = {};
12535         this.fireEvent("clear");
12536     },
12537    
12538 /**
12539  * Returns the first item in the collection.
12540  * @return {Object} the first item in the collection..
12541  */
12542     first : function(){
12543         return this.items[0]; 
12544     },
12545    
12546 /**
12547  * Returns the last item in the collection.
12548  * @return {Object} the last item in the collection..
12549  */
12550     last : function(){
12551         return this.items[this.length-1];   
12552     },
12553     
12554     _sort : function(property, dir, fn){
12555         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12556         fn = fn || function(a, b){
12557             return a-b;
12558         };
12559         var c = [], k = this.keys, items = this.items;
12560         for(var i = 0, len = items.length; i < len; i++){
12561             c[c.length] = {key: k[i], value: items[i], index: i};
12562         }
12563         c.sort(function(a, b){
12564             var v = fn(a[property], b[property]) * dsc;
12565             if(v == 0){
12566                 v = (a.index < b.index ? -1 : 1);
12567             }
12568             return v;
12569         });
12570         for(var i = 0, len = c.length; i < len; i++){
12571             items[i] = c[i].value;
12572             k[i] = c[i].key;
12573         }
12574         this.fireEvent("sort", this);
12575     },
12576     
12577     /**
12578      * Sorts this collection with the passed comparison function
12579      * @param {String} direction (optional) "ASC" or "DESC"
12580      * @param {Function} fn (optional) comparison function
12581      */
12582     sort : function(dir, fn){
12583         this._sort("value", dir, fn);
12584     },
12585     
12586     /**
12587      * Sorts this collection by keys
12588      * @param {String} direction (optional) "ASC" or "DESC"
12589      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12590      */
12591     keySort : function(dir, fn){
12592         this._sort("key", dir, fn || function(a, b){
12593             return String(a).toUpperCase()-String(b).toUpperCase();
12594         });
12595     },
12596     
12597     /**
12598      * Returns a range of items in this collection
12599      * @param {Number} startIndex (optional) defaults to 0
12600      * @param {Number} endIndex (optional) default to the last item
12601      * @return {Array} An array of items
12602      */
12603     getRange : function(start, end){
12604         var items = this.items;
12605         if(items.length < 1){
12606             return [];
12607         }
12608         start = start || 0;
12609         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12610         var r = [];
12611         if(start <= end){
12612             for(var i = start; i <= end; i++) {
12613                     r[r.length] = items[i];
12614             }
12615         }else{
12616             for(var i = start; i >= end; i--) {
12617                     r[r.length] = items[i];
12618             }
12619         }
12620         return r;
12621     },
12622         
12623     /**
12624      * Filter the <i>objects</i> in this collection by a specific property. 
12625      * Returns a new collection that has been filtered.
12626      * @param {String} property A property on your objects
12627      * @param {String/RegExp} value Either string that the property values 
12628      * should start with or a RegExp to test against the property
12629      * @return {MixedCollection} The new filtered collection
12630      */
12631     filter : function(property, value){
12632         if(!value.exec){ // not a regex
12633             value = String(value);
12634             if(value.length == 0){
12635                 return this.clone();
12636             }
12637             value = new RegExp("^" + Roo.escapeRe(value), "i");
12638         }
12639         return this.filterBy(function(o){
12640             return o && value.test(o[property]);
12641         });
12642         },
12643     
12644     /**
12645      * Filter by a function. * Returns a new collection that has been filtered.
12646      * The passed function will be called with each 
12647      * object in the collection. If the function returns true, the value is included 
12648      * otherwise it is filtered.
12649      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12650      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12651      * @return {MixedCollection} The new filtered collection
12652      */
12653     filterBy : function(fn, scope){
12654         var r = new Roo.util.MixedCollection();
12655         r.getKey = this.getKey;
12656         var k = this.keys, it = this.items;
12657         for(var i = 0, len = it.length; i < len; i++){
12658             if(fn.call(scope||this, it[i], k[i])){
12659                                 r.add(k[i], it[i]);
12660                         }
12661         }
12662         return r;
12663     },
12664     
12665     /**
12666      * Creates a duplicate of this collection
12667      * @return {MixedCollection}
12668      */
12669     clone : function(){
12670         var r = new Roo.util.MixedCollection();
12671         var k = this.keys, it = this.items;
12672         for(var i = 0, len = it.length; i < len; i++){
12673             r.add(k[i], it[i]);
12674         }
12675         r.getKey = this.getKey;
12676         return r;
12677     }
12678 });
12679 /**
12680  * Returns the item associated with the passed key or index.
12681  * @method
12682  * @param {String/Number} key The key or index of the item.
12683  * @return {Object} The item associated with the passed key.
12684  */
12685 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12686  * Based on:
12687  * Ext JS Library 1.1.1
12688  * Copyright(c) 2006-2007, Ext JS, LLC.
12689  *
12690  * Originally Released Under LGPL - original licence link has changed is not relivant.
12691  *
12692  * Fork - LGPL
12693  * <script type="text/javascript">
12694  */
12695 /**
12696  * @class Roo.util.JSON
12697  * Modified version of Douglas Crockford"s json.js that doesn"t
12698  * mess with the Object prototype 
12699  * http://www.json.org/js.html
12700  * @singleton
12701  */
12702 Roo.util.JSON = new (function(){
12703     var useHasOwn = {}.hasOwnProperty ? true : false;
12704     
12705     // crashes Safari in some instances
12706     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12707     
12708     var pad = function(n) {
12709         return n < 10 ? "0" + n : n;
12710     };
12711     
12712     var m = {
12713         "\b": '\\b',
12714         "\t": '\\t',
12715         "\n": '\\n',
12716         "\f": '\\f',
12717         "\r": '\\r',
12718         '"' : '\\"',
12719         "\\": '\\\\'
12720     };
12721
12722     var encodeString = function(s){
12723         if (/["\\\x00-\x1f]/.test(s)) {
12724             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12725                 var c = m[b];
12726                 if(c){
12727                     return c;
12728                 }
12729                 c = b.charCodeAt();
12730                 return "\\u00" +
12731                     Math.floor(c / 16).toString(16) +
12732                     (c % 16).toString(16);
12733             }) + '"';
12734         }
12735         return '"' + s + '"';
12736     };
12737     
12738     var encodeArray = function(o){
12739         var a = ["["], b, i, l = o.length, v;
12740             for (i = 0; i < l; i += 1) {
12741                 v = o[i];
12742                 switch (typeof v) {
12743                     case "undefined":
12744                     case "function":
12745                     case "unknown":
12746                         break;
12747                     default:
12748                         if (b) {
12749                             a.push(',');
12750                         }
12751                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12752                         b = true;
12753                 }
12754             }
12755             a.push("]");
12756             return a.join("");
12757     };
12758     
12759     var encodeDate = function(o){
12760         return '"' + o.getFullYear() + "-" +
12761                 pad(o.getMonth() + 1) + "-" +
12762                 pad(o.getDate()) + "T" +
12763                 pad(o.getHours()) + ":" +
12764                 pad(o.getMinutes()) + ":" +
12765                 pad(o.getSeconds()) + '"';
12766     };
12767     
12768     /**
12769      * Encodes an Object, Array or other value
12770      * @param {Mixed} o The variable to encode
12771      * @return {String} The JSON string
12772      */
12773     this.encode = function(o)
12774     {
12775         // should this be extended to fully wrap stringify..
12776         
12777         if(typeof o == "undefined" || o === null){
12778             return "null";
12779         }else if(o instanceof Array){
12780             return encodeArray(o);
12781         }else if(o instanceof Date){
12782             return encodeDate(o);
12783         }else if(typeof o == "string"){
12784             return encodeString(o);
12785         }else if(typeof o == "number"){
12786             return isFinite(o) ? String(o) : "null";
12787         }else if(typeof o == "boolean"){
12788             return String(o);
12789         }else {
12790             var a = ["{"], b, i, v;
12791             for (i in o) {
12792                 if(!useHasOwn || o.hasOwnProperty(i)) {
12793                     v = o[i];
12794                     switch (typeof v) {
12795                     case "undefined":
12796                     case "function":
12797                     case "unknown":
12798                         break;
12799                     default:
12800                         if(b){
12801                             a.push(',');
12802                         }
12803                         a.push(this.encode(i), ":",
12804                                 v === null ? "null" : this.encode(v));
12805                         b = true;
12806                     }
12807                 }
12808             }
12809             a.push("}");
12810             return a.join("");
12811         }
12812     };
12813     
12814     /**
12815      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12816      * @param {String} json The JSON string
12817      * @return {Object} The resulting object
12818      */
12819     this.decode = function(json){
12820         
12821         return  /** eval:var:json */ eval("(" + json + ')');
12822     };
12823 })();
12824 /** 
12825  * Shorthand for {@link Roo.util.JSON#encode}
12826  * @member Roo encode 
12827  * @method */
12828 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12829 /** 
12830  * Shorthand for {@link Roo.util.JSON#decode}
12831  * @member Roo decode 
12832  * @method */
12833 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12834 /*
12835  * Based on:
12836  * Ext JS Library 1.1.1
12837  * Copyright(c) 2006-2007, Ext JS, LLC.
12838  *
12839  * Originally Released Under LGPL - original licence link has changed is not relivant.
12840  *
12841  * Fork - LGPL
12842  * <script type="text/javascript">
12843  */
12844  
12845 /**
12846  * @class Roo.util.Format
12847  * Reusable data formatting functions
12848  * @singleton
12849  */
12850 Roo.util.Format = function(){
12851     var trimRe = /^\s+|\s+$/g;
12852     return {
12853         /**
12854          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12855          * @param {String} value The string to truncate
12856          * @param {Number} length The maximum length to allow before truncating
12857          * @return {String} The converted text
12858          */
12859         ellipsis : function(value, len){
12860             if(value && value.length > len){
12861                 return value.substr(0, len-3)+"...";
12862             }
12863             return value;
12864         },
12865
12866         /**
12867          * Checks a reference and converts it to empty string if it is undefined
12868          * @param {Mixed} value Reference to check
12869          * @return {Mixed} Empty string if converted, otherwise the original value
12870          */
12871         undef : function(value){
12872             return typeof value != "undefined" ? value : "";
12873         },
12874
12875         /**
12876          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12877          * @param {String} value The string to encode
12878          * @return {String} The encoded text
12879          */
12880         htmlEncode : function(value){
12881             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12882         },
12883
12884         /**
12885          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12886          * @param {String} value The string to decode
12887          * @return {String} The decoded text
12888          */
12889         htmlDecode : function(value){
12890             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12891         },
12892
12893         /**
12894          * Trims any whitespace from either side of a string
12895          * @param {String} value The text to trim
12896          * @return {String} The trimmed text
12897          */
12898         trim : function(value){
12899             return String(value).replace(trimRe, "");
12900         },
12901
12902         /**
12903          * Returns a substring from within an original string
12904          * @param {String} value The original text
12905          * @param {Number} start The start index of the substring
12906          * @param {Number} length The length of the substring
12907          * @return {String} The substring
12908          */
12909         substr : function(value, start, length){
12910             return String(value).substr(start, length);
12911         },
12912
12913         /**
12914          * Converts a string to all lower case letters
12915          * @param {String} value The text to convert
12916          * @return {String} The converted text
12917          */
12918         lowercase : function(value){
12919             return String(value).toLowerCase();
12920         },
12921
12922         /**
12923          * Converts a string to all upper case letters
12924          * @param {String} value The text to convert
12925          * @return {String} The converted text
12926          */
12927         uppercase : function(value){
12928             return String(value).toUpperCase();
12929         },
12930
12931         /**
12932          * Converts the first character only of a string to upper case
12933          * @param {String} value The text to convert
12934          * @return {String} The converted text
12935          */
12936         capitalize : function(value){
12937             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12938         },
12939
12940         // private
12941         call : function(value, fn){
12942             if(arguments.length > 2){
12943                 var args = Array.prototype.slice.call(arguments, 2);
12944                 args.unshift(value);
12945                  
12946                 return /** eval:var:value */  eval(fn).apply(window, args);
12947             }else{
12948                 /** eval:var:value */
12949                 return /** eval:var:value */ eval(fn).call(window, value);
12950             }
12951         },
12952
12953        
12954         /**
12955          * safer version of Math.toFixed..??/
12956          * @param {Number/String} value The numeric value to format
12957          * @param {Number/String} value Decimal places 
12958          * @return {String} The formatted currency string
12959          */
12960         toFixed : function(v, n)
12961         {
12962             // why not use to fixed - precision is buggered???
12963             if (!n) {
12964                 return Math.round(v-0);
12965             }
12966             var fact = Math.pow(10,n+1);
12967             v = (Math.round((v-0)*fact))/fact;
12968             var z = (''+fact).substring(2);
12969             if (v == Math.floor(v)) {
12970                 return Math.floor(v) + '.' + z;
12971             }
12972             
12973             // now just padd decimals..
12974             var ps = String(v).split('.');
12975             var fd = (ps[1] + z);
12976             var r = fd.substring(0,n); 
12977             var rm = fd.substring(n); 
12978             if (rm < 5) {
12979                 return ps[0] + '.' + r;
12980             }
12981             r*=1; // turn it into a number;
12982             r++;
12983             if (String(r).length != n) {
12984                 ps[0]*=1;
12985                 ps[0]++;
12986                 r = String(r).substring(1); // chop the end off.
12987             }
12988             
12989             return ps[0] + '.' + r;
12990              
12991         },
12992         
12993         /**
12994          * Format a number as US currency
12995          * @param {Number/String} value The numeric value to format
12996          * @return {String} The formatted currency string
12997          */
12998         usMoney : function(v){
12999             v = (Math.round((v-0)*100))/100;
13000             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13001             v = String(v);
13002             var ps = v.split('.');
13003             var whole = ps[0];
13004             var sub = ps[1] ? '.'+ ps[1] : '.00';
13005             var r = /(\d+)(\d{3})/;
13006             while (r.test(whole)) {
13007                 whole = whole.replace(r, '$1' + ',' + '$2');
13008             }
13009             return "$" + whole + sub ;
13010         },
13011         
13012         /**
13013          * Parse a value into a formatted date using the specified format pattern.
13014          * @param {Mixed} value The value to format
13015          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13016          * @return {String} The formatted date string
13017          */
13018         date : function(v, format){
13019             if(!v){
13020                 return "";
13021             }
13022             if(!(v instanceof Date)){
13023                 v = new Date(Date.parse(v));
13024             }
13025             return v.dateFormat(format || "m/d/Y");
13026         },
13027
13028         /**
13029          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13030          * @param {String} format Any valid date format string
13031          * @return {Function} The date formatting function
13032          */
13033         dateRenderer : function(format){
13034             return function(v){
13035                 return Roo.util.Format.date(v, format);  
13036             };
13037         },
13038
13039         // private
13040         stripTagsRE : /<\/?[^>]+>/gi,
13041         
13042         /**
13043          * Strips all HTML tags
13044          * @param {Mixed} value The text from which to strip tags
13045          * @return {String} The stripped text
13046          */
13047         stripTags : function(v){
13048             return !v ? v : String(v).replace(this.stripTagsRE, "");
13049         }
13050     };
13051 }();/*
13052  * Based on:
13053  * Ext JS Library 1.1.1
13054  * Copyright(c) 2006-2007, Ext JS, LLC.
13055  *
13056  * Originally Released Under LGPL - original licence link has changed is not relivant.
13057  *
13058  * Fork - LGPL
13059  * <script type="text/javascript">
13060  */
13061
13062
13063  
13064
13065 /**
13066  * @class Roo.MasterTemplate
13067  * @extends Roo.Template
13068  * Provides a template that can have child templates. The syntax is:
13069 <pre><code>
13070 var t = new Roo.MasterTemplate(
13071         '&lt;select name="{name}"&gt;',
13072                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13073         '&lt;/select&gt;'
13074 );
13075 t.add('options', {value: 'foo', text: 'bar'});
13076 // or you can add multiple child elements in one shot
13077 t.addAll('options', [
13078     {value: 'foo', text: 'bar'},
13079     {value: 'foo2', text: 'bar2'},
13080     {value: 'foo3', text: 'bar3'}
13081 ]);
13082 // then append, applying the master template values
13083 t.append('my-form', {name: 'my-select'});
13084 </code></pre>
13085 * A name attribute for the child template is not required if you have only one child
13086 * template or you want to refer to them by index.
13087  */
13088 Roo.MasterTemplate = function(){
13089     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13090     this.originalHtml = this.html;
13091     var st = {};
13092     var m, re = this.subTemplateRe;
13093     re.lastIndex = 0;
13094     var subIndex = 0;
13095     while(m = re.exec(this.html)){
13096         var name = m[1], content = m[2];
13097         st[subIndex] = {
13098             name: name,
13099             index: subIndex,
13100             buffer: [],
13101             tpl : new Roo.Template(content)
13102         };
13103         if(name){
13104             st[name] = st[subIndex];
13105         }
13106         st[subIndex].tpl.compile();
13107         st[subIndex].tpl.call = this.call.createDelegate(this);
13108         subIndex++;
13109     }
13110     this.subCount = subIndex;
13111     this.subs = st;
13112 };
13113 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13114     /**
13115     * The regular expression used to match sub templates
13116     * @type RegExp
13117     * @property
13118     */
13119     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13120
13121     /**
13122      * Applies the passed values to a child template.
13123      * @param {String/Number} name (optional) The name or index of the child template
13124      * @param {Array/Object} values The values to be applied to the template
13125      * @return {MasterTemplate} this
13126      */
13127      add : function(name, values){
13128         if(arguments.length == 1){
13129             values = arguments[0];
13130             name = 0;
13131         }
13132         var s = this.subs[name];
13133         s.buffer[s.buffer.length] = s.tpl.apply(values);
13134         return this;
13135     },
13136
13137     /**
13138      * Applies all the passed values to a child template.
13139      * @param {String/Number} name (optional) The name or index of the child template
13140      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13141      * @param {Boolean} reset (optional) True to reset the template first
13142      * @return {MasterTemplate} this
13143      */
13144     fill : function(name, values, reset){
13145         var a = arguments;
13146         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13147             values = a[0];
13148             name = 0;
13149             reset = a[1];
13150         }
13151         if(reset){
13152             this.reset();
13153         }
13154         for(var i = 0, len = values.length; i < len; i++){
13155             this.add(name, values[i]);
13156         }
13157         return this;
13158     },
13159
13160     /**
13161      * Resets the template for reuse
13162      * @return {MasterTemplate} this
13163      */
13164      reset : function(){
13165         var s = this.subs;
13166         for(var i = 0; i < this.subCount; i++){
13167             s[i].buffer = [];
13168         }
13169         return this;
13170     },
13171
13172     applyTemplate : function(values){
13173         var s = this.subs;
13174         var replaceIndex = -1;
13175         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13176             return s[++replaceIndex].buffer.join("");
13177         });
13178         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13179     },
13180
13181     apply : function(){
13182         return this.applyTemplate.apply(this, arguments);
13183     },
13184
13185     compile : function(){return this;}
13186 });
13187
13188 /**
13189  * Alias for fill().
13190  * @method
13191  */
13192 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13193  /**
13194  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13195  * var tpl = Roo.MasterTemplate.from('element-id');
13196  * @param {String/HTMLElement} el
13197  * @param {Object} config
13198  * @static
13199  */
13200 Roo.MasterTemplate.from = function(el, config){
13201     el = Roo.getDom(el);
13202     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13203 };/*
13204  * Based on:
13205  * Ext JS Library 1.1.1
13206  * Copyright(c) 2006-2007, Ext JS, LLC.
13207  *
13208  * Originally Released Under LGPL - original licence link has changed is not relivant.
13209  *
13210  * Fork - LGPL
13211  * <script type="text/javascript">
13212  */
13213
13214  
13215 /**
13216  * @class Roo.util.CSS
13217  * Utility class for manipulating CSS rules
13218  * @singleton
13219  */
13220 Roo.util.CSS = function(){
13221         var rules = null;
13222         var doc = document;
13223
13224     var camelRe = /(-[a-z])/gi;
13225     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13226
13227    return {
13228    /**
13229     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13230     * tag and appended to the HEAD of the document.
13231     * @param {String|Object} cssText The text containing the css rules
13232     * @param {String} id An id to add to the stylesheet for later removal
13233     * @return {StyleSheet}
13234     */
13235     createStyleSheet : function(cssText, id){
13236         var ss;
13237         var head = doc.getElementsByTagName("head")[0];
13238         var nrules = doc.createElement("style");
13239         nrules.setAttribute("type", "text/css");
13240         if(id){
13241             nrules.setAttribute("id", id);
13242         }
13243         if (typeof(cssText) != 'string') {
13244             // support object maps..
13245             // not sure if this a good idea.. 
13246             // perhaps it should be merged with the general css handling
13247             // and handle js style props.
13248             var cssTextNew = [];
13249             for(var n in cssText) {
13250                 var citems = [];
13251                 for(var k in cssText[n]) {
13252                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13253                 }
13254                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13255                 
13256             }
13257             cssText = cssTextNew.join("\n");
13258             
13259         }
13260        
13261        
13262        if(Roo.isIE){
13263            head.appendChild(nrules);
13264            ss = nrules.styleSheet;
13265            ss.cssText = cssText;
13266        }else{
13267            try{
13268                 nrules.appendChild(doc.createTextNode(cssText));
13269            }catch(e){
13270                nrules.cssText = cssText; 
13271            }
13272            head.appendChild(nrules);
13273            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13274        }
13275        this.cacheStyleSheet(ss);
13276        return ss;
13277    },
13278
13279    /**
13280     * Removes a style or link tag by id
13281     * @param {String} id The id of the tag
13282     */
13283    removeStyleSheet : function(id){
13284        var existing = doc.getElementById(id);
13285        if(existing){
13286            existing.parentNode.removeChild(existing);
13287        }
13288    },
13289
13290    /**
13291     * Dynamically swaps an existing stylesheet reference for a new one
13292     * @param {String} id The id of an existing link tag to remove
13293     * @param {String} url The href of the new stylesheet to include
13294     */
13295    swapStyleSheet : function(id, url){
13296        this.removeStyleSheet(id);
13297        var ss = doc.createElement("link");
13298        ss.setAttribute("rel", "stylesheet");
13299        ss.setAttribute("type", "text/css");
13300        ss.setAttribute("id", id);
13301        ss.setAttribute("href", url);
13302        doc.getElementsByTagName("head")[0].appendChild(ss);
13303    },
13304    
13305    /**
13306     * Refresh the rule cache if you have dynamically added stylesheets
13307     * @return {Object} An object (hash) of rules indexed by selector
13308     */
13309    refreshCache : function(){
13310        return this.getRules(true);
13311    },
13312
13313    // private
13314    cacheStyleSheet : function(stylesheet){
13315        if(!rules){
13316            rules = {};
13317        }
13318        try{// try catch for cross domain access issue
13319            var ssRules = stylesheet.cssRules || stylesheet.rules;
13320            for(var j = ssRules.length-1; j >= 0; --j){
13321                rules[ssRules[j].selectorText] = ssRules[j];
13322            }
13323        }catch(e){}
13324    },
13325    
13326    /**
13327     * Gets all css rules for the document
13328     * @param {Boolean} refreshCache true to refresh the internal cache
13329     * @return {Object} An object (hash) of rules indexed by selector
13330     */
13331    getRules : function(refreshCache){
13332                 if(rules == null || refreshCache){
13333                         rules = {};
13334                         var ds = doc.styleSheets;
13335                         for(var i =0, len = ds.length; i < len; i++){
13336                             try{
13337                         this.cacheStyleSheet(ds[i]);
13338                     }catch(e){} 
13339                 }
13340                 }
13341                 return rules;
13342         },
13343         
13344         /**
13345     * Gets an an individual CSS rule by selector(s)
13346     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13347     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13348     * @return {CSSRule} The CSS rule or null if one is not found
13349     */
13350    getRule : function(selector, refreshCache){
13351                 var rs = this.getRules(refreshCache);
13352                 if(!(selector instanceof Array)){
13353                     return rs[selector];
13354                 }
13355                 for(var i = 0; i < selector.length; i++){
13356                         if(rs[selector[i]]){
13357                                 return rs[selector[i]];
13358                         }
13359                 }
13360                 return null;
13361         },
13362         
13363         
13364         /**
13365     * Updates a rule property
13366     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13367     * @param {String} property The css property
13368     * @param {String} value The new value for the property
13369     * @return {Boolean} true If a rule was found and updated
13370     */
13371    updateRule : function(selector, property, value){
13372                 if(!(selector instanceof Array)){
13373                         var rule = this.getRule(selector);
13374                         if(rule){
13375                                 rule.style[property.replace(camelRe, camelFn)] = value;
13376                                 return true;
13377                         }
13378                 }else{
13379                         for(var i = 0; i < selector.length; i++){
13380                                 if(this.updateRule(selector[i], property, value)){
13381                                         return true;
13382                                 }
13383                         }
13384                 }
13385                 return false;
13386         }
13387    };   
13388 }();/*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398
13399  
13400
13401 /**
13402  * @class Roo.util.ClickRepeater
13403  * @extends Roo.util.Observable
13404  * 
13405  * A wrapper class which can be applied to any element. Fires a "click" event while the
13406  * mouse is pressed. The interval between firings may be specified in the config but
13407  * defaults to 10 milliseconds.
13408  * 
13409  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13410  * 
13411  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13412  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13413  * Similar to an autorepeat key delay.
13414  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13415  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13416  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13417  *           "interval" and "delay" are ignored. "immediate" is honored.
13418  * @cfg {Boolean} preventDefault True to prevent the default click event
13419  * @cfg {Boolean} stopDefault True to stop the default click event
13420  * 
13421  * @history
13422  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13423  *     2007-02-02 jvs Renamed to ClickRepeater
13424  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13425  *
13426  *  @constructor
13427  * @param {String/HTMLElement/Element} el The element to listen on
13428  * @param {Object} config
13429  **/
13430 Roo.util.ClickRepeater = function(el, config)
13431 {
13432     this.el = Roo.get(el);
13433     this.el.unselectable();
13434
13435     Roo.apply(this, config);
13436
13437     this.addEvents({
13438     /**
13439      * @event mousedown
13440      * Fires when the mouse button is depressed.
13441      * @param {Roo.util.ClickRepeater} this
13442      */
13443         "mousedown" : true,
13444     /**
13445      * @event click
13446      * Fires on a specified interval during the time the element is pressed.
13447      * @param {Roo.util.ClickRepeater} this
13448      */
13449         "click" : true,
13450     /**
13451      * @event mouseup
13452      * Fires when the mouse key is released.
13453      * @param {Roo.util.ClickRepeater} this
13454      */
13455         "mouseup" : true
13456     });
13457
13458     this.el.on("mousedown", this.handleMouseDown, this);
13459     if(this.preventDefault || this.stopDefault){
13460         this.el.on("click", function(e){
13461             if(this.preventDefault){
13462                 e.preventDefault();
13463             }
13464             if(this.stopDefault){
13465                 e.stopEvent();
13466             }
13467         }, this);
13468     }
13469
13470     // allow inline handler
13471     if(this.handler){
13472         this.on("click", this.handler,  this.scope || this);
13473     }
13474
13475     Roo.util.ClickRepeater.superclass.constructor.call(this);
13476 };
13477
13478 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13479     interval : 20,
13480     delay: 250,
13481     preventDefault : true,
13482     stopDefault : false,
13483     timer : 0,
13484
13485     // private
13486     handleMouseDown : function(){
13487         clearTimeout(this.timer);
13488         this.el.blur();
13489         if(this.pressClass){
13490             this.el.addClass(this.pressClass);
13491         }
13492         this.mousedownTime = new Date();
13493
13494         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13495         this.el.on("mouseout", this.handleMouseOut, this);
13496
13497         this.fireEvent("mousedown", this);
13498         this.fireEvent("click", this);
13499         
13500         this.timer = this.click.defer(this.delay || this.interval, this);
13501     },
13502
13503     // private
13504     click : function(){
13505         this.fireEvent("click", this);
13506         this.timer = this.click.defer(this.getInterval(), this);
13507     },
13508
13509     // private
13510     getInterval: function(){
13511         if(!this.accelerate){
13512             return this.interval;
13513         }
13514         var pressTime = this.mousedownTime.getElapsed();
13515         if(pressTime < 500){
13516             return 400;
13517         }else if(pressTime < 1700){
13518             return 320;
13519         }else if(pressTime < 2600){
13520             return 250;
13521         }else if(pressTime < 3500){
13522             return 180;
13523         }else if(pressTime < 4400){
13524             return 140;
13525         }else if(pressTime < 5300){
13526             return 80;
13527         }else if(pressTime < 6200){
13528             return 50;
13529         }else{
13530             return 10;
13531         }
13532     },
13533
13534     // private
13535     handleMouseOut : function(){
13536         clearTimeout(this.timer);
13537         if(this.pressClass){
13538             this.el.removeClass(this.pressClass);
13539         }
13540         this.el.on("mouseover", this.handleMouseReturn, this);
13541     },
13542
13543     // private
13544     handleMouseReturn : function(){
13545         this.el.un("mouseover", this.handleMouseReturn);
13546         if(this.pressClass){
13547             this.el.addClass(this.pressClass);
13548         }
13549         this.click();
13550     },
13551
13552     // private
13553     handleMouseUp : function(){
13554         clearTimeout(this.timer);
13555         this.el.un("mouseover", this.handleMouseReturn);
13556         this.el.un("mouseout", this.handleMouseOut);
13557         Roo.get(document).un("mouseup", this.handleMouseUp);
13558         this.el.removeClass(this.pressClass);
13559         this.fireEvent("mouseup", this);
13560     }
13561 });/*
13562  * Based on:
13563  * Ext JS Library 1.1.1
13564  * Copyright(c) 2006-2007, Ext JS, LLC.
13565  *
13566  * Originally Released Under LGPL - original licence link has changed is not relivant.
13567  *
13568  * Fork - LGPL
13569  * <script type="text/javascript">
13570  */
13571
13572  
13573 /**
13574  * @class Roo.KeyNav
13575  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13576  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13577  * way to implement custom navigation schemes for any UI component.</p>
13578  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13579  * pageUp, pageDown, del, home, end.  Usage:</p>
13580  <pre><code>
13581 var nav = new Roo.KeyNav("my-element", {
13582     "left" : function(e){
13583         this.moveLeft(e.ctrlKey);
13584     },
13585     "right" : function(e){
13586         this.moveRight(e.ctrlKey);
13587     },
13588     "enter" : function(e){
13589         this.save();
13590     },
13591     scope : this
13592 });
13593 </code></pre>
13594  * @constructor
13595  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13596  * @param {Object} config The config
13597  */
13598 Roo.KeyNav = function(el, config){
13599     this.el = Roo.get(el);
13600     Roo.apply(this, config);
13601     if(!this.disabled){
13602         this.disabled = true;
13603         this.enable();
13604     }
13605 };
13606
13607 Roo.KeyNav.prototype = {
13608     /**
13609      * @cfg {Boolean} disabled
13610      * True to disable this KeyNav instance (defaults to false)
13611      */
13612     disabled : false,
13613     /**
13614      * @cfg {String} defaultEventAction
13615      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13616      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13617      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13618      */
13619     defaultEventAction: "stopEvent",
13620     /**
13621      * @cfg {Boolean} forceKeyDown
13622      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13623      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13624      * handle keydown instead of keypress.
13625      */
13626     forceKeyDown : false,
13627
13628     // private
13629     prepareEvent : function(e){
13630         var k = e.getKey();
13631         var h = this.keyToHandler[k];
13632         //if(h && this[h]){
13633         //    e.stopPropagation();
13634         //}
13635         if(Roo.isSafari && h && k >= 37 && k <= 40){
13636             e.stopEvent();
13637         }
13638     },
13639
13640     // private
13641     relay : function(e){
13642         var k = e.getKey();
13643         var h = this.keyToHandler[k];
13644         if(h && this[h]){
13645             if(this.doRelay(e, this[h], h) !== true){
13646                 e[this.defaultEventAction]();
13647             }
13648         }
13649     },
13650
13651     // private
13652     doRelay : function(e, h, hname){
13653         return h.call(this.scope || this, e);
13654     },
13655
13656     // possible handlers
13657     enter : false,
13658     left : false,
13659     right : false,
13660     up : false,
13661     down : false,
13662     tab : false,
13663     esc : false,
13664     pageUp : false,
13665     pageDown : false,
13666     del : false,
13667     home : false,
13668     end : false,
13669
13670     // quick lookup hash
13671     keyToHandler : {
13672         37 : "left",
13673         39 : "right",
13674         38 : "up",
13675         40 : "down",
13676         33 : "pageUp",
13677         34 : "pageDown",
13678         46 : "del",
13679         36 : "home",
13680         35 : "end",
13681         13 : "enter",
13682         27 : "esc",
13683         9  : "tab"
13684     },
13685
13686         /**
13687          * Enable this KeyNav
13688          */
13689         enable: function(){
13690                 if(this.disabled){
13691             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13692             // the EventObject will normalize Safari automatically
13693             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13694                 this.el.on("keydown", this.relay,  this);
13695             }else{
13696                 this.el.on("keydown", this.prepareEvent,  this);
13697                 this.el.on("keypress", this.relay,  this);
13698             }
13699                     this.disabled = false;
13700                 }
13701         },
13702
13703         /**
13704          * Disable this KeyNav
13705          */
13706         disable: function(){
13707                 if(!this.disabled){
13708                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13709                 this.el.un("keydown", this.relay);
13710             }else{
13711                 this.el.un("keydown", this.prepareEvent);
13712                 this.el.un("keypress", this.relay);
13713             }
13714                     this.disabled = true;
13715                 }
13716         }
13717 };/*
13718  * Based on:
13719  * Ext JS Library 1.1.1
13720  * Copyright(c) 2006-2007, Ext JS, LLC.
13721  *
13722  * Originally Released Under LGPL - original licence link has changed is not relivant.
13723  *
13724  * Fork - LGPL
13725  * <script type="text/javascript">
13726  */
13727
13728  
13729 /**
13730  * @class Roo.KeyMap
13731  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13732  * The constructor accepts the same config object as defined by {@link #addBinding}.
13733  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13734  * combination it will call the function with this signature (if the match is a multi-key
13735  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13736  * A KeyMap can also handle a string representation of keys.<br />
13737  * Usage:
13738  <pre><code>
13739 // map one key by key code
13740 var map = new Roo.KeyMap("my-element", {
13741     key: 13, // or Roo.EventObject.ENTER
13742     fn: myHandler,
13743     scope: myObject
13744 });
13745
13746 // map multiple keys to one action by string
13747 var map = new Roo.KeyMap("my-element", {
13748     key: "a\r\n\t",
13749     fn: myHandler,
13750     scope: myObject
13751 });
13752
13753 // map multiple keys to multiple actions by strings and array of codes
13754 var map = new Roo.KeyMap("my-element", [
13755     {
13756         key: [10,13],
13757         fn: function(){ alert("Return was pressed"); }
13758     }, {
13759         key: "abc",
13760         fn: function(){ alert('a, b or c was pressed'); }
13761     }, {
13762         key: "\t",
13763         ctrl:true,
13764         shift:true,
13765         fn: function(){ alert('Control + shift + tab was pressed.'); }
13766     }
13767 ]);
13768 </code></pre>
13769  * <b>Note: A KeyMap starts enabled</b>
13770  * @constructor
13771  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13772  * @param {Object} config The config (see {@link #addBinding})
13773  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13774  */
13775 Roo.KeyMap = function(el, config, eventName){
13776     this.el  = Roo.get(el);
13777     this.eventName = eventName || "keydown";
13778     this.bindings = [];
13779     if(config){
13780         this.addBinding(config);
13781     }
13782     this.enable();
13783 };
13784
13785 Roo.KeyMap.prototype = {
13786     /**
13787      * True to stop the event from bubbling and prevent the default browser action if the
13788      * key was handled by the KeyMap (defaults to false)
13789      * @type Boolean
13790      */
13791     stopEvent : false,
13792
13793     /**
13794      * Add a new binding to this KeyMap. The following config object properties are supported:
13795      * <pre>
13796 Property    Type             Description
13797 ----------  ---------------  ----------------------------------------------------------------------
13798 key         String/Array     A single keycode or an array of keycodes to handle
13799 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13800 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13801 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13802 fn          Function         The function to call when KeyMap finds the expected key combination
13803 scope       Object           The scope of the callback function
13804 </pre>
13805      *
13806      * Usage:
13807      * <pre><code>
13808 // Create a KeyMap
13809 var map = new Roo.KeyMap(document, {
13810     key: Roo.EventObject.ENTER,
13811     fn: handleKey,
13812     scope: this
13813 });
13814
13815 //Add a new binding to the existing KeyMap later
13816 map.addBinding({
13817     key: 'abc',
13818     shift: true,
13819     fn: handleKey,
13820     scope: this
13821 });
13822 </code></pre>
13823      * @param {Object/Array} config A single KeyMap config or an array of configs
13824      */
13825         addBinding : function(config){
13826         if(config instanceof Array){
13827             for(var i = 0, len = config.length; i < len; i++){
13828                 this.addBinding(config[i]);
13829             }
13830             return;
13831         }
13832         var keyCode = config.key,
13833             shift = config.shift, 
13834             ctrl = config.ctrl, 
13835             alt = config.alt,
13836             fn = config.fn,
13837             scope = config.scope;
13838         if(typeof keyCode == "string"){
13839             var ks = [];
13840             var keyString = keyCode.toUpperCase();
13841             for(var j = 0, len = keyString.length; j < len; j++){
13842                 ks.push(keyString.charCodeAt(j));
13843             }
13844             keyCode = ks;
13845         }
13846         var keyArray = keyCode instanceof Array;
13847         var handler = function(e){
13848             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13849                 var k = e.getKey();
13850                 if(keyArray){
13851                     for(var i = 0, len = keyCode.length; i < len; i++){
13852                         if(keyCode[i] == k){
13853                           if(this.stopEvent){
13854                               e.stopEvent();
13855                           }
13856                           fn.call(scope || window, k, e);
13857                           return;
13858                         }
13859                     }
13860                 }else{
13861                     if(k == keyCode){
13862                         if(this.stopEvent){
13863                            e.stopEvent();
13864                         }
13865                         fn.call(scope || window, k, e);
13866                     }
13867                 }
13868             }
13869         };
13870         this.bindings.push(handler);  
13871         },
13872
13873     /**
13874      * Shorthand for adding a single key listener
13875      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13876      * following options:
13877      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13878      * @param {Function} fn The function to call
13879      * @param {Object} scope (optional) The scope of the function
13880      */
13881     on : function(key, fn, scope){
13882         var keyCode, shift, ctrl, alt;
13883         if(typeof key == "object" && !(key instanceof Array)){
13884             keyCode = key.key;
13885             shift = key.shift;
13886             ctrl = key.ctrl;
13887             alt = key.alt;
13888         }else{
13889             keyCode = key;
13890         }
13891         this.addBinding({
13892             key: keyCode,
13893             shift: shift,
13894             ctrl: ctrl,
13895             alt: alt,
13896             fn: fn,
13897             scope: scope
13898         })
13899     },
13900
13901     // private
13902     handleKeyDown : function(e){
13903             if(this.enabled){ //just in case
13904             var b = this.bindings;
13905             for(var i = 0, len = b.length; i < len; i++){
13906                 b[i].call(this, e);
13907             }
13908             }
13909         },
13910         
13911         /**
13912          * Returns true if this KeyMap is enabled
13913          * @return {Boolean} 
13914          */
13915         isEnabled : function(){
13916             return this.enabled;  
13917         },
13918         
13919         /**
13920          * Enables this KeyMap
13921          */
13922         enable: function(){
13923                 if(!this.enabled){
13924                     this.el.on(this.eventName, this.handleKeyDown, this);
13925                     this.enabled = true;
13926                 }
13927         },
13928
13929         /**
13930          * Disable this KeyMap
13931          */
13932         disable: function(){
13933                 if(this.enabled){
13934                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13935                     this.enabled = false;
13936                 }
13937         }
13938 };/*
13939  * Based on:
13940  * Ext JS Library 1.1.1
13941  * Copyright(c) 2006-2007, Ext JS, LLC.
13942  *
13943  * Originally Released Under LGPL - original licence link has changed is not relivant.
13944  *
13945  * Fork - LGPL
13946  * <script type="text/javascript">
13947  */
13948
13949  
13950 /**
13951  * @class Roo.util.TextMetrics
13952  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13953  * wide, in pixels, a given block of text will be.
13954  * @singleton
13955  */
13956 Roo.util.TextMetrics = function(){
13957     var shared;
13958     return {
13959         /**
13960          * Measures the size of the specified text
13961          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13962          * that can affect the size of the rendered text
13963          * @param {String} text The text to measure
13964          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13965          * in order to accurately measure the text height
13966          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13967          */
13968         measure : function(el, text, fixedWidth){
13969             if(!shared){
13970                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13971             }
13972             shared.bind(el);
13973             shared.setFixedWidth(fixedWidth || 'auto');
13974             return shared.getSize(text);
13975         },
13976
13977         /**
13978          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13979          * the overhead of multiple calls to initialize the style properties on each measurement.
13980          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13981          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13982          * in order to accurately measure the text height
13983          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13984          */
13985         createInstance : function(el, fixedWidth){
13986             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13987         }
13988     };
13989 }();
13990
13991  
13992
13993 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13994     var ml = new Roo.Element(document.createElement('div'));
13995     document.body.appendChild(ml.dom);
13996     ml.position('absolute');
13997     ml.setLeftTop(-1000, -1000);
13998     ml.hide();
13999
14000     if(fixedWidth){
14001         ml.setWidth(fixedWidth);
14002     }
14003      
14004     var instance = {
14005         /**
14006          * Returns the size of the specified text based on the internal element's style and width properties
14007          * @memberOf Roo.util.TextMetrics.Instance#
14008          * @param {String} text The text to measure
14009          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14010          */
14011         getSize : function(text){
14012             ml.update(text);
14013             var s = ml.getSize();
14014             ml.update('');
14015             return s;
14016         },
14017
14018         /**
14019          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14020          * that can affect the size of the rendered text
14021          * @memberOf Roo.util.TextMetrics.Instance#
14022          * @param {String/HTMLElement} el The element, dom node or id
14023          */
14024         bind : function(el){
14025             ml.setStyle(
14026                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14027             );
14028         },
14029
14030         /**
14031          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14032          * to set a fixed width in order to accurately measure the text height.
14033          * @memberOf Roo.util.TextMetrics.Instance#
14034          * @param {Number} width The width to set on the element
14035          */
14036         setFixedWidth : function(width){
14037             ml.setWidth(width);
14038         },
14039
14040         /**
14041          * Returns the measured width of the specified text
14042          * @memberOf Roo.util.TextMetrics.Instance#
14043          * @param {String} text The text to measure
14044          * @return {Number} width The width in pixels
14045          */
14046         getWidth : function(text){
14047             ml.dom.style.width = 'auto';
14048             return this.getSize(text).width;
14049         },
14050
14051         /**
14052          * Returns the measured height of the specified text.  For multiline text, be sure to call
14053          * {@link #setFixedWidth} if necessary.
14054          * @memberOf Roo.util.TextMetrics.Instance#
14055          * @param {String} text The text to measure
14056          * @return {Number} height The height in pixels
14057          */
14058         getHeight : function(text){
14059             return this.getSize(text).height;
14060         }
14061     };
14062
14063     instance.bind(bindTo);
14064
14065     return instance;
14066 };
14067
14068 // backwards compat
14069 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14070  * Based on:
14071  * Ext JS Library 1.1.1
14072  * Copyright(c) 2006-2007, Ext JS, LLC.
14073  *
14074  * Originally Released Under LGPL - original licence link has changed is not relivant.
14075  *
14076  * Fork - LGPL
14077  * <script type="text/javascript">
14078  */
14079
14080 /**
14081  * @class Roo.state.Provider
14082  * Abstract base class for state provider implementations. This class provides methods
14083  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14084  * Provider interface.
14085  */
14086 Roo.state.Provider = function(){
14087     /**
14088      * @event statechange
14089      * Fires when a state change occurs.
14090      * @param {Provider} this This state provider
14091      * @param {String} key The state key which was changed
14092      * @param {String} value The encoded value for the state
14093      */
14094     this.addEvents({
14095         "statechange": true
14096     });
14097     this.state = {};
14098     Roo.state.Provider.superclass.constructor.call(this);
14099 };
14100 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14101     /**
14102      * Returns the current value for a key
14103      * @param {String} name The key name
14104      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14105      * @return {Mixed} The state data
14106      */
14107     get : function(name, defaultValue){
14108         return typeof this.state[name] == "undefined" ?
14109             defaultValue : this.state[name];
14110     },
14111     
14112     /**
14113      * Clears a value from the state
14114      * @param {String} name The key name
14115      */
14116     clear : function(name){
14117         delete this.state[name];
14118         this.fireEvent("statechange", this, name, null);
14119     },
14120     
14121     /**
14122      * Sets the value for a key
14123      * @param {String} name The key name
14124      * @param {Mixed} value The value to set
14125      */
14126     set : function(name, value){
14127         this.state[name] = value;
14128         this.fireEvent("statechange", this, name, value);
14129     },
14130     
14131     /**
14132      * Decodes a string previously encoded with {@link #encodeValue}.
14133      * @param {String} value The value to decode
14134      * @return {Mixed} The decoded value
14135      */
14136     decodeValue : function(cookie){
14137         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14138         var matches = re.exec(unescape(cookie));
14139         if(!matches || !matches[1]) return; // non state cookie
14140         var type = matches[1];
14141         var v = matches[2];
14142         switch(type){
14143             case "n":
14144                 return parseFloat(v);
14145             case "d":
14146                 return new Date(Date.parse(v));
14147             case "b":
14148                 return (v == "1");
14149             case "a":
14150                 var all = [];
14151                 var values = v.split("^");
14152                 for(var i = 0, len = values.length; i < len; i++){
14153                     all.push(this.decodeValue(values[i]));
14154                 }
14155                 return all;
14156            case "o":
14157                 var all = {};
14158                 var values = v.split("^");
14159                 for(var i = 0, len = values.length; i < len; i++){
14160                     var kv = values[i].split("=");
14161                     all[kv[0]] = this.decodeValue(kv[1]);
14162                 }
14163                 return all;
14164            default:
14165                 return v;
14166         }
14167     },
14168     
14169     /**
14170      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14171      * @param {Mixed} value The value to encode
14172      * @return {String} The encoded value
14173      */
14174     encodeValue : function(v){
14175         var enc;
14176         if(typeof v == "number"){
14177             enc = "n:" + v;
14178         }else if(typeof v == "boolean"){
14179             enc = "b:" + (v ? "1" : "0");
14180         }else if(v instanceof Date){
14181             enc = "d:" + v.toGMTString();
14182         }else if(v instanceof Array){
14183             var flat = "";
14184             for(var i = 0, len = v.length; i < len; i++){
14185                 flat += this.encodeValue(v[i]);
14186                 if(i != len-1) flat += "^";
14187             }
14188             enc = "a:" + flat;
14189         }else if(typeof v == "object"){
14190             var flat = "";
14191             for(var key in v){
14192                 if(typeof v[key] != "function"){
14193                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14194                 }
14195             }
14196             enc = "o:" + flat.substring(0, flat.length-1);
14197         }else{
14198             enc = "s:" + v;
14199         }
14200         return escape(enc);        
14201     }
14202 });
14203
14204 /*
14205  * Based on:
14206  * Ext JS Library 1.1.1
14207  * Copyright(c) 2006-2007, Ext JS, LLC.
14208  *
14209  * Originally Released Under LGPL - original licence link has changed is not relivant.
14210  *
14211  * Fork - LGPL
14212  * <script type="text/javascript">
14213  */
14214 /**
14215  * @class Roo.state.Manager
14216  * This is the global state manager. By default all components that are "state aware" check this class
14217  * for state information if you don't pass them a custom state provider. In order for this class
14218  * to be useful, it must be initialized with a provider when your application initializes.
14219  <pre><code>
14220 // in your initialization function
14221 init : function(){
14222    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14223    ...
14224    // supposed you have a {@link Roo.BorderLayout}
14225    var layout = new Roo.BorderLayout(...);
14226    layout.restoreState();
14227    // or a {Roo.BasicDialog}
14228    var dialog = new Roo.BasicDialog(...);
14229    dialog.restoreState();
14230  </code></pre>
14231  * @singleton
14232  */
14233 Roo.state.Manager = function(){
14234     var provider = new Roo.state.Provider();
14235     
14236     return {
14237         /**
14238          * Configures the default state provider for your application
14239          * @param {Provider} stateProvider The state provider to set
14240          */
14241         setProvider : function(stateProvider){
14242             provider = stateProvider;
14243         },
14244         
14245         /**
14246          * Returns the current value for a key
14247          * @param {String} name The key name
14248          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14249          * @return {Mixed} The state data
14250          */
14251         get : function(key, defaultValue){
14252             return provider.get(key, defaultValue);
14253         },
14254         
14255         /**
14256          * Sets the value for a key
14257          * @param {String} name The key name
14258          * @param {Mixed} value The state data
14259          */
14260          set : function(key, value){
14261             provider.set(key, value);
14262         },
14263         
14264         /**
14265          * Clears a value from the state
14266          * @param {String} name The key name
14267          */
14268         clear : function(key){
14269             provider.clear(key);
14270         },
14271         
14272         /**
14273          * Gets the currently configured state provider
14274          * @return {Provider} The state provider
14275          */
14276         getProvider : function(){
14277             return provider;
14278         }
14279     };
14280 }();
14281 /*
14282  * Based on:
14283  * Ext JS Library 1.1.1
14284  * Copyright(c) 2006-2007, Ext JS, LLC.
14285  *
14286  * Originally Released Under LGPL - original licence link has changed is not relivant.
14287  *
14288  * Fork - LGPL
14289  * <script type="text/javascript">
14290  */
14291 /**
14292  * @class Roo.state.CookieProvider
14293  * @extends Roo.state.Provider
14294  * The default Provider implementation which saves state via cookies.
14295  * <br />Usage:
14296  <pre><code>
14297    var cp = new Roo.state.CookieProvider({
14298        path: "/cgi-bin/",
14299        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14300        domain: "roojs.com"
14301    })
14302    Roo.state.Manager.setProvider(cp);
14303  </code></pre>
14304  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14305  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14306  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14307  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14308  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14309  * domain the page is running on including the 'www' like 'www.roojs.com')
14310  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14311  * @constructor
14312  * Create a new CookieProvider
14313  * @param {Object} config The configuration object
14314  */
14315 Roo.state.CookieProvider = function(config){
14316     Roo.state.CookieProvider.superclass.constructor.call(this);
14317     this.path = "/";
14318     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14319     this.domain = null;
14320     this.secure = false;
14321     Roo.apply(this, config);
14322     this.state = this.readCookies();
14323 };
14324
14325 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14326     // private
14327     set : function(name, value){
14328         if(typeof value == "undefined" || value === null){
14329             this.clear(name);
14330             return;
14331         }
14332         this.setCookie(name, value);
14333         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14334     },
14335
14336     // private
14337     clear : function(name){
14338         this.clearCookie(name);
14339         Roo.state.CookieProvider.superclass.clear.call(this, name);
14340     },
14341
14342     // private
14343     readCookies : function(){
14344         var cookies = {};
14345         var c = document.cookie + ";";
14346         var re = /\s?(.*?)=(.*?);/g;
14347         var matches;
14348         while((matches = re.exec(c)) != null){
14349             var name = matches[1];
14350             var value = matches[2];
14351             if(name && name.substring(0,3) == "ys-"){
14352                 cookies[name.substr(3)] = this.decodeValue(value);
14353             }
14354         }
14355         return cookies;
14356     },
14357
14358     // private
14359     setCookie : function(name, value){
14360         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14361            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14362            ((this.path == null) ? "" : ("; path=" + this.path)) +
14363            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14364            ((this.secure == true) ? "; secure" : "");
14365     },
14366
14367     // private
14368     clearCookie : function(name){
14369         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14370            ((this.path == null) ? "" : ("; path=" + this.path)) +
14371            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14372            ((this.secure == true) ? "; secure" : "");
14373     }
14374 });/*
14375  * Based on:
14376  * Ext JS Library 1.1.1
14377  * Copyright(c) 2006-2007, Ext JS, LLC.
14378  *
14379  * Originally Released Under LGPL - original licence link has changed is not relivant.
14380  *
14381  * Fork - LGPL
14382  * <script type="text/javascript">
14383  */
14384
14385
14386
14387 /*
14388  * These classes are derivatives of the similarly named classes in the YUI Library.
14389  * The original license:
14390  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14391  * Code licensed under the BSD License:
14392  * http://developer.yahoo.net/yui/license.txt
14393  */
14394
14395 (function() {
14396
14397 var Event=Roo.EventManager;
14398 var Dom=Roo.lib.Dom;
14399
14400 /**
14401  * @class Roo.dd.DragDrop
14402  * Defines the interface and base operation of items that that can be
14403  * dragged or can be drop targets.  It was designed to be extended, overriding
14404  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14405  * Up to three html elements can be associated with a DragDrop instance:
14406  * <ul>
14407  * <li>linked element: the element that is passed into the constructor.
14408  * This is the element which defines the boundaries for interaction with
14409  * other DragDrop objects.</li>
14410  * <li>handle element(s): The drag operation only occurs if the element that
14411  * was clicked matches a handle element.  By default this is the linked
14412  * element, but there are times that you will want only a portion of the
14413  * linked element to initiate the drag operation, and the setHandleElId()
14414  * method provides a way to define this.</li>
14415  * <li>drag element: this represents the element that would be moved along
14416  * with the cursor during a drag operation.  By default, this is the linked
14417  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14418  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14419  * </li>
14420  * </ul>
14421  * This class should not be instantiated until the onload event to ensure that
14422  * the associated elements are available.
14423  * The following would define a DragDrop obj that would interact with any
14424  * other DragDrop obj in the "group1" group:
14425  * <pre>
14426  *  dd = new Roo.dd.DragDrop("div1", "group1");
14427  * </pre>
14428  * Since none of the event handlers have been implemented, nothing would
14429  * actually happen if you were to run the code above.  Normally you would
14430  * override this class or one of the default implementations, but you can
14431  * also override the methods you want on an instance of the class...
14432  * <pre>
14433  *  dd.onDragDrop = function(e, id) {
14434  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14435  *  }
14436  * </pre>
14437  * @constructor
14438  * @param {String} id of the element that is linked to this instance
14439  * @param {String} sGroup the group of related DragDrop objects
14440  * @param {object} config an object containing configurable attributes
14441  *                Valid properties for DragDrop:
14442  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14443  */
14444 Roo.dd.DragDrop = function(id, sGroup, config) {
14445     if (id) {
14446         this.init(id, sGroup, config);
14447     }
14448     
14449 };
14450
14451 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14452
14453     /**
14454      * The id of the element associated with this object.  This is what we
14455      * refer to as the "linked element" because the size and position of
14456      * this element is used to determine when the drag and drop objects have
14457      * interacted.
14458      * @property id
14459      * @type String
14460      */
14461     id: null,
14462
14463     /**
14464      * Configuration attributes passed into the constructor
14465      * @property config
14466      * @type object
14467      */
14468     config: null,
14469
14470     /**
14471      * The id of the element that will be dragged.  By default this is same
14472      * as the linked element , but could be changed to another element. Ex:
14473      * Roo.dd.DDProxy
14474      * @property dragElId
14475      * @type String
14476      * @private
14477      */
14478     dragElId: null,
14479
14480     /**
14481      * the id of the element that initiates the drag operation.  By default
14482      * this is the linked element, but could be changed to be a child of this
14483      * element.  This lets us do things like only starting the drag when the
14484      * header element within the linked html element is clicked.
14485      * @property handleElId
14486      * @type String
14487      * @private
14488      */
14489     handleElId: null,
14490
14491     /**
14492      * An associative array of HTML tags that will be ignored if clicked.
14493      * @property invalidHandleTypes
14494      * @type {string: string}
14495      */
14496     invalidHandleTypes: null,
14497
14498     /**
14499      * An associative array of ids for elements that will be ignored if clicked
14500      * @property invalidHandleIds
14501      * @type {string: string}
14502      */
14503     invalidHandleIds: null,
14504
14505     /**
14506      * An indexted array of css class names for elements that will be ignored
14507      * if clicked.
14508      * @property invalidHandleClasses
14509      * @type string[]
14510      */
14511     invalidHandleClasses: null,
14512
14513     /**
14514      * The linked element's absolute X position at the time the drag was
14515      * started
14516      * @property startPageX
14517      * @type int
14518      * @private
14519      */
14520     startPageX: 0,
14521
14522     /**
14523      * The linked element's absolute X position at the time the drag was
14524      * started
14525      * @property startPageY
14526      * @type int
14527      * @private
14528      */
14529     startPageY: 0,
14530
14531     /**
14532      * The group defines a logical collection of DragDrop objects that are
14533      * related.  Instances only get events when interacting with other
14534      * DragDrop object in the same group.  This lets us define multiple
14535      * groups using a single DragDrop subclass if we want.
14536      * @property groups
14537      * @type {string: string}
14538      */
14539     groups: null,
14540
14541     /**
14542      * Individual drag/drop instances can be locked.  This will prevent
14543      * onmousedown start drag.
14544      * @property locked
14545      * @type boolean
14546      * @private
14547      */
14548     locked: false,
14549
14550     /**
14551      * Lock this instance
14552      * @method lock
14553      */
14554     lock: function() { this.locked = true; },
14555
14556     /**
14557      * Unlock this instace
14558      * @method unlock
14559      */
14560     unlock: function() { this.locked = false; },
14561
14562     /**
14563      * By default, all insances can be a drop target.  This can be disabled by
14564      * setting isTarget to false.
14565      * @method isTarget
14566      * @type boolean
14567      */
14568     isTarget: true,
14569
14570     /**
14571      * The padding configured for this drag and drop object for calculating
14572      * the drop zone intersection with this object.
14573      * @method padding
14574      * @type int[]
14575      */
14576     padding: null,
14577
14578     /**
14579      * Cached reference to the linked element
14580      * @property _domRef
14581      * @private
14582      */
14583     _domRef: null,
14584
14585     /**
14586      * Internal typeof flag
14587      * @property __ygDragDrop
14588      * @private
14589      */
14590     __ygDragDrop: true,
14591
14592     /**
14593      * Set to true when horizontal contraints are applied
14594      * @property constrainX
14595      * @type boolean
14596      * @private
14597      */
14598     constrainX: false,
14599
14600     /**
14601      * Set to true when vertical contraints are applied
14602      * @property constrainY
14603      * @type boolean
14604      * @private
14605      */
14606     constrainY: false,
14607
14608     /**
14609      * The left constraint
14610      * @property minX
14611      * @type int
14612      * @private
14613      */
14614     minX: 0,
14615
14616     /**
14617      * The right constraint
14618      * @property maxX
14619      * @type int
14620      * @private
14621      */
14622     maxX: 0,
14623
14624     /**
14625      * The up constraint
14626      * @property minY
14627      * @type int
14628      * @type int
14629      * @private
14630      */
14631     minY: 0,
14632
14633     /**
14634      * The down constraint
14635      * @property maxY
14636      * @type int
14637      * @private
14638      */
14639     maxY: 0,
14640
14641     /**
14642      * Maintain offsets when we resetconstraints.  Set to true when you want
14643      * the position of the element relative to its parent to stay the same
14644      * when the page changes
14645      *
14646      * @property maintainOffset
14647      * @type boolean
14648      */
14649     maintainOffset: false,
14650
14651     /**
14652      * Array of pixel locations the element will snap to if we specified a
14653      * horizontal graduation/interval.  This array is generated automatically
14654      * when you define a tick interval.
14655      * @property xTicks
14656      * @type int[]
14657      */
14658     xTicks: null,
14659
14660     /**
14661      * Array of pixel locations the element will snap to if we specified a
14662      * vertical graduation/interval.  This array is generated automatically
14663      * when you define a tick interval.
14664      * @property yTicks
14665      * @type int[]
14666      */
14667     yTicks: null,
14668
14669     /**
14670      * By default the drag and drop instance will only respond to the primary
14671      * button click (left button for a right-handed mouse).  Set to true to
14672      * allow drag and drop to start with any mouse click that is propogated
14673      * by the browser
14674      * @property primaryButtonOnly
14675      * @type boolean
14676      */
14677     primaryButtonOnly: true,
14678
14679     /**
14680      * The availabe property is false until the linked dom element is accessible.
14681      * @property available
14682      * @type boolean
14683      */
14684     available: false,
14685
14686     /**
14687      * By default, drags can only be initiated if the mousedown occurs in the
14688      * region the linked element is.  This is done in part to work around a
14689      * bug in some browsers that mis-report the mousedown if the previous
14690      * mouseup happened outside of the window.  This property is set to true
14691      * if outer handles are defined.
14692      *
14693      * @property hasOuterHandles
14694      * @type boolean
14695      * @default false
14696      */
14697     hasOuterHandles: false,
14698
14699     /**
14700      * Code that executes immediately before the startDrag event
14701      * @method b4StartDrag
14702      * @private
14703      */
14704     b4StartDrag: function(x, y) { },
14705
14706     /**
14707      * Abstract method called after a drag/drop object is clicked
14708      * and the drag or mousedown time thresholds have beeen met.
14709      * @method startDrag
14710      * @param {int} X click location
14711      * @param {int} Y click location
14712      */
14713     startDrag: function(x, y) { /* override this */ },
14714
14715     /**
14716      * Code that executes immediately before the onDrag event
14717      * @method b4Drag
14718      * @private
14719      */
14720     b4Drag: function(e) { },
14721
14722     /**
14723      * Abstract method called during the onMouseMove event while dragging an
14724      * object.
14725      * @method onDrag
14726      * @param {Event} e the mousemove event
14727      */
14728     onDrag: function(e) { /* override this */ },
14729
14730     /**
14731      * Abstract method called when this element fist begins hovering over
14732      * another DragDrop obj
14733      * @method onDragEnter
14734      * @param {Event} e the mousemove event
14735      * @param {String|DragDrop[]} id In POINT mode, the element
14736      * id this is hovering over.  In INTERSECT mode, an array of one or more
14737      * dragdrop items being hovered over.
14738      */
14739     onDragEnter: function(e, id) { /* override this */ },
14740
14741     /**
14742      * Code that executes immediately before the onDragOver event
14743      * @method b4DragOver
14744      * @private
14745      */
14746     b4DragOver: function(e) { },
14747
14748     /**
14749      * Abstract method called when this element is hovering over another
14750      * DragDrop obj
14751      * @method onDragOver
14752      * @param {Event} e the mousemove event
14753      * @param {String|DragDrop[]} id In POINT mode, the element
14754      * id this is hovering over.  In INTERSECT mode, an array of dd items
14755      * being hovered over.
14756      */
14757     onDragOver: function(e, id) { /* override this */ },
14758
14759     /**
14760      * Code that executes immediately before the onDragOut event
14761      * @method b4DragOut
14762      * @private
14763      */
14764     b4DragOut: function(e) { },
14765
14766     /**
14767      * Abstract method called when we are no longer hovering over an element
14768      * @method onDragOut
14769      * @param {Event} e the mousemove event
14770      * @param {String|DragDrop[]} id In POINT mode, the element
14771      * id this was hovering over.  In INTERSECT mode, an array of dd items
14772      * that the mouse is no longer over.
14773      */
14774     onDragOut: function(e, id) { /* override this */ },
14775
14776     /**
14777      * Code that executes immediately before the onDragDrop event
14778      * @method b4DragDrop
14779      * @private
14780      */
14781     b4DragDrop: function(e) { },
14782
14783     /**
14784      * Abstract method called when this item is dropped on another DragDrop
14785      * obj
14786      * @method onDragDrop
14787      * @param {Event} e the mouseup event
14788      * @param {String|DragDrop[]} id In POINT mode, the element
14789      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14790      * was dropped on.
14791      */
14792     onDragDrop: function(e, id) { /* override this */ },
14793
14794     /**
14795      * Abstract method called when this item is dropped on an area with no
14796      * drop target
14797      * @method onInvalidDrop
14798      * @param {Event} e the mouseup event
14799      */
14800     onInvalidDrop: function(e) { /* override this */ },
14801
14802     /**
14803      * Code that executes immediately before the endDrag event
14804      * @method b4EndDrag
14805      * @private
14806      */
14807     b4EndDrag: function(e) { },
14808
14809     /**
14810      * Fired when we are done dragging the object
14811      * @method endDrag
14812      * @param {Event} e the mouseup event
14813      */
14814     endDrag: function(e) { /* override this */ },
14815
14816     /**
14817      * Code executed immediately before the onMouseDown event
14818      * @method b4MouseDown
14819      * @param {Event} e the mousedown event
14820      * @private
14821      */
14822     b4MouseDown: function(e) {  },
14823
14824     /**
14825      * Event handler that fires when a drag/drop obj gets a mousedown
14826      * @method onMouseDown
14827      * @param {Event} e the mousedown event
14828      */
14829     onMouseDown: function(e) { /* override this */ },
14830
14831     /**
14832      * Event handler that fires when a drag/drop obj gets a mouseup
14833      * @method onMouseUp
14834      * @param {Event} e the mouseup event
14835      */
14836     onMouseUp: function(e) { /* override this */ },
14837
14838     /**
14839      * Override the onAvailable method to do what is needed after the initial
14840      * position was determined.
14841      * @method onAvailable
14842      */
14843     onAvailable: function () {
14844     },
14845
14846     /*
14847      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14848      * @type Object
14849      */
14850     defaultPadding : {left:0, right:0, top:0, bottom:0},
14851
14852     /*
14853      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14854  *
14855  * Usage:
14856  <pre><code>
14857  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14858                 { dragElId: "existingProxyDiv" });
14859  dd.startDrag = function(){
14860      this.constrainTo("parent-id");
14861  };
14862  </code></pre>
14863  * Or you can initalize it using the {@link Roo.Element} object:
14864  <pre><code>
14865  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14866      startDrag : function(){
14867          this.constrainTo("parent-id");
14868      }
14869  });
14870  </code></pre>
14871      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14872      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14873      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14874      * an object containing the sides to pad. For example: {right:10, bottom:10}
14875      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14876      */
14877     constrainTo : function(constrainTo, pad, inContent){
14878         if(typeof pad == "number"){
14879             pad = {left: pad, right:pad, top:pad, bottom:pad};
14880         }
14881         pad = pad || this.defaultPadding;
14882         var b = Roo.get(this.getEl()).getBox();
14883         var ce = Roo.get(constrainTo);
14884         var s = ce.getScroll();
14885         var c, cd = ce.dom;
14886         if(cd == document.body){
14887             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14888         }else{
14889             xy = ce.getXY();
14890             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14891         }
14892
14893
14894         var topSpace = b.y - c.y;
14895         var leftSpace = b.x - c.x;
14896
14897         this.resetConstraints();
14898         this.setXConstraint(leftSpace - (pad.left||0), // left
14899                 c.width - leftSpace - b.width - (pad.right||0) //right
14900         );
14901         this.setYConstraint(topSpace - (pad.top||0), //top
14902                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14903         );
14904     },
14905
14906     /**
14907      * Returns a reference to the linked element
14908      * @method getEl
14909      * @return {HTMLElement} the html element
14910      */
14911     getEl: function() {
14912         if (!this._domRef) {
14913             this._domRef = Roo.getDom(this.id);
14914         }
14915
14916         return this._domRef;
14917     },
14918
14919     /**
14920      * Returns a reference to the actual element to drag.  By default this is
14921      * the same as the html element, but it can be assigned to another
14922      * element. An example of this can be found in Roo.dd.DDProxy
14923      * @method getDragEl
14924      * @return {HTMLElement} the html element
14925      */
14926     getDragEl: function() {
14927         return Roo.getDom(this.dragElId);
14928     },
14929
14930     /**
14931      * Sets up the DragDrop object.  Must be called in the constructor of any
14932      * Roo.dd.DragDrop subclass
14933      * @method init
14934      * @param id the id of the linked element
14935      * @param {String} sGroup the group of related items
14936      * @param {object} config configuration attributes
14937      */
14938     init: function(id, sGroup, config) {
14939         this.initTarget(id, sGroup, config);
14940         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14941         // Event.on(this.id, "selectstart", Event.preventDefault);
14942     },
14943
14944     /**
14945      * Initializes Targeting functionality only... the object does not
14946      * get a mousedown handler.
14947      * @method initTarget
14948      * @param id the id of the linked element
14949      * @param {String} sGroup the group of related items
14950      * @param {object} config configuration attributes
14951      */
14952     initTarget: function(id, sGroup, config) {
14953
14954         // configuration attributes
14955         this.config = config || {};
14956
14957         // create a local reference to the drag and drop manager
14958         this.DDM = Roo.dd.DDM;
14959         // initialize the groups array
14960         this.groups = {};
14961
14962         // assume that we have an element reference instead of an id if the
14963         // parameter is not a string
14964         if (typeof id !== "string") {
14965             id = Roo.id(id);
14966         }
14967
14968         // set the id
14969         this.id = id;
14970
14971         // add to an interaction group
14972         this.addToGroup((sGroup) ? sGroup : "default");
14973
14974         // We don't want to register this as the handle with the manager
14975         // so we just set the id rather than calling the setter.
14976         this.handleElId = id;
14977
14978         // the linked element is the element that gets dragged by default
14979         this.setDragElId(id);
14980
14981         // by default, clicked anchors will not start drag operations.
14982         this.invalidHandleTypes = { A: "A" };
14983         this.invalidHandleIds = {};
14984         this.invalidHandleClasses = [];
14985
14986         this.applyConfig();
14987
14988         this.handleOnAvailable();
14989     },
14990
14991     /**
14992      * Applies the configuration parameters that were passed into the constructor.
14993      * This is supposed to happen at each level through the inheritance chain.  So
14994      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14995      * DragDrop in order to get all of the parameters that are available in
14996      * each object.
14997      * @method applyConfig
14998      */
14999     applyConfig: function() {
15000
15001         // configurable properties:
15002         //    padding, isTarget, maintainOffset, primaryButtonOnly
15003         this.padding           = this.config.padding || [0, 0, 0, 0];
15004         this.isTarget          = (this.config.isTarget !== false);
15005         this.maintainOffset    = (this.config.maintainOffset);
15006         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15007
15008     },
15009
15010     /**
15011      * Executed when the linked element is available
15012      * @method handleOnAvailable
15013      * @private
15014      */
15015     handleOnAvailable: function() {
15016         this.available = true;
15017         this.resetConstraints();
15018         this.onAvailable();
15019     },
15020
15021      /**
15022      * Configures the padding for the target zone in px.  Effectively expands
15023      * (or reduces) the virtual object size for targeting calculations.
15024      * Supports css-style shorthand; if only one parameter is passed, all sides
15025      * will have that padding, and if only two are passed, the top and bottom
15026      * will have the first param, the left and right the second.
15027      * @method setPadding
15028      * @param {int} iTop    Top pad
15029      * @param {int} iRight  Right pad
15030      * @param {int} iBot    Bot pad
15031      * @param {int} iLeft   Left pad
15032      */
15033     setPadding: function(iTop, iRight, iBot, iLeft) {
15034         // this.padding = [iLeft, iRight, iTop, iBot];
15035         if (!iRight && 0 !== iRight) {
15036             this.padding = [iTop, iTop, iTop, iTop];
15037         } else if (!iBot && 0 !== iBot) {
15038             this.padding = [iTop, iRight, iTop, iRight];
15039         } else {
15040             this.padding = [iTop, iRight, iBot, iLeft];
15041         }
15042     },
15043
15044     /**
15045      * Stores the initial placement of the linked element.
15046      * @method setInitialPosition
15047      * @param {int} diffX   the X offset, default 0
15048      * @param {int} diffY   the Y offset, default 0
15049      */
15050     setInitPosition: function(diffX, diffY) {
15051         var el = this.getEl();
15052
15053         if (!this.DDM.verifyEl(el)) {
15054             return;
15055         }
15056
15057         var dx = diffX || 0;
15058         var dy = diffY || 0;
15059
15060         var p = Dom.getXY( el );
15061
15062         this.initPageX = p[0] - dx;
15063         this.initPageY = p[1] - dy;
15064
15065         this.lastPageX = p[0];
15066         this.lastPageY = p[1];
15067
15068
15069         this.setStartPosition(p);
15070     },
15071
15072     /**
15073      * Sets the start position of the element.  This is set when the obj
15074      * is initialized, the reset when a drag is started.
15075      * @method setStartPosition
15076      * @param pos current position (from previous lookup)
15077      * @private
15078      */
15079     setStartPosition: function(pos) {
15080         var p = pos || Dom.getXY( this.getEl() );
15081         this.deltaSetXY = null;
15082
15083         this.startPageX = p[0];
15084         this.startPageY = p[1];
15085     },
15086
15087     /**
15088      * Add this instance to a group of related drag/drop objects.  All
15089      * instances belong to at least one group, and can belong to as many
15090      * groups as needed.
15091      * @method addToGroup
15092      * @param sGroup {string} the name of the group
15093      */
15094     addToGroup: function(sGroup) {
15095         this.groups[sGroup] = true;
15096         this.DDM.regDragDrop(this, sGroup);
15097     },
15098
15099     /**
15100      * Remove's this instance from the supplied interaction group
15101      * @method removeFromGroup
15102      * @param {string}  sGroup  The group to drop
15103      */
15104     removeFromGroup: function(sGroup) {
15105         if (this.groups[sGroup]) {
15106             delete this.groups[sGroup];
15107         }
15108
15109         this.DDM.removeDDFromGroup(this, sGroup);
15110     },
15111
15112     /**
15113      * Allows you to specify that an element other than the linked element
15114      * will be moved with the cursor during a drag
15115      * @method setDragElId
15116      * @param id {string} the id of the element that will be used to initiate the drag
15117      */
15118     setDragElId: function(id) {
15119         this.dragElId = id;
15120     },
15121
15122     /**
15123      * Allows you to specify a child of the linked element that should be
15124      * used to initiate the drag operation.  An example of this would be if
15125      * you have a content div with text and links.  Clicking anywhere in the
15126      * content area would normally start the drag operation.  Use this method
15127      * to specify that an element inside of the content div is the element
15128      * that starts the drag operation.
15129      * @method setHandleElId
15130      * @param id {string} the id of the element that will be used to
15131      * initiate the drag.
15132      */
15133     setHandleElId: function(id) {
15134         if (typeof id !== "string") {
15135             id = Roo.id(id);
15136         }
15137         this.handleElId = id;
15138         this.DDM.regHandle(this.id, id);
15139     },
15140
15141     /**
15142      * Allows you to set an element outside of the linked element as a drag
15143      * handle
15144      * @method setOuterHandleElId
15145      * @param id the id of the element that will be used to initiate the drag
15146      */
15147     setOuterHandleElId: function(id) {
15148         if (typeof id !== "string") {
15149             id = Roo.id(id);
15150         }
15151         Event.on(id, "mousedown",
15152                 this.handleMouseDown, this);
15153         this.setHandleElId(id);
15154
15155         this.hasOuterHandles = true;
15156     },
15157
15158     /**
15159      * Remove all drag and drop hooks for this element
15160      * @method unreg
15161      */
15162     unreg: function() {
15163         Event.un(this.id, "mousedown",
15164                 this.handleMouseDown);
15165         this._domRef = null;
15166         this.DDM._remove(this);
15167     },
15168
15169     destroy : function(){
15170         this.unreg();
15171     },
15172
15173     /**
15174      * Returns true if this instance is locked, or the drag drop mgr is locked
15175      * (meaning that all drag/drop is disabled on the page.)
15176      * @method isLocked
15177      * @return {boolean} true if this obj or all drag/drop is locked, else
15178      * false
15179      */
15180     isLocked: function() {
15181         return (this.DDM.isLocked() || this.locked);
15182     },
15183
15184     /**
15185      * Fired when this object is clicked
15186      * @method handleMouseDown
15187      * @param {Event} e
15188      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15189      * @private
15190      */
15191     handleMouseDown: function(e, oDD){
15192         if (this.primaryButtonOnly && e.button != 0) {
15193             return;
15194         }
15195
15196         if (this.isLocked()) {
15197             return;
15198         }
15199
15200         this.DDM.refreshCache(this.groups);
15201
15202         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15203         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15204         } else {
15205             if (this.clickValidator(e)) {
15206
15207                 // set the initial element position
15208                 this.setStartPosition();
15209
15210
15211                 this.b4MouseDown(e);
15212                 this.onMouseDown(e);
15213
15214                 this.DDM.handleMouseDown(e, this);
15215
15216                 this.DDM.stopEvent(e);
15217             } else {
15218
15219
15220             }
15221         }
15222     },
15223
15224     clickValidator: function(e) {
15225         var target = e.getTarget();
15226         return ( this.isValidHandleChild(target) &&
15227                     (this.id == this.handleElId ||
15228                         this.DDM.handleWasClicked(target, this.id)) );
15229     },
15230
15231     /**
15232      * Allows you to specify a tag name that should not start a drag operation
15233      * when clicked.  This is designed to facilitate embedding links within a
15234      * drag handle that do something other than start the drag.
15235      * @method addInvalidHandleType
15236      * @param {string} tagName the type of element to exclude
15237      */
15238     addInvalidHandleType: function(tagName) {
15239         var type = tagName.toUpperCase();
15240         this.invalidHandleTypes[type] = type;
15241     },
15242
15243     /**
15244      * Lets you to specify an element id for a child of a drag handle
15245      * that should not initiate a drag
15246      * @method addInvalidHandleId
15247      * @param {string} id the element id of the element you wish to ignore
15248      */
15249     addInvalidHandleId: function(id) {
15250         if (typeof id !== "string") {
15251             id = Roo.id(id);
15252         }
15253         this.invalidHandleIds[id] = id;
15254     },
15255
15256     /**
15257      * Lets you specify a css class of elements that will not initiate a drag
15258      * @method addInvalidHandleClass
15259      * @param {string} cssClass the class of the elements you wish to ignore
15260      */
15261     addInvalidHandleClass: function(cssClass) {
15262         this.invalidHandleClasses.push(cssClass);
15263     },
15264
15265     /**
15266      * Unsets an excluded tag name set by addInvalidHandleType
15267      * @method removeInvalidHandleType
15268      * @param {string} tagName the type of element to unexclude
15269      */
15270     removeInvalidHandleType: function(tagName) {
15271         var type = tagName.toUpperCase();
15272         // this.invalidHandleTypes[type] = null;
15273         delete this.invalidHandleTypes[type];
15274     },
15275
15276     /**
15277      * Unsets an invalid handle id
15278      * @method removeInvalidHandleId
15279      * @param {string} id the id of the element to re-enable
15280      */
15281     removeInvalidHandleId: function(id) {
15282         if (typeof id !== "string") {
15283             id = Roo.id(id);
15284         }
15285         delete this.invalidHandleIds[id];
15286     },
15287
15288     /**
15289      * Unsets an invalid css class
15290      * @method removeInvalidHandleClass
15291      * @param {string} cssClass the class of the element(s) you wish to
15292      * re-enable
15293      */
15294     removeInvalidHandleClass: function(cssClass) {
15295         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15296             if (this.invalidHandleClasses[i] == cssClass) {
15297                 delete this.invalidHandleClasses[i];
15298             }
15299         }
15300     },
15301
15302     /**
15303      * Checks the tag exclusion list to see if this click should be ignored
15304      * @method isValidHandleChild
15305      * @param {HTMLElement} node the HTMLElement to evaluate
15306      * @return {boolean} true if this is a valid tag type, false if not
15307      */
15308     isValidHandleChild: function(node) {
15309
15310         var valid = true;
15311         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15312         var nodeName;
15313         try {
15314             nodeName = node.nodeName.toUpperCase();
15315         } catch(e) {
15316             nodeName = node.nodeName;
15317         }
15318         valid = valid && !this.invalidHandleTypes[nodeName];
15319         valid = valid && !this.invalidHandleIds[node.id];
15320
15321         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15322             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15323         }
15324
15325
15326         return valid;
15327
15328     },
15329
15330     /**
15331      * Create the array of horizontal tick marks if an interval was specified
15332      * in setXConstraint().
15333      * @method setXTicks
15334      * @private
15335      */
15336     setXTicks: function(iStartX, iTickSize) {
15337         this.xTicks = [];
15338         this.xTickSize = iTickSize;
15339
15340         var tickMap = {};
15341
15342         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15343             if (!tickMap[i]) {
15344                 this.xTicks[this.xTicks.length] = i;
15345                 tickMap[i] = true;
15346             }
15347         }
15348
15349         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15350             if (!tickMap[i]) {
15351                 this.xTicks[this.xTicks.length] = i;
15352                 tickMap[i] = true;
15353             }
15354         }
15355
15356         this.xTicks.sort(this.DDM.numericSort) ;
15357     },
15358
15359     /**
15360      * Create the array of vertical tick marks if an interval was specified in
15361      * setYConstraint().
15362      * @method setYTicks
15363      * @private
15364      */
15365     setYTicks: function(iStartY, iTickSize) {
15366         this.yTicks = [];
15367         this.yTickSize = iTickSize;
15368
15369         var tickMap = {};
15370
15371         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15372             if (!tickMap[i]) {
15373                 this.yTicks[this.yTicks.length] = i;
15374                 tickMap[i] = true;
15375             }
15376         }
15377
15378         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15379             if (!tickMap[i]) {
15380                 this.yTicks[this.yTicks.length] = i;
15381                 tickMap[i] = true;
15382             }
15383         }
15384
15385         this.yTicks.sort(this.DDM.numericSort) ;
15386     },
15387
15388     /**
15389      * By default, the element can be dragged any place on the screen.  Use
15390      * this method to limit the horizontal travel of the element.  Pass in
15391      * 0,0 for the parameters if you want to lock the drag to the y axis.
15392      * @method setXConstraint
15393      * @param {int} iLeft the number of pixels the element can move to the left
15394      * @param {int} iRight the number of pixels the element can move to the
15395      * right
15396      * @param {int} iTickSize optional parameter for specifying that the
15397      * element
15398      * should move iTickSize pixels at a time.
15399      */
15400     setXConstraint: function(iLeft, iRight, iTickSize) {
15401         this.leftConstraint = iLeft;
15402         this.rightConstraint = iRight;
15403
15404         this.minX = this.initPageX - iLeft;
15405         this.maxX = this.initPageX + iRight;
15406         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15407
15408         this.constrainX = true;
15409     },
15410
15411     /**
15412      * Clears any constraints applied to this instance.  Also clears ticks
15413      * since they can't exist independent of a constraint at this time.
15414      * @method clearConstraints
15415      */
15416     clearConstraints: function() {
15417         this.constrainX = false;
15418         this.constrainY = false;
15419         this.clearTicks();
15420     },
15421
15422     /**
15423      * Clears any tick interval defined for this instance
15424      * @method clearTicks
15425      */
15426     clearTicks: function() {
15427         this.xTicks = null;
15428         this.yTicks = null;
15429         this.xTickSize = 0;
15430         this.yTickSize = 0;
15431     },
15432
15433     /**
15434      * By default, the element can be dragged any place on the screen.  Set
15435      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15436      * parameters if you want to lock the drag to the x axis.
15437      * @method setYConstraint
15438      * @param {int} iUp the number of pixels the element can move up
15439      * @param {int} iDown the number of pixels the element can move down
15440      * @param {int} iTickSize optional parameter for specifying that the
15441      * element should move iTickSize pixels at a time.
15442      */
15443     setYConstraint: function(iUp, iDown, iTickSize) {
15444         this.topConstraint = iUp;
15445         this.bottomConstraint = iDown;
15446
15447         this.minY = this.initPageY - iUp;
15448         this.maxY = this.initPageY + iDown;
15449         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15450
15451         this.constrainY = true;
15452
15453     },
15454
15455     /**
15456      * resetConstraints must be called if you manually reposition a dd element.
15457      * @method resetConstraints
15458      * @param {boolean} maintainOffset
15459      */
15460     resetConstraints: function() {
15461
15462
15463         // Maintain offsets if necessary
15464         if (this.initPageX || this.initPageX === 0) {
15465             // figure out how much this thing has moved
15466             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15467             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15468
15469             this.setInitPosition(dx, dy);
15470
15471         // This is the first time we have detected the element's position
15472         } else {
15473             this.setInitPosition();
15474         }
15475
15476         if (this.constrainX) {
15477             this.setXConstraint( this.leftConstraint,
15478                                  this.rightConstraint,
15479                                  this.xTickSize        );
15480         }
15481
15482         if (this.constrainY) {
15483             this.setYConstraint( this.topConstraint,
15484                                  this.bottomConstraint,
15485                                  this.yTickSize         );
15486         }
15487     },
15488
15489     /**
15490      * Normally the drag element is moved pixel by pixel, but we can specify
15491      * that it move a number of pixels at a time.  This method resolves the
15492      * location when we have it set up like this.
15493      * @method getTick
15494      * @param {int} val where we want to place the object
15495      * @param {int[]} tickArray sorted array of valid points
15496      * @return {int} the closest tick
15497      * @private
15498      */
15499     getTick: function(val, tickArray) {
15500
15501         if (!tickArray) {
15502             // If tick interval is not defined, it is effectively 1 pixel,
15503             // so we return the value passed to us.
15504             return val;
15505         } else if (tickArray[0] >= val) {
15506             // The value is lower than the first tick, so we return the first
15507             // tick.
15508             return tickArray[0];
15509         } else {
15510             for (var i=0, len=tickArray.length; i<len; ++i) {
15511                 var next = i + 1;
15512                 if (tickArray[next] && tickArray[next] >= val) {
15513                     var diff1 = val - tickArray[i];
15514                     var diff2 = tickArray[next] - val;
15515                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15516                 }
15517             }
15518
15519             // The value is larger than the last tick, so we return the last
15520             // tick.
15521             return tickArray[tickArray.length - 1];
15522         }
15523     },
15524
15525     /**
15526      * toString method
15527      * @method toString
15528      * @return {string} string representation of the dd obj
15529      */
15530     toString: function() {
15531         return ("DragDrop " + this.id);
15532     }
15533
15534 });
15535
15536 })();
15537 /*
15538  * Based on:
15539  * Ext JS Library 1.1.1
15540  * Copyright(c) 2006-2007, Ext JS, LLC.
15541  *
15542  * Originally Released Under LGPL - original licence link has changed is not relivant.
15543  *
15544  * Fork - LGPL
15545  * <script type="text/javascript">
15546  */
15547
15548
15549 /**
15550  * The drag and drop utility provides a framework for building drag and drop
15551  * applications.  In addition to enabling drag and drop for specific elements,
15552  * the drag and drop elements are tracked by the manager class, and the
15553  * interactions between the various elements are tracked during the drag and
15554  * the implementing code is notified about these important moments.
15555  */
15556
15557 // Only load the library once.  Rewriting the manager class would orphan
15558 // existing drag and drop instances.
15559 if (!Roo.dd.DragDropMgr) {
15560
15561 /**
15562  * @class Roo.dd.DragDropMgr
15563  * DragDropMgr is a singleton that tracks the element interaction for
15564  * all DragDrop items in the window.  Generally, you will not call
15565  * this class directly, but it does have helper methods that could
15566  * be useful in your DragDrop implementations.
15567  * @singleton
15568  */
15569 Roo.dd.DragDropMgr = function() {
15570
15571     var Event = Roo.EventManager;
15572
15573     return {
15574
15575         /**
15576          * Two dimensional Array of registered DragDrop objects.  The first
15577          * dimension is the DragDrop item group, the second the DragDrop
15578          * object.
15579          * @property ids
15580          * @type {string: string}
15581          * @private
15582          * @static
15583          */
15584         ids: {},
15585
15586         /**
15587          * Array of element ids defined as drag handles.  Used to determine
15588          * if the element that generated the mousedown event is actually the
15589          * handle and not the html element itself.
15590          * @property handleIds
15591          * @type {string: string}
15592          * @private
15593          * @static
15594          */
15595         handleIds: {},
15596
15597         /**
15598          * the DragDrop object that is currently being dragged
15599          * @property dragCurrent
15600          * @type DragDrop
15601          * @private
15602          * @static
15603          **/
15604         dragCurrent: null,
15605
15606         /**
15607          * the DragDrop object(s) that are being hovered over
15608          * @property dragOvers
15609          * @type Array
15610          * @private
15611          * @static
15612          */
15613         dragOvers: {},
15614
15615         /**
15616          * the X distance between the cursor and the object being dragged
15617          * @property deltaX
15618          * @type int
15619          * @private
15620          * @static
15621          */
15622         deltaX: 0,
15623
15624         /**
15625          * the Y distance between the cursor and the object being dragged
15626          * @property deltaY
15627          * @type int
15628          * @private
15629          * @static
15630          */
15631         deltaY: 0,
15632
15633         /**
15634          * Flag to determine if we should prevent the default behavior of the
15635          * events we define. By default this is true, but this can be set to
15636          * false if you need the default behavior (not recommended)
15637          * @property preventDefault
15638          * @type boolean
15639          * @static
15640          */
15641         preventDefault: true,
15642
15643         /**
15644          * Flag to determine if we should stop the propagation of the events
15645          * we generate. This is true by default but you may want to set it to
15646          * false if the html element contains other features that require the
15647          * mouse click.
15648          * @property stopPropagation
15649          * @type boolean
15650          * @static
15651          */
15652         stopPropagation: true,
15653
15654         /**
15655          * Internal flag that is set to true when drag and drop has been
15656          * intialized
15657          * @property initialized
15658          * @private
15659          * @static
15660          */
15661         initalized: false,
15662
15663         /**
15664          * All drag and drop can be disabled.
15665          * @property locked
15666          * @private
15667          * @static
15668          */
15669         locked: false,
15670
15671         /**
15672          * Called the first time an element is registered.
15673          * @method init
15674          * @private
15675          * @static
15676          */
15677         init: function() {
15678             this.initialized = true;
15679         },
15680
15681         /**
15682          * In point mode, drag and drop interaction is defined by the
15683          * location of the cursor during the drag/drop
15684          * @property POINT
15685          * @type int
15686          * @static
15687          */
15688         POINT: 0,
15689
15690         /**
15691          * In intersect mode, drag and drop interactio nis defined by the
15692          * overlap of two or more drag and drop objects.
15693          * @property INTERSECT
15694          * @type int
15695          * @static
15696          */
15697         INTERSECT: 1,
15698
15699         /**
15700          * The current drag and drop mode.  Default: POINT
15701          * @property mode
15702          * @type int
15703          * @static
15704          */
15705         mode: 0,
15706
15707         /**
15708          * Runs method on all drag and drop objects
15709          * @method _execOnAll
15710          * @private
15711          * @static
15712          */
15713         _execOnAll: function(sMethod, args) {
15714             for (var i in this.ids) {
15715                 for (var j in this.ids[i]) {
15716                     var oDD = this.ids[i][j];
15717                     if (! this.isTypeOfDD(oDD)) {
15718                         continue;
15719                     }
15720                     oDD[sMethod].apply(oDD, args);
15721                 }
15722             }
15723         },
15724
15725         /**
15726          * Drag and drop initialization.  Sets up the global event handlers
15727          * @method _onLoad
15728          * @private
15729          * @static
15730          */
15731         _onLoad: function() {
15732
15733             this.init();
15734
15735
15736             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15737             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15738             Event.on(window,   "unload",    this._onUnload, this, true);
15739             Event.on(window,   "resize",    this._onResize, this, true);
15740             // Event.on(window,   "mouseout",    this._test);
15741
15742         },
15743
15744         /**
15745          * Reset constraints on all drag and drop objs
15746          * @method _onResize
15747          * @private
15748          * @static
15749          */
15750         _onResize: function(e) {
15751             this._execOnAll("resetConstraints", []);
15752         },
15753
15754         /**
15755          * Lock all drag and drop functionality
15756          * @method lock
15757          * @static
15758          */
15759         lock: function() { this.locked = true; },
15760
15761         /**
15762          * Unlock all drag and drop functionality
15763          * @method unlock
15764          * @static
15765          */
15766         unlock: function() { this.locked = false; },
15767
15768         /**
15769          * Is drag and drop locked?
15770          * @method isLocked
15771          * @return {boolean} True if drag and drop is locked, false otherwise.
15772          * @static
15773          */
15774         isLocked: function() { return this.locked; },
15775
15776         /**
15777          * Location cache that is set for all drag drop objects when a drag is
15778          * initiated, cleared when the drag is finished.
15779          * @property locationCache
15780          * @private
15781          * @static
15782          */
15783         locationCache: {},
15784
15785         /**
15786          * Set useCache to false if you want to force object the lookup of each
15787          * drag and drop linked element constantly during a drag.
15788          * @property useCache
15789          * @type boolean
15790          * @static
15791          */
15792         useCache: true,
15793
15794         /**
15795          * The number of pixels that the mouse needs to move after the
15796          * mousedown before the drag is initiated.  Default=3;
15797          * @property clickPixelThresh
15798          * @type int
15799          * @static
15800          */
15801         clickPixelThresh: 3,
15802
15803         /**
15804          * The number of milliseconds after the mousedown event to initiate the
15805          * drag if we don't get a mouseup event. Default=1000
15806          * @property clickTimeThresh
15807          * @type int
15808          * @static
15809          */
15810         clickTimeThresh: 350,
15811
15812         /**
15813          * Flag that indicates that either the drag pixel threshold or the
15814          * mousdown time threshold has been met
15815          * @property dragThreshMet
15816          * @type boolean
15817          * @private
15818          * @static
15819          */
15820         dragThreshMet: false,
15821
15822         /**
15823          * Timeout used for the click time threshold
15824          * @property clickTimeout
15825          * @type Object
15826          * @private
15827          * @static
15828          */
15829         clickTimeout: null,
15830
15831         /**
15832          * The X position of the mousedown event stored for later use when a
15833          * drag threshold is met.
15834          * @property startX
15835          * @type int
15836          * @private
15837          * @static
15838          */
15839         startX: 0,
15840
15841         /**
15842          * The Y position of the mousedown event stored for later use when a
15843          * drag threshold is met.
15844          * @property startY
15845          * @type int
15846          * @private
15847          * @static
15848          */
15849         startY: 0,
15850
15851         /**
15852          * Each DragDrop instance must be registered with the DragDropMgr.
15853          * This is executed in DragDrop.init()
15854          * @method regDragDrop
15855          * @param {DragDrop} oDD the DragDrop object to register
15856          * @param {String} sGroup the name of the group this element belongs to
15857          * @static
15858          */
15859         regDragDrop: function(oDD, sGroup) {
15860             if (!this.initialized) { this.init(); }
15861
15862             if (!this.ids[sGroup]) {
15863                 this.ids[sGroup] = {};
15864             }
15865             this.ids[sGroup][oDD.id] = oDD;
15866         },
15867
15868         /**
15869          * Removes the supplied dd instance from the supplied group. Executed
15870          * by DragDrop.removeFromGroup, so don't call this function directly.
15871          * @method removeDDFromGroup
15872          * @private
15873          * @static
15874          */
15875         removeDDFromGroup: function(oDD, sGroup) {
15876             if (!this.ids[sGroup]) {
15877                 this.ids[sGroup] = {};
15878             }
15879
15880             var obj = this.ids[sGroup];
15881             if (obj && obj[oDD.id]) {
15882                 delete obj[oDD.id];
15883             }
15884         },
15885
15886         /**
15887          * Unregisters a drag and drop item.  This is executed in
15888          * DragDrop.unreg, use that method instead of calling this directly.
15889          * @method _remove
15890          * @private
15891          * @static
15892          */
15893         _remove: function(oDD) {
15894             for (var g in oDD.groups) {
15895                 if (g && this.ids[g][oDD.id]) {
15896                     delete this.ids[g][oDD.id];
15897                 }
15898             }
15899             delete this.handleIds[oDD.id];
15900         },
15901
15902         /**
15903          * Each DragDrop handle element must be registered.  This is done
15904          * automatically when executing DragDrop.setHandleElId()
15905          * @method regHandle
15906          * @param {String} sDDId the DragDrop id this element is a handle for
15907          * @param {String} sHandleId the id of the element that is the drag
15908          * handle
15909          * @static
15910          */
15911         regHandle: function(sDDId, sHandleId) {
15912             if (!this.handleIds[sDDId]) {
15913                 this.handleIds[sDDId] = {};
15914             }
15915             this.handleIds[sDDId][sHandleId] = sHandleId;
15916         },
15917
15918         /**
15919          * Utility function to determine if a given element has been
15920          * registered as a drag drop item.
15921          * @method isDragDrop
15922          * @param {String} id the element id to check
15923          * @return {boolean} true if this element is a DragDrop item,
15924          * false otherwise
15925          * @static
15926          */
15927         isDragDrop: function(id) {
15928             return ( this.getDDById(id) ) ? true : false;
15929         },
15930
15931         /**
15932          * Returns the drag and drop instances that are in all groups the
15933          * passed in instance belongs to.
15934          * @method getRelated
15935          * @param {DragDrop} p_oDD the obj to get related data for
15936          * @param {boolean} bTargetsOnly if true, only return targetable objs
15937          * @return {DragDrop[]} the related instances
15938          * @static
15939          */
15940         getRelated: function(p_oDD, bTargetsOnly) {
15941             var oDDs = [];
15942             for (var i in p_oDD.groups) {
15943                 for (j in this.ids[i]) {
15944                     var dd = this.ids[i][j];
15945                     if (! this.isTypeOfDD(dd)) {
15946                         continue;
15947                     }
15948                     if (!bTargetsOnly || dd.isTarget) {
15949                         oDDs[oDDs.length] = dd;
15950                     }
15951                 }
15952             }
15953
15954             return oDDs;
15955         },
15956
15957         /**
15958          * Returns true if the specified dd target is a legal target for
15959          * the specifice drag obj
15960          * @method isLegalTarget
15961          * @param {DragDrop} the drag obj
15962          * @param {DragDrop} the target
15963          * @return {boolean} true if the target is a legal target for the
15964          * dd obj
15965          * @static
15966          */
15967         isLegalTarget: function (oDD, oTargetDD) {
15968             var targets = this.getRelated(oDD, true);
15969             for (var i=0, len=targets.length;i<len;++i) {
15970                 if (targets[i].id == oTargetDD.id) {
15971                     return true;
15972                 }
15973             }
15974
15975             return false;
15976         },
15977
15978         /**
15979          * My goal is to be able to transparently determine if an object is
15980          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15981          * returns "object", oDD.constructor.toString() always returns
15982          * "DragDrop" and not the name of the subclass.  So for now it just
15983          * evaluates a well-known variable in DragDrop.
15984          * @method isTypeOfDD
15985          * @param {Object} the object to evaluate
15986          * @return {boolean} true if typeof oDD = DragDrop
15987          * @static
15988          */
15989         isTypeOfDD: function (oDD) {
15990             return (oDD && oDD.__ygDragDrop);
15991         },
15992
15993         /**
15994          * Utility function to determine if a given element has been
15995          * registered as a drag drop handle for the given Drag Drop object.
15996          * @method isHandle
15997          * @param {String} id the element id to check
15998          * @return {boolean} true if this element is a DragDrop handle, false
15999          * otherwise
16000          * @static
16001          */
16002         isHandle: function(sDDId, sHandleId) {
16003             return ( this.handleIds[sDDId] &&
16004                             this.handleIds[sDDId][sHandleId] );
16005         },
16006
16007         /**
16008          * Returns the DragDrop instance for a given id
16009          * @method getDDById
16010          * @param {String} id the id of the DragDrop object
16011          * @return {DragDrop} the drag drop object, null if it is not found
16012          * @static
16013          */
16014         getDDById: function(id) {
16015             for (var i in this.ids) {
16016                 if (this.ids[i][id]) {
16017                     return this.ids[i][id];
16018                 }
16019             }
16020             return null;
16021         },
16022
16023         /**
16024          * Fired after a registered DragDrop object gets the mousedown event.
16025          * Sets up the events required to track the object being dragged
16026          * @method handleMouseDown
16027          * @param {Event} e the event
16028          * @param oDD the DragDrop object being dragged
16029          * @private
16030          * @static
16031          */
16032         handleMouseDown: function(e, oDD) {
16033             if(Roo.QuickTips){
16034                 Roo.QuickTips.disable();
16035             }
16036             this.currentTarget = e.getTarget();
16037
16038             this.dragCurrent = oDD;
16039
16040             var el = oDD.getEl();
16041
16042             // track start position
16043             this.startX = e.getPageX();
16044             this.startY = e.getPageY();
16045
16046             this.deltaX = this.startX - el.offsetLeft;
16047             this.deltaY = this.startY - el.offsetTop;
16048
16049             this.dragThreshMet = false;
16050
16051             this.clickTimeout = setTimeout(
16052                     function() {
16053                         var DDM = Roo.dd.DDM;
16054                         DDM.startDrag(DDM.startX, DDM.startY);
16055                     },
16056                     this.clickTimeThresh );
16057         },
16058
16059         /**
16060          * Fired when either the drag pixel threshol or the mousedown hold
16061          * time threshold has been met.
16062          * @method startDrag
16063          * @param x {int} the X position of the original mousedown
16064          * @param y {int} the Y position of the original mousedown
16065          * @static
16066          */
16067         startDrag: function(x, y) {
16068             clearTimeout(this.clickTimeout);
16069             if (this.dragCurrent) {
16070                 this.dragCurrent.b4StartDrag(x, y);
16071                 this.dragCurrent.startDrag(x, y);
16072             }
16073             this.dragThreshMet = true;
16074         },
16075
16076         /**
16077          * Internal function to handle the mouseup event.  Will be invoked
16078          * from the context of the document.
16079          * @method handleMouseUp
16080          * @param {Event} e the event
16081          * @private
16082          * @static
16083          */
16084         handleMouseUp: function(e) {
16085
16086             if(Roo.QuickTips){
16087                 Roo.QuickTips.enable();
16088             }
16089             if (! this.dragCurrent) {
16090                 return;
16091             }
16092
16093             clearTimeout(this.clickTimeout);
16094
16095             if (this.dragThreshMet) {
16096                 this.fireEvents(e, true);
16097             } else {
16098             }
16099
16100             this.stopDrag(e);
16101
16102             this.stopEvent(e);
16103         },
16104
16105         /**
16106          * Utility to stop event propagation and event default, if these
16107          * features are turned on.
16108          * @method stopEvent
16109          * @param {Event} e the event as returned by this.getEvent()
16110          * @static
16111          */
16112         stopEvent: function(e){
16113             if(this.stopPropagation) {
16114                 e.stopPropagation();
16115             }
16116
16117             if (this.preventDefault) {
16118                 e.preventDefault();
16119             }
16120         },
16121
16122         /**
16123          * Internal function to clean up event handlers after the drag
16124          * operation is complete
16125          * @method stopDrag
16126          * @param {Event} e the event
16127          * @private
16128          * @static
16129          */
16130         stopDrag: function(e) {
16131             // Fire the drag end event for the item that was dragged
16132             if (this.dragCurrent) {
16133                 if (this.dragThreshMet) {
16134                     this.dragCurrent.b4EndDrag(e);
16135                     this.dragCurrent.endDrag(e);
16136                 }
16137
16138                 this.dragCurrent.onMouseUp(e);
16139             }
16140
16141             this.dragCurrent = null;
16142             this.dragOvers = {};
16143         },
16144
16145         /**
16146          * Internal function to handle the mousemove event.  Will be invoked
16147          * from the context of the html element.
16148          *
16149          * @TODO figure out what we can do about mouse events lost when the
16150          * user drags objects beyond the window boundary.  Currently we can
16151          * detect this in internet explorer by verifying that the mouse is
16152          * down during the mousemove event.  Firefox doesn't give us the
16153          * button state on the mousemove event.
16154          * @method handleMouseMove
16155          * @param {Event} e the event
16156          * @private
16157          * @static
16158          */
16159         handleMouseMove: function(e) {
16160             if (! this.dragCurrent) {
16161                 return true;
16162             }
16163
16164             // var button = e.which || e.button;
16165
16166             // check for IE mouseup outside of page boundary
16167             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16168                 this.stopEvent(e);
16169                 return this.handleMouseUp(e);
16170             }
16171
16172             if (!this.dragThreshMet) {
16173                 var diffX = Math.abs(this.startX - e.getPageX());
16174                 var diffY = Math.abs(this.startY - e.getPageY());
16175                 if (diffX > this.clickPixelThresh ||
16176                             diffY > this.clickPixelThresh) {
16177                     this.startDrag(this.startX, this.startY);
16178                 }
16179             }
16180
16181             if (this.dragThreshMet) {
16182                 this.dragCurrent.b4Drag(e);
16183                 this.dragCurrent.onDrag(e);
16184                 if(!this.dragCurrent.moveOnly){
16185                     this.fireEvents(e, false);
16186                 }
16187             }
16188
16189             this.stopEvent(e);
16190
16191             return true;
16192         },
16193
16194         /**
16195          * Iterates over all of the DragDrop elements to find ones we are
16196          * hovering over or dropping on
16197          * @method fireEvents
16198          * @param {Event} e the event
16199          * @param {boolean} isDrop is this a drop op or a mouseover op?
16200          * @private
16201          * @static
16202          */
16203         fireEvents: function(e, isDrop) {
16204             var dc = this.dragCurrent;
16205
16206             // If the user did the mouse up outside of the window, we could
16207             // get here even though we have ended the drag.
16208             if (!dc || dc.isLocked()) {
16209                 return;
16210             }
16211
16212             var pt = e.getPoint();
16213
16214             // cache the previous dragOver array
16215             var oldOvers = [];
16216
16217             var outEvts   = [];
16218             var overEvts  = [];
16219             var dropEvts  = [];
16220             var enterEvts = [];
16221
16222             // Check to see if the object(s) we were hovering over is no longer
16223             // being hovered over so we can fire the onDragOut event
16224             for (var i in this.dragOvers) {
16225
16226                 var ddo = this.dragOvers[i];
16227
16228                 if (! this.isTypeOfDD(ddo)) {
16229                     continue;
16230                 }
16231
16232                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16233                     outEvts.push( ddo );
16234                 }
16235
16236                 oldOvers[i] = true;
16237                 delete this.dragOvers[i];
16238             }
16239
16240             for (var sGroup in dc.groups) {
16241
16242                 if ("string" != typeof sGroup) {
16243                     continue;
16244                 }
16245
16246                 for (i in this.ids[sGroup]) {
16247                     var oDD = this.ids[sGroup][i];
16248                     if (! this.isTypeOfDD(oDD)) {
16249                         continue;
16250                     }
16251
16252                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16253                         if (this.isOverTarget(pt, oDD, this.mode)) {
16254                             // look for drop interactions
16255                             if (isDrop) {
16256                                 dropEvts.push( oDD );
16257                             // look for drag enter and drag over interactions
16258                             } else {
16259
16260                                 // initial drag over: dragEnter fires
16261                                 if (!oldOvers[oDD.id]) {
16262                                     enterEvts.push( oDD );
16263                                 // subsequent drag overs: dragOver fires
16264                                 } else {
16265                                     overEvts.push( oDD );
16266                                 }
16267
16268                                 this.dragOvers[oDD.id] = oDD;
16269                             }
16270                         }
16271                     }
16272                 }
16273             }
16274
16275             if (this.mode) {
16276                 if (outEvts.length) {
16277                     dc.b4DragOut(e, outEvts);
16278                     dc.onDragOut(e, outEvts);
16279                 }
16280
16281                 if (enterEvts.length) {
16282                     dc.onDragEnter(e, enterEvts);
16283                 }
16284
16285                 if (overEvts.length) {
16286                     dc.b4DragOver(e, overEvts);
16287                     dc.onDragOver(e, overEvts);
16288                 }
16289
16290                 if (dropEvts.length) {
16291                     dc.b4DragDrop(e, dropEvts);
16292                     dc.onDragDrop(e, dropEvts);
16293                 }
16294
16295             } else {
16296                 // fire dragout events
16297                 var len = 0;
16298                 for (i=0, len=outEvts.length; i<len; ++i) {
16299                     dc.b4DragOut(e, outEvts[i].id);
16300                     dc.onDragOut(e, outEvts[i].id);
16301                 }
16302
16303                 // fire enter events
16304                 for (i=0,len=enterEvts.length; i<len; ++i) {
16305                     // dc.b4DragEnter(e, oDD.id);
16306                     dc.onDragEnter(e, enterEvts[i].id);
16307                 }
16308
16309                 // fire over events
16310                 for (i=0,len=overEvts.length; i<len; ++i) {
16311                     dc.b4DragOver(e, overEvts[i].id);
16312                     dc.onDragOver(e, overEvts[i].id);
16313                 }
16314
16315                 // fire drop events
16316                 for (i=0, len=dropEvts.length; i<len; ++i) {
16317                     dc.b4DragDrop(e, dropEvts[i].id);
16318                     dc.onDragDrop(e, dropEvts[i].id);
16319                 }
16320
16321             }
16322
16323             // notify about a drop that did not find a target
16324             if (isDrop && !dropEvts.length) {
16325                 dc.onInvalidDrop(e);
16326             }
16327
16328         },
16329
16330         /**
16331          * Helper function for getting the best match from the list of drag
16332          * and drop objects returned by the drag and drop events when we are
16333          * in INTERSECT mode.  It returns either the first object that the
16334          * cursor is over, or the object that has the greatest overlap with
16335          * the dragged element.
16336          * @method getBestMatch
16337          * @param  {DragDrop[]} dds The array of drag and drop objects
16338          * targeted
16339          * @return {DragDrop}       The best single match
16340          * @static
16341          */
16342         getBestMatch: function(dds) {
16343             var winner = null;
16344             // Return null if the input is not what we expect
16345             //if (!dds || !dds.length || dds.length == 0) {
16346                // winner = null;
16347             // If there is only one item, it wins
16348             //} else if (dds.length == 1) {
16349
16350             var len = dds.length;
16351
16352             if (len == 1) {
16353                 winner = dds[0];
16354             } else {
16355                 // Loop through the targeted items
16356                 for (var i=0; i<len; ++i) {
16357                     var dd = dds[i];
16358                     // If the cursor is over the object, it wins.  If the
16359                     // cursor is over multiple matches, the first one we come
16360                     // to wins.
16361                     if (dd.cursorIsOver) {
16362                         winner = dd;
16363                         break;
16364                     // Otherwise the object with the most overlap wins
16365                     } else {
16366                         if (!winner ||
16367                             winner.overlap.getArea() < dd.overlap.getArea()) {
16368                             winner = dd;
16369                         }
16370                     }
16371                 }
16372             }
16373
16374             return winner;
16375         },
16376
16377         /**
16378          * Refreshes the cache of the top-left and bottom-right points of the
16379          * drag and drop objects in the specified group(s).  This is in the
16380          * format that is stored in the drag and drop instance, so typical
16381          * usage is:
16382          * <code>
16383          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16384          * </code>
16385          * Alternatively:
16386          * <code>
16387          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16388          * </code>
16389          * @TODO this really should be an indexed array.  Alternatively this
16390          * method could accept both.
16391          * @method refreshCache
16392          * @param {Object} groups an associative array of groups to refresh
16393          * @static
16394          */
16395         refreshCache: function(groups) {
16396             for (var sGroup in groups) {
16397                 if ("string" != typeof sGroup) {
16398                     continue;
16399                 }
16400                 for (var i in this.ids[sGroup]) {
16401                     var oDD = this.ids[sGroup][i];
16402
16403                     if (this.isTypeOfDD(oDD)) {
16404                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16405                         var loc = this.getLocation(oDD);
16406                         if (loc) {
16407                             this.locationCache[oDD.id] = loc;
16408                         } else {
16409                             delete this.locationCache[oDD.id];
16410                             // this will unregister the drag and drop object if
16411                             // the element is not in a usable state
16412                             // oDD.unreg();
16413                         }
16414                     }
16415                 }
16416             }
16417         },
16418
16419         /**
16420          * This checks to make sure an element exists and is in the DOM.  The
16421          * main purpose is to handle cases where innerHTML is used to remove
16422          * drag and drop objects from the DOM.  IE provides an 'unspecified
16423          * error' when trying to access the offsetParent of such an element
16424          * @method verifyEl
16425          * @param {HTMLElement} el the element to check
16426          * @return {boolean} true if the element looks usable
16427          * @static
16428          */
16429         verifyEl: function(el) {
16430             if (el) {
16431                 var parent;
16432                 if(Roo.isIE){
16433                     try{
16434                         parent = el.offsetParent;
16435                     }catch(e){}
16436                 }else{
16437                     parent = el.offsetParent;
16438                 }
16439                 if (parent) {
16440                     return true;
16441                 }
16442             }
16443
16444             return false;
16445         },
16446
16447         /**
16448          * Returns a Region object containing the drag and drop element's position
16449          * and size, including the padding configured for it
16450          * @method getLocation
16451          * @param {DragDrop} oDD the drag and drop object to get the
16452          *                       location for
16453          * @return {Roo.lib.Region} a Region object representing the total area
16454          *                             the element occupies, including any padding
16455          *                             the instance is configured for.
16456          * @static
16457          */
16458         getLocation: function(oDD) {
16459             if (! this.isTypeOfDD(oDD)) {
16460                 return null;
16461             }
16462
16463             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16464
16465             try {
16466                 pos= Roo.lib.Dom.getXY(el);
16467             } catch (e) { }
16468
16469             if (!pos) {
16470                 return null;
16471             }
16472
16473             x1 = pos[0];
16474             x2 = x1 + el.offsetWidth;
16475             y1 = pos[1];
16476             y2 = y1 + el.offsetHeight;
16477
16478             t = y1 - oDD.padding[0];
16479             r = x2 + oDD.padding[1];
16480             b = y2 + oDD.padding[2];
16481             l = x1 - oDD.padding[3];
16482
16483             return new Roo.lib.Region( t, r, b, l );
16484         },
16485
16486         /**
16487          * Checks the cursor location to see if it over the target
16488          * @method isOverTarget
16489          * @param {Roo.lib.Point} pt The point to evaluate
16490          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16491          * @return {boolean} true if the mouse is over the target
16492          * @private
16493          * @static
16494          */
16495         isOverTarget: function(pt, oTarget, intersect) {
16496             // use cache if available
16497             var loc = this.locationCache[oTarget.id];
16498             if (!loc || !this.useCache) {
16499                 loc = this.getLocation(oTarget);
16500                 this.locationCache[oTarget.id] = loc;
16501
16502             }
16503
16504             if (!loc) {
16505                 return false;
16506             }
16507
16508             oTarget.cursorIsOver = loc.contains( pt );
16509
16510             // DragDrop is using this as a sanity check for the initial mousedown
16511             // in this case we are done.  In POINT mode, if the drag obj has no
16512             // contraints, we are also done. Otherwise we need to evaluate the
16513             // location of the target as related to the actual location of the
16514             // dragged element.
16515             var dc = this.dragCurrent;
16516             if (!dc || !dc.getTargetCoord ||
16517                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16518                 return oTarget.cursorIsOver;
16519             }
16520
16521             oTarget.overlap = null;
16522
16523             // Get the current location of the drag element, this is the
16524             // location of the mouse event less the delta that represents
16525             // where the original mousedown happened on the element.  We
16526             // need to consider constraints and ticks as well.
16527             var pos = dc.getTargetCoord(pt.x, pt.y);
16528
16529             var el = dc.getDragEl();
16530             var curRegion = new Roo.lib.Region( pos.y,
16531                                                    pos.x + el.offsetWidth,
16532                                                    pos.y + el.offsetHeight,
16533                                                    pos.x );
16534
16535             var overlap = curRegion.intersect(loc);
16536
16537             if (overlap) {
16538                 oTarget.overlap = overlap;
16539                 return (intersect) ? true : oTarget.cursorIsOver;
16540             } else {
16541                 return false;
16542             }
16543         },
16544
16545         /**
16546          * unload event handler
16547          * @method _onUnload
16548          * @private
16549          * @static
16550          */
16551         _onUnload: function(e, me) {
16552             Roo.dd.DragDropMgr.unregAll();
16553         },
16554
16555         /**
16556          * Cleans up the drag and drop events and objects.
16557          * @method unregAll
16558          * @private
16559          * @static
16560          */
16561         unregAll: function() {
16562
16563             if (this.dragCurrent) {
16564                 this.stopDrag();
16565                 this.dragCurrent = null;
16566             }
16567
16568             this._execOnAll("unreg", []);
16569
16570             for (i in this.elementCache) {
16571                 delete this.elementCache[i];
16572             }
16573
16574             this.elementCache = {};
16575             this.ids = {};
16576         },
16577
16578         /**
16579          * A cache of DOM elements
16580          * @property elementCache
16581          * @private
16582          * @static
16583          */
16584         elementCache: {},
16585
16586         /**
16587          * Get the wrapper for the DOM element specified
16588          * @method getElWrapper
16589          * @param {String} id the id of the element to get
16590          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16591          * @private
16592          * @deprecated This wrapper isn't that useful
16593          * @static
16594          */
16595         getElWrapper: function(id) {
16596             var oWrapper = this.elementCache[id];
16597             if (!oWrapper || !oWrapper.el) {
16598                 oWrapper = this.elementCache[id] =
16599                     new this.ElementWrapper(Roo.getDom(id));
16600             }
16601             return oWrapper;
16602         },
16603
16604         /**
16605          * Returns the actual DOM element
16606          * @method getElement
16607          * @param {String} id the id of the elment to get
16608          * @return {Object} The element
16609          * @deprecated use Roo.getDom instead
16610          * @static
16611          */
16612         getElement: function(id) {
16613             return Roo.getDom(id);
16614         },
16615
16616         /**
16617          * Returns the style property for the DOM element (i.e.,
16618          * document.getElById(id).style)
16619          * @method getCss
16620          * @param {String} id the id of the elment to get
16621          * @return {Object} The style property of the element
16622          * @deprecated use Roo.getDom instead
16623          * @static
16624          */
16625         getCss: function(id) {
16626             var el = Roo.getDom(id);
16627             return (el) ? el.style : null;
16628         },
16629
16630         /**
16631          * Inner class for cached elements
16632          * @class DragDropMgr.ElementWrapper
16633          * @for DragDropMgr
16634          * @private
16635          * @deprecated
16636          */
16637         ElementWrapper: function(el) {
16638                 /**
16639                  * The element
16640                  * @property el
16641                  */
16642                 this.el = el || null;
16643                 /**
16644                  * The element id
16645                  * @property id
16646                  */
16647                 this.id = this.el && el.id;
16648                 /**
16649                  * A reference to the style property
16650                  * @property css
16651                  */
16652                 this.css = this.el && el.style;
16653             },
16654
16655         /**
16656          * Returns the X position of an html element
16657          * @method getPosX
16658          * @param el the element for which to get the position
16659          * @return {int} the X coordinate
16660          * @for DragDropMgr
16661          * @deprecated use Roo.lib.Dom.getX instead
16662          * @static
16663          */
16664         getPosX: function(el) {
16665             return Roo.lib.Dom.getX(el);
16666         },
16667
16668         /**
16669          * Returns the Y position of an html element
16670          * @method getPosY
16671          * @param el the element for which to get the position
16672          * @return {int} the Y coordinate
16673          * @deprecated use Roo.lib.Dom.getY instead
16674          * @static
16675          */
16676         getPosY: function(el) {
16677             return Roo.lib.Dom.getY(el);
16678         },
16679
16680         /**
16681          * Swap two nodes.  In IE, we use the native method, for others we
16682          * emulate the IE behavior
16683          * @method swapNode
16684          * @param n1 the first node to swap
16685          * @param n2 the other node to swap
16686          * @static
16687          */
16688         swapNode: function(n1, n2) {
16689             if (n1.swapNode) {
16690                 n1.swapNode(n2);
16691             } else {
16692                 var p = n2.parentNode;
16693                 var s = n2.nextSibling;
16694
16695                 if (s == n1) {
16696                     p.insertBefore(n1, n2);
16697                 } else if (n2 == n1.nextSibling) {
16698                     p.insertBefore(n2, n1);
16699                 } else {
16700                     n1.parentNode.replaceChild(n2, n1);
16701                     p.insertBefore(n1, s);
16702                 }
16703             }
16704         },
16705
16706         /**
16707          * Returns the current scroll position
16708          * @method getScroll
16709          * @private
16710          * @static
16711          */
16712         getScroll: function () {
16713             var t, l, dde=document.documentElement, db=document.body;
16714             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16715                 t = dde.scrollTop;
16716                 l = dde.scrollLeft;
16717             } else if (db) {
16718                 t = db.scrollTop;
16719                 l = db.scrollLeft;
16720             } else {
16721
16722             }
16723             return { top: t, left: l };
16724         },
16725
16726         /**
16727          * Returns the specified element style property
16728          * @method getStyle
16729          * @param {HTMLElement} el          the element
16730          * @param {string}      styleProp   the style property
16731          * @return {string} The value of the style property
16732          * @deprecated use Roo.lib.Dom.getStyle
16733          * @static
16734          */
16735         getStyle: function(el, styleProp) {
16736             return Roo.fly(el).getStyle(styleProp);
16737         },
16738
16739         /**
16740          * Gets the scrollTop
16741          * @method getScrollTop
16742          * @return {int} the document's scrollTop
16743          * @static
16744          */
16745         getScrollTop: function () { return this.getScroll().top; },
16746
16747         /**
16748          * Gets the scrollLeft
16749          * @method getScrollLeft
16750          * @return {int} the document's scrollTop
16751          * @static
16752          */
16753         getScrollLeft: function () { return this.getScroll().left; },
16754
16755         /**
16756          * Sets the x/y position of an element to the location of the
16757          * target element.
16758          * @method moveToEl
16759          * @param {HTMLElement} moveEl      The element to move
16760          * @param {HTMLElement} targetEl    The position reference element
16761          * @static
16762          */
16763         moveToEl: function (moveEl, targetEl) {
16764             var aCoord = Roo.lib.Dom.getXY(targetEl);
16765             Roo.lib.Dom.setXY(moveEl, aCoord);
16766         },
16767
16768         /**
16769          * Numeric array sort function
16770          * @method numericSort
16771          * @static
16772          */
16773         numericSort: function(a, b) { return (a - b); },
16774
16775         /**
16776          * Internal counter
16777          * @property _timeoutCount
16778          * @private
16779          * @static
16780          */
16781         _timeoutCount: 0,
16782
16783         /**
16784          * Trying to make the load order less important.  Without this we get
16785          * an error if this file is loaded before the Event Utility.
16786          * @method _addListeners
16787          * @private
16788          * @static
16789          */
16790         _addListeners: function() {
16791             var DDM = Roo.dd.DDM;
16792             if ( Roo.lib.Event && document ) {
16793                 DDM._onLoad();
16794             } else {
16795                 if (DDM._timeoutCount > 2000) {
16796                 } else {
16797                     setTimeout(DDM._addListeners, 10);
16798                     if (document && document.body) {
16799                         DDM._timeoutCount += 1;
16800                     }
16801                 }
16802             }
16803         },
16804
16805         /**
16806          * Recursively searches the immediate parent and all child nodes for
16807          * the handle element in order to determine wheter or not it was
16808          * clicked.
16809          * @method handleWasClicked
16810          * @param node the html element to inspect
16811          * @static
16812          */
16813         handleWasClicked: function(node, id) {
16814             if (this.isHandle(id, node.id)) {
16815                 return true;
16816             } else {
16817                 // check to see if this is a text node child of the one we want
16818                 var p = node.parentNode;
16819
16820                 while (p) {
16821                     if (this.isHandle(id, p.id)) {
16822                         return true;
16823                     } else {
16824                         p = p.parentNode;
16825                     }
16826                 }
16827             }
16828
16829             return false;
16830         }
16831
16832     };
16833
16834 }();
16835
16836 // shorter alias, save a few bytes
16837 Roo.dd.DDM = Roo.dd.DragDropMgr;
16838 Roo.dd.DDM._addListeners();
16839
16840 }/*
16841  * Based on:
16842  * Ext JS Library 1.1.1
16843  * Copyright(c) 2006-2007, Ext JS, LLC.
16844  *
16845  * Originally Released Under LGPL - original licence link has changed is not relivant.
16846  *
16847  * Fork - LGPL
16848  * <script type="text/javascript">
16849  */
16850
16851 /**
16852  * @class Roo.dd.DD
16853  * A DragDrop implementation where the linked element follows the
16854  * mouse cursor during a drag.
16855  * @extends Roo.dd.DragDrop
16856  * @constructor
16857  * @param {String} id the id of the linked element
16858  * @param {String} sGroup the group of related DragDrop items
16859  * @param {object} config an object containing configurable attributes
16860  *                Valid properties for DD:
16861  *                    scroll
16862  */
16863 Roo.dd.DD = function(id, sGroup, config) {
16864     if (id) {
16865         this.init(id, sGroup, config);
16866     }
16867 };
16868
16869 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16870
16871     /**
16872      * When set to true, the utility automatically tries to scroll the browser
16873      * window wehn a drag and drop element is dragged near the viewport boundary.
16874      * Defaults to true.
16875      * @property scroll
16876      * @type boolean
16877      */
16878     scroll: true,
16879
16880     /**
16881      * Sets the pointer offset to the distance between the linked element's top
16882      * left corner and the location the element was clicked
16883      * @method autoOffset
16884      * @param {int} iPageX the X coordinate of the click
16885      * @param {int} iPageY the Y coordinate of the click
16886      */
16887     autoOffset: function(iPageX, iPageY) {
16888         var x = iPageX - this.startPageX;
16889         var y = iPageY - this.startPageY;
16890         this.setDelta(x, y);
16891     },
16892
16893     /**
16894      * Sets the pointer offset.  You can call this directly to force the
16895      * offset to be in a particular location (e.g., pass in 0,0 to set it
16896      * to the center of the object)
16897      * @method setDelta
16898      * @param {int} iDeltaX the distance from the left
16899      * @param {int} iDeltaY the distance from the top
16900      */
16901     setDelta: function(iDeltaX, iDeltaY) {
16902         this.deltaX = iDeltaX;
16903         this.deltaY = iDeltaY;
16904     },
16905
16906     /**
16907      * Sets the drag element to the location of the mousedown or click event,
16908      * maintaining the cursor location relative to the location on the element
16909      * that was clicked.  Override this if you want to place the element in a
16910      * location other than where the cursor is.
16911      * @method setDragElPos
16912      * @param {int} iPageX the X coordinate of the mousedown or drag event
16913      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16914      */
16915     setDragElPos: function(iPageX, iPageY) {
16916         // the first time we do this, we are going to check to make sure
16917         // the element has css positioning
16918
16919         var el = this.getDragEl();
16920         this.alignElWithMouse(el, iPageX, iPageY);
16921     },
16922
16923     /**
16924      * Sets the element to the location of the mousedown or click event,
16925      * maintaining the cursor location relative to the location on the element
16926      * that was clicked.  Override this if you want to place the element in a
16927      * location other than where the cursor is.
16928      * @method alignElWithMouse
16929      * @param {HTMLElement} el the element to move
16930      * @param {int} iPageX the X coordinate of the mousedown or drag event
16931      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16932      */
16933     alignElWithMouse: function(el, iPageX, iPageY) {
16934         var oCoord = this.getTargetCoord(iPageX, iPageY);
16935         var fly = el.dom ? el : Roo.fly(el);
16936         if (!this.deltaSetXY) {
16937             var aCoord = [oCoord.x, oCoord.y];
16938             fly.setXY(aCoord);
16939             var newLeft = fly.getLeft(true);
16940             var newTop  = fly.getTop(true);
16941             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16942         } else {
16943             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16944         }
16945
16946         this.cachePosition(oCoord.x, oCoord.y);
16947         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16948         return oCoord;
16949     },
16950
16951     /**
16952      * Saves the most recent position so that we can reset the constraints and
16953      * tick marks on-demand.  We need to know this so that we can calculate the
16954      * number of pixels the element is offset from its original position.
16955      * @method cachePosition
16956      * @param iPageX the current x position (optional, this just makes it so we
16957      * don't have to look it up again)
16958      * @param iPageY the current y position (optional, this just makes it so we
16959      * don't have to look it up again)
16960      */
16961     cachePosition: function(iPageX, iPageY) {
16962         if (iPageX) {
16963             this.lastPageX = iPageX;
16964             this.lastPageY = iPageY;
16965         } else {
16966             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16967             this.lastPageX = aCoord[0];
16968             this.lastPageY = aCoord[1];
16969         }
16970     },
16971
16972     /**
16973      * Auto-scroll the window if the dragged object has been moved beyond the
16974      * visible window boundary.
16975      * @method autoScroll
16976      * @param {int} x the drag element's x position
16977      * @param {int} y the drag element's y position
16978      * @param {int} h the height of the drag element
16979      * @param {int} w the width of the drag element
16980      * @private
16981      */
16982     autoScroll: function(x, y, h, w) {
16983
16984         if (this.scroll) {
16985             // The client height
16986             var clientH = Roo.lib.Dom.getViewWidth();
16987
16988             // The client width
16989             var clientW = Roo.lib.Dom.getViewHeight();
16990
16991             // The amt scrolled down
16992             var st = this.DDM.getScrollTop();
16993
16994             // The amt scrolled right
16995             var sl = this.DDM.getScrollLeft();
16996
16997             // Location of the bottom of the element
16998             var bot = h + y;
16999
17000             // Location of the right of the element
17001             var right = w + x;
17002
17003             // The distance from the cursor to the bottom of the visible area,
17004             // adjusted so that we don't scroll if the cursor is beyond the
17005             // element drag constraints
17006             var toBot = (clientH + st - y - this.deltaY);
17007
17008             // The distance from the cursor to the right of the visible area
17009             var toRight = (clientW + sl - x - this.deltaX);
17010
17011
17012             // How close to the edge the cursor must be before we scroll
17013             // var thresh = (document.all) ? 100 : 40;
17014             var thresh = 40;
17015
17016             // How many pixels to scroll per autoscroll op.  This helps to reduce
17017             // clunky scrolling. IE is more sensitive about this ... it needs this
17018             // value to be higher.
17019             var scrAmt = (document.all) ? 80 : 30;
17020
17021             // Scroll down if we are near the bottom of the visible page and the
17022             // obj extends below the crease
17023             if ( bot > clientH && toBot < thresh ) {
17024                 window.scrollTo(sl, st + scrAmt);
17025             }
17026
17027             // Scroll up if the window is scrolled down and the top of the object
17028             // goes above the top border
17029             if ( y < st && st > 0 && y - st < thresh ) {
17030                 window.scrollTo(sl, st - scrAmt);
17031             }
17032
17033             // Scroll right if the obj is beyond the right border and the cursor is
17034             // near the border.
17035             if ( right > clientW && toRight < thresh ) {
17036                 window.scrollTo(sl + scrAmt, st);
17037             }
17038
17039             // Scroll left if the window has been scrolled to the right and the obj
17040             // extends past the left border
17041             if ( x < sl && sl > 0 && x - sl < thresh ) {
17042                 window.scrollTo(sl - scrAmt, st);
17043             }
17044         }
17045     },
17046
17047     /**
17048      * Finds the location the element should be placed if we want to move
17049      * it to where the mouse location less the click offset would place us.
17050      * @method getTargetCoord
17051      * @param {int} iPageX the X coordinate of the click
17052      * @param {int} iPageY the Y coordinate of the click
17053      * @return an object that contains the coordinates (Object.x and Object.y)
17054      * @private
17055      */
17056     getTargetCoord: function(iPageX, iPageY) {
17057
17058
17059         var x = iPageX - this.deltaX;
17060         var y = iPageY - this.deltaY;
17061
17062         if (this.constrainX) {
17063             if (x < this.minX) { x = this.minX; }
17064             if (x > this.maxX) { x = this.maxX; }
17065         }
17066
17067         if (this.constrainY) {
17068             if (y < this.minY) { y = this.minY; }
17069             if (y > this.maxY) { y = this.maxY; }
17070         }
17071
17072         x = this.getTick(x, this.xTicks);
17073         y = this.getTick(y, this.yTicks);
17074
17075
17076         return {x:x, y:y};
17077     },
17078
17079     /*
17080      * Sets up config options specific to this class. Overrides
17081      * Roo.dd.DragDrop, but all versions of this method through the
17082      * inheritance chain are called
17083      */
17084     applyConfig: function() {
17085         Roo.dd.DD.superclass.applyConfig.call(this);
17086         this.scroll = (this.config.scroll !== false);
17087     },
17088
17089     /*
17090      * Event that fires prior to the onMouseDown event.  Overrides
17091      * Roo.dd.DragDrop.
17092      */
17093     b4MouseDown: function(e) {
17094         // this.resetConstraints();
17095         this.autoOffset(e.getPageX(),
17096                             e.getPageY());
17097     },
17098
17099     /*
17100      * Event that fires prior to the onDrag event.  Overrides
17101      * Roo.dd.DragDrop.
17102      */
17103     b4Drag: function(e) {
17104         this.setDragElPos(e.getPageX(),
17105                             e.getPageY());
17106     },
17107
17108     toString: function() {
17109         return ("DD " + this.id);
17110     }
17111
17112     //////////////////////////////////////////////////////////////////////////
17113     // Debugging ygDragDrop events that can be overridden
17114     //////////////////////////////////////////////////////////////////////////
17115     /*
17116     startDrag: function(x, y) {
17117     },
17118
17119     onDrag: function(e) {
17120     },
17121
17122     onDragEnter: function(e, id) {
17123     },
17124
17125     onDragOver: function(e, id) {
17126     },
17127
17128     onDragOut: function(e, id) {
17129     },
17130
17131     onDragDrop: function(e, id) {
17132     },
17133
17134     endDrag: function(e) {
17135     }
17136
17137     */
17138
17139 });/*
17140  * Based on:
17141  * Ext JS Library 1.1.1
17142  * Copyright(c) 2006-2007, Ext JS, LLC.
17143  *
17144  * Originally Released Under LGPL - original licence link has changed is not relivant.
17145  *
17146  * Fork - LGPL
17147  * <script type="text/javascript">
17148  */
17149
17150 /**
17151  * @class Roo.dd.DDProxy
17152  * A DragDrop implementation that inserts an empty, bordered div into
17153  * the document that follows the cursor during drag operations.  At the time of
17154  * the click, the frame div is resized to the dimensions of the linked html
17155  * element, and moved to the exact location of the linked element.
17156  *
17157  * References to the "frame" element refer to the single proxy element that
17158  * was created to be dragged in place of all DDProxy elements on the
17159  * page.
17160  *
17161  * @extends Roo.dd.DD
17162  * @constructor
17163  * @param {String} id the id of the linked html element
17164  * @param {String} sGroup the group of related DragDrop objects
17165  * @param {object} config an object containing configurable attributes
17166  *                Valid properties for DDProxy in addition to those in DragDrop:
17167  *                   resizeFrame, centerFrame, dragElId
17168  */
17169 Roo.dd.DDProxy = function(id, sGroup, config) {
17170     if (id) {
17171         this.init(id, sGroup, config);
17172         this.initFrame();
17173     }
17174 };
17175
17176 /**
17177  * The default drag frame div id
17178  * @property Roo.dd.DDProxy.dragElId
17179  * @type String
17180  * @static
17181  */
17182 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17183
17184 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17185
17186     /**
17187      * By default we resize the drag frame to be the same size as the element
17188      * we want to drag (this is to get the frame effect).  We can turn it off
17189      * if we want a different behavior.
17190      * @property resizeFrame
17191      * @type boolean
17192      */
17193     resizeFrame: true,
17194
17195     /**
17196      * By default the frame is positioned exactly where the drag element is, so
17197      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17198      * you do not have constraints on the obj is to have the drag frame centered
17199      * around the cursor.  Set centerFrame to true for this effect.
17200      * @property centerFrame
17201      * @type boolean
17202      */
17203     centerFrame: false,
17204
17205     /**
17206      * Creates the proxy element if it does not yet exist
17207      * @method createFrame
17208      */
17209     createFrame: function() {
17210         var self = this;
17211         var body = document.body;
17212
17213         if (!body || !body.firstChild) {
17214             setTimeout( function() { self.createFrame(); }, 50 );
17215             return;
17216         }
17217
17218         var div = this.getDragEl();
17219
17220         if (!div) {
17221             div    = document.createElement("div");
17222             div.id = this.dragElId;
17223             var s  = div.style;
17224
17225             s.position   = "absolute";
17226             s.visibility = "hidden";
17227             s.cursor     = "move";
17228             s.border     = "2px solid #aaa";
17229             s.zIndex     = 999;
17230
17231             // appendChild can blow up IE if invoked prior to the window load event
17232             // while rendering a table.  It is possible there are other scenarios
17233             // that would cause this to happen as well.
17234             body.insertBefore(div, body.firstChild);
17235         }
17236     },
17237
17238     /**
17239      * Initialization for the drag frame element.  Must be called in the
17240      * constructor of all subclasses
17241      * @method initFrame
17242      */
17243     initFrame: function() {
17244         this.createFrame();
17245     },
17246
17247     applyConfig: function() {
17248         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17249
17250         this.resizeFrame = (this.config.resizeFrame !== false);
17251         this.centerFrame = (this.config.centerFrame);
17252         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17253     },
17254
17255     /**
17256      * Resizes the drag frame to the dimensions of the clicked object, positions
17257      * it over the object, and finally displays it
17258      * @method showFrame
17259      * @param {int} iPageX X click position
17260      * @param {int} iPageY Y click position
17261      * @private
17262      */
17263     showFrame: function(iPageX, iPageY) {
17264         var el = this.getEl();
17265         var dragEl = this.getDragEl();
17266         var s = dragEl.style;
17267
17268         this._resizeProxy();
17269
17270         if (this.centerFrame) {
17271             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17272                            Math.round(parseInt(s.height, 10)/2) );
17273         }
17274
17275         this.setDragElPos(iPageX, iPageY);
17276
17277         Roo.fly(dragEl).show();
17278     },
17279
17280     /**
17281      * The proxy is automatically resized to the dimensions of the linked
17282      * element when a drag is initiated, unless resizeFrame is set to false
17283      * @method _resizeProxy
17284      * @private
17285      */
17286     _resizeProxy: function() {
17287         if (this.resizeFrame) {
17288             var el = this.getEl();
17289             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17290         }
17291     },
17292
17293     // overrides Roo.dd.DragDrop
17294     b4MouseDown: function(e) {
17295         var x = e.getPageX();
17296         var y = e.getPageY();
17297         this.autoOffset(x, y);
17298         this.setDragElPos(x, y);
17299     },
17300
17301     // overrides Roo.dd.DragDrop
17302     b4StartDrag: function(x, y) {
17303         // show the drag frame
17304         this.showFrame(x, y);
17305     },
17306
17307     // overrides Roo.dd.DragDrop
17308     b4EndDrag: function(e) {
17309         Roo.fly(this.getDragEl()).hide();
17310     },
17311
17312     // overrides Roo.dd.DragDrop
17313     // By default we try to move the element to the last location of the frame.
17314     // This is so that the default behavior mirrors that of Roo.dd.DD.
17315     endDrag: function(e) {
17316
17317         var lel = this.getEl();
17318         var del = this.getDragEl();
17319
17320         // Show the drag frame briefly so we can get its position
17321         del.style.visibility = "";
17322
17323         this.beforeMove();
17324         // Hide the linked element before the move to get around a Safari
17325         // rendering bug.
17326         lel.style.visibility = "hidden";
17327         Roo.dd.DDM.moveToEl(lel, del);
17328         del.style.visibility = "hidden";
17329         lel.style.visibility = "";
17330
17331         this.afterDrag();
17332     },
17333
17334     beforeMove : function(){
17335
17336     },
17337
17338     afterDrag : function(){
17339
17340     },
17341
17342     toString: function() {
17343         return ("DDProxy " + this.id);
17344     }
17345
17346 });
17347 /*
17348  * Based on:
17349  * Ext JS Library 1.1.1
17350  * Copyright(c) 2006-2007, Ext JS, LLC.
17351  *
17352  * Originally Released Under LGPL - original licence link has changed is not relivant.
17353  *
17354  * Fork - LGPL
17355  * <script type="text/javascript">
17356  */
17357
17358  /**
17359  * @class Roo.dd.DDTarget
17360  * A DragDrop implementation that does not move, but can be a drop
17361  * target.  You would get the same result by simply omitting implementation
17362  * for the event callbacks, but this way we reduce the processing cost of the
17363  * event listener and the callbacks.
17364  * @extends Roo.dd.DragDrop
17365  * @constructor
17366  * @param {String} id the id of the element that is a drop target
17367  * @param {String} sGroup the group of related DragDrop objects
17368  * @param {object} config an object containing configurable attributes
17369  *                 Valid properties for DDTarget in addition to those in
17370  *                 DragDrop:
17371  *                    none
17372  */
17373 Roo.dd.DDTarget = function(id, sGroup, config) {
17374     if (id) {
17375         this.initTarget(id, sGroup, config);
17376     }
17377     if (config.listeners || config.events) { 
17378        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17379             listeners : config.listeners || {}, 
17380             events : config.events || {} 
17381         });    
17382     }
17383 };
17384
17385 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17386 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17387     toString: function() {
17388         return ("DDTarget " + this.id);
17389     }
17390 });
17391 /*
17392  * Based on:
17393  * Ext JS Library 1.1.1
17394  * Copyright(c) 2006-2007, Ext JS, LLC.
17395  *
17396  * Originally Released Under LGPL - original licence link has changed is not relivant.
17397  *
17398  * Fork - LGPL
17399  * <script type="text/javascript">
17400  */
17401  
17402
17403 /**
17404  * @class Roo.dd.ScrollManager
17405  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17406  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17407  * @singleton
17408  */
17409 Roo.dd.ScrollManager = function(){
17410     var ddm = Roo.dd.DragDropMgr;
17411     var els = {};
17412     var dragEl = null;
17413     var proc = {};
17414     
17415     var onStop = function(e){
17416         dragEl = null;
17417         clearProc();
17418     };
17419     
17420     var triggerRefresh = function(){
17421         if(ddm.dragCurrent){
17422              ddm.refreshCache(ddm.dragCurrent.groups);
17423         }
17424     };
17425     
17426     var doScroll = function(){
17427         if(ddm.dragCurrent){
17428             var dds = Roo.dd.ScrollManager;
17429             if(!dds.animate){
17430                 if(proc.el.scroll(proc.dir, dds.increment)){
17431                     triggerRefresh();
17432                 }
17433             }else{
17434                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17435             }
17436         }
17437     };
17438     
17439     var clearProc = function(){
17440         if(proc.id){
17441             clearInterval(proc.id);
17442         }
17443         proc.id = 0;
17444         proc.el = null;
17445         proc.dir = "";
17446     };
17447     
17448     var startProc = function(el, dir){
17449         clearProc();
17450         proc.el = el;
17451         proc.dir = dir;
17452         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17453     };
17454     
17455     var onFire = function(e, isDrop){
17456         if(isDrop || !ddm.dragCurrent){ return; }
17457         var dds = Roo.dd.ScrollManager;
17458         if(!dragEl || dragEl != ddm.dragCurrent){
17459             dragEl = ddm.dragCurrent;
17460             // refresh regions on drag start
17461             dds.refreshCache();
17462         }
17463         
17464         var xy = Roo.lib.Event.getXY(e);
17465         var pt = new Roo.lib.Point(xy[0], xy[1]);
17466         for(var id in els){
17467             var el = els[id], r = el._region;
17468             if(r && r.contains(pt) && el.isScrollable()){
17469                 if(r.bottom - pt.y <= dds.thresh){
17470                     if(proc.el != el){
17471                         startProc(el, "down");
17472                     }
17473                     return;
17474                 }else if(r.right - pt.x <= dds.thresh){
17475                     if(proc.el != el){
17476                         startProc(el, "left");
17477                     }
17478                     return;
17479                 }else if(pt.y - r.top <= dds.thresh){
17480                     if(proc.el != el){
17481                         startProc(el, "up");
17482                     }
17483                     return;
17484                 }else if(pt.x - r.left <= dds.thresh){
17485                     if(proc.el != el){
17486                         startProc(el, "right");
17487                     }
17488                     return;
17489                 }
17490             }
17491         }
17492         clearProc();
17493     };
17494     
17495     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17496     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17497     
17498     return {
17499         /**
17500          * Registers new overflow element(s) to auto scroll
17501          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17502          */
17503         register : function(el){
17504             if(el instanceof Array){
17505                 for(var i = 0, len = el.length; i < len; i++) {
17506                         this.register(el[i]);
17507                 }
17508             }else{
17509                 el = Roo.get(el);
17510                 els[el.id] = el;
17511             }
17512         },
17513         
17514         /**
17515          * Unregisters overflow element(s) so they are no longer scrolled
17516          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17517          */
17518         unregister : function(el){
17519             if(el instanceof Array){
17520                 for(var i = 0, len = el.length; i < len; i++) {
17521                         this.unregister(el[i]);
17522                 }
17523             }else{
17524                 el = Roo.get(el);
17525                 delete els[el.id];
17526             }
17527         },
17528         
17529         /**
17530          * The number of pixels from the edge of a container the pointer needs to be to 
17531          * trigger scrolling (defaults to 25)
17532          * @type Number
17533          */
17534         thresh : 25,
17535         
17536         /**
17537          * The number of pixels to scroll in each scroll increment (defaults to 50)
17538          * @type Number
17539          */
17540         increment : 100,
17541         
17542         /**
17543          * The frequency of scrolls in milliseconds (defaults to 500)
17544          * @type Number
17545          */
17546         frequency : 500,
17547         
17548         /**
17549          * True to animate the scroll (defaults to true)
17550          * @type Boolean
17551          */
17552         animate: true,
17553         
17554         /**
17555          * The animation duration in seconds - 
17556          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17557          * @type Number
17558          */
17559         animDuration: .4,
17560         
17561         /**
17562          * Manually trigger a cache refresh.
17563          */
17564         refreshCache : function(){
17565             for(var id in els){
17566                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17567                     els[id]._region = els[id].getRegion();
17568                 }
17569             }
17570         }
17571     };
17572 }();/*
17573  * Based on:
17574  * Ext JS Library 1.1.1
17575  * Copyright(c) 2006-2007, Ext JS, LLC.
17576  *
17577  * Originally Released Under LGPL - original licence link has changed is not relivant.
17578  *
17579  * Fork - LGPL
17580  * <script type="text/javascript">
17581  */
17582  
17583
17584 /**
17585  * @class Roo.dd.Registry
17586  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17587  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17588  * @singleton
17589  */
17590 Roo.dd.Registry = function(){
17591     var elements = {}; 
17592     var handles = {}; 
17593     var autoIdSeed = 0;
17594
17595     var getId = function(el, autogen){
17596         if(typeof el == "string"){
17597             return el;
17598         }
17599         var id = el.id;
17600         if(!id && autogen !== false){
17601             id = "roodd-" + (++autoIdSeed);
17602             el.id = id;
17603         }
17604         return id;
17605     };
17606     
17607     return {
17608     /**
17609      * Register a drag drop element
17610      * @param {String|HTMLElement} element The id or DOM node to register
17611      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17612      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17613      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17614      * populated in the data object (if applicable):
17615      * <pre>
17616 Value      Description<br />
17617 ---------  ------------------------------------------<br />
17618 handles    Array of DOM nodes that trigger dragging<br />
17619            for the element being registered<br />
17620 isHandle   True if the element passed in triggers<br />
17621            dragging itself, else false
17622 </pre>
17623      */
17624         register : function(el, data){
17625             data = data || {};
17626             if(typeof el == "string"){
17627                 el = document.getElementById(el);
17628             }
17629             data.ddel = el;
17630             elements[getId(el)] = data;
17631             if(data.isHandle !== false){
17632                 handles[data.ddel.id] = data;
17633             }
17634             if(data.handles){
17635                 var hs = data.handles;
17636                 for(var i = 0, len = hs.length; i < len; i++){
17637                         handles[getId(hs[i])] = data;
17638                 }
17639             }
17640         },
17641
17642     /**
17643      * Unregister a drag drop element
17644      * @param {String|HTMLElement}  element The id or DOM node to unregister
17645      */
17646         unregister : function(el){
17647             var id = getId(el, false);
17648             var data = elements[id];
17649             if(data){
17650                 delete elements[id];
17651                 if(data.handles){
17652                     var hs = data.handles;
17653                     for(var i = 0, len = hs.length; i < len; i++){
17654                         delete handles[getId(hs[i], false)];
17655                     }
17656                 }
17657             }
17658         },
17659
17660     /**
17661      * Returns the handle registered for a DOM Node by id
17662      * @param {String|HTMLElement} id The DOM node or id to look up
17663      * @return {Object} handle The custom handle data
17664      */
17665         getHandle : function(id){
17666             if(typeof id != "string"){ // must be element?
17667                 id = id.id;
17668             }
17669             return handles[id];
17670         },
17671
17672     /**
17673      * Returns the handle that is registered for the DOM node that is the target of the event
17674      * @param {Event} e The event
17675      * @return {Object} handle The custom handle data
17676      */
17677         getHandleFromEvent : function(e){
17678             var t = Roo.lib.Event.getTarget(e);
17679             return t ? handles[t.id] : null;
17680         },
17681
17682     /**
17683      * Returns a custom data object that is registered for a DOM node by id
17684      * @param {String|HTMLElement} id The DOM node or id to look up
17685      * @return {Object} data The custom data
17686      */
17687         getTarget : function(id){
17688             if(typeof id != "string"){ // must be element?
17689                 id = id.id;
17690             }
17691             return elements[id];
17692         },
17693
17694     /**
17695      * Returns a custom data object that is registered for the DOM node that is the target of the event
17696      * @param {Event} e The event
17697      * @return {Object} data The custom data
17698      */
17699         getTargetFromEvent : function(e){
17700             var t = Roo.lib.Event.getTarget(e);
17701             return t ? elements[t.id] || handles[t.id] : null;
17702         }
17703     };
17704 }();/*
17705  * Based on:
17706  * Ext JS Library 1.1.1
17707  * Copyright(c) 2006-2007, Ext JS, LLC.
17708  *
17709  * Originally Released Under LGPL - original licence link has changed is not relivant.
17710  *
17711  * Fork - LGPL
17712  * <script type="text/javascript">
17713  */
17714  
17715
17716 /**
17717  * @class Roo.dd.StatusProxy
17718  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17719  * default drag proxy used by all Roo.dd components.
17720  * @constructor
17721  * @param {Object} config
17722  */
17723 Roo.dd.StatusProxy = function(config){
17724     Roo.apply(this, config);
17725     this.id = this.id || Roo.id();
17726     this.el = new Roo.Layer({
17727         dh: {
17728             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17729                 {tag: "div", cls: "x-dd-drop-icon"},
17730                 {tag: "div", cls: "x-dd-drag-ghost"}
17731             ]
17732         }, 
17733         shadow: !config || config.shadow !== false
17734     });
17735     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17736     this.dropStatus = this.dropNotAllowed;
17737 };
17738
17739 Roo.dd.StatusProxy.prototype = {
17740     /**
17741      * @cfg {String} dropAllowed
17742      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17743      */
17744     dropAllowed : "x-dd-drop-ok",
17745     /**
17746      * @cfg {String} dropNotAllowed
17747      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17748      */
17749     dropNotAllowed : "x-dd-drop-nodrop",
17750
17751     /**
17752      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17753      * over the current target element.
17754      * @param {String} cssClass The css class for the new drop status indicator image
17755      */
17756     setStatus : function(cssClass){
17757         cssClass = cssClass || this.dropNotAllowed;
17758         if(this.dropStatus != cssClass){
17759             this.el.replaceClass(this.dropStatus, cssClass);
17760             this.dropStatus = cssClass;
17761         }
17762     },
17763
17764     /**
17765      * Resets the status indicator to the default dropNotAllowed value
17766      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17767      */
17768     reset : function(clearGhost){
17769         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17770         this.dropStatus = this.dropNotAllowed;
17771         if(clearGhost){
17772             this.ghost.update("");
17773         }
17774     },
17775
17776     /**
17777      * Updates the contents of the ghost element
17778      * @param {String} html The html that will replace the current innerHTML of the ghost element
17779      */
17780     update : function(html){
17781         if(typeof html == "string"){
17782             this.ghost.update(html);
17783         }else{
17784             this.ghost.update("");
17785             html.style.margin = "0";
17786             this.ghost.dom.appendChild(html);
17787         }
17788         // ensure float = none set?? cant remember why though.
17789         var el = this.ghost.dom.firstChild;
17790                 if(el){
17791                         Roo.fly(el).setStyle('float', 'none');
17792                 }
17793     },
17794     
17795     /**
17796      * Returns the underlying proxy {@link Roo.Layer}
17797      * @return {Roo.Layer} el
17798     */
17799     getEl : function(){
17800         return this.el;
17801     },
17802
17803     /**
17804      * Returns the ghost element
17805      * @return {Roo.Element} el
17806      */
17807     getGhost : function(){
17808         return this.ghost;
17809     },
17810
17811     /**
17812      * Hides the proxy
17813      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17814      */
17815     hide : function(clear){
17816         this.el.hide();
17817         if(clear){
17818             this.reset(true);
17819         }
17820     },
17821
17822     /**
17823      * Stops the repair animation if it's currently running
17824      */
17825     stop : function(){
17826         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17827             this.anim.stop();
17828         }
17829     },
17830
17831     /**
17832      * Displays this proxy
17833      */
17834     show : function(){
17835         this.el.show();
17836     },
17837
17838     /**
17839      * Force the Layer to sync its shadow and shim positions to the element
17840      */
17841     sync : function(){
17842         this.el.sync();
17843     },
17844
17845     /**
17846      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17847      * invalid drop operation by the item being dragged.
17848      * @param {Array} xy The XY position of the element ([x, y])
17849      * @param {Function} callback The function to call after the repair is complete
17850      * @param {Object} scope The scope in which to execute the callback
17851      */
17852     repair : function(xy, callback, scope){
17853         this.callback = callback;
17854         this.scope = scope;
17855         if(xy && this.animRepair !== false){
17856             this.el.addClass("x-dd-drag-repair");
17857             this.el.hideUnders(true);
17858             this.anim = this.el.shift({
17859                 duration: this.repairDuration || .5,
17860                 easing: 'easeOut',
17861                 xy: xy,
17862                 stopFx: true,
17863                 callback: this.afterRepair,
17864                 scope: this
17865             });
17866         }else{
17867             this.afterRepair();
17868         }
17869     },
17870
17871     // private
17872     afterRepair : function(){
17873         this.hide(true);
17874         if(typeof this.callback == "function"){
17875             this.callback.call(this.scope || this);
17876         }
17877         this.callback = null;
17878         this.scope = null;
17879     }
17880 };/*
17881  * Based on:
17882  * Ext JS Library 1.1.1
17883  * Copyright(c) 2006-2007, Ext JS, LLC.
17884  *
17885  * Originally Released Under LGPL - original licence link has changed is not relivant.
17886  *
17887  * Fork - LGPL
17888  * <script type="text/javascript">
17889  */
17890
17891 /**
17892  * @class Roo.dd.DragSource
17893  * @extends Roo.dd.DDProxy
17894  * A simple class that provides the basic implementation needed to make any element draggable.
17895  * @constructor
17896  * @param {String/HTMLElement/Element} el The container element
17897  * @param {Object} config
17898  */
17899 Roo.dd.DragSource = function(el, config){
17900     this.el = Roo.get(el);
17901     this.dragData = {};
17902     
17903     Roo.apply(this, config);
17904     
17905     if(!this.proxy){
17906         this.proxy = new Roo.dd.StatusProxy();
17907     }
17908
17909     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17910           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17911     
17912     this.dragging = false;
17913 };
17914
17915 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17916     /**
17917      * @cfg {String} dropAllowed
17918      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17919      */
17920     dropAllowed : "x-dd-drop-ok",
17921     /**
17922      * @cfg {String} dropNotAllowed
17923      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17924      */
17925     dropNotAllowed : "x-dd-drop-nodrop",
17926
17927     /**
17928      * Returns the data object associated with this drag source
17929      * @return {Object} data An object containing arbitrary data
17930      */
17931     getDragData : function(e){
17932         return this.dragData;
17933     },
17934
17935     // private
17936     onDragEnter : function(e, id){
17937         var target = Roo.dd.DragDropMgr.getDDById(id);
17938         this.cachedTarget = target;
17939         if(this.beforeDragEnter(target, e, id) !== false){
17940             if(target.isNotifyTarget){
17941                 var status = target.notifyEnter(this, e, this.dragData);
17942                 this.proxy.setStatus(status);
17943             }else{
17944                 this.proxy.setStatus(this.dropAllowed);
17945             }
17946             
17947             if(this.afterDragEnter){
17948                 /**
17949                  * An empty function by default, but provided so that you can perform a custom action
17950                  * when the dragged item enters the drop target by providing an implementation.
17951                  * @param {Roo.dd.DragDrop} target The drop target
17952                  * @param {Event} e The event object
17953                  * @param {String} id The id of the dragged element
17954                  * @method afterDragEnter
17955                  */
17956                 this.afterDragEnter(target, e, id);
17957             }
17958         }
17959     },
17960
17961     /**
17962      * An empty function by default, but provided so that you can perform a custom action
17963      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17964      * @param {Roo.dd.DragDrop} target The drop target
17965      * @param {Event} e The event object
17966      * @param {String} id The id of the dragged element
17967      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17968      */
17969     beforeDragEnter : function(target, e, id){
17970         return true;
17971     },
17972
17973     // private
17974     alignElWithMouse: function() {
17975         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17976         this.proxy.sync();
17977     },
17978
17979     // private
17980     onDragOver : function(e, id){
17981         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17982         if(this.beforeDragOver(target, e, id) !== false){
17983             if(target.isNotifyTarget){
17984                 var status = target.notifyOver(this, e, this.dragData);
17985                 this.proxy.setStatus(status);
17986             }
17987
17988             if(this.afterDragOver){
17989                 /**
17990                  * An empty function by default, but provided so that you can perform a custom action
17991                  * while the dragged item is over the drop target by providing an implementation.
17992                  * @param {Roo.dd.DragDrop} target The drop target
17993                  * @param {Event} e The event object
17994                  * @param {String} id The id of the dragged element
17995                  * @method afterDragOver
17996                  */
17997                 this.afterDragOver(target, e, id);
17998             }
17999         }
18000     },
18001
18002     /**
18003      * An empty function by default, but provided so that you can perform a custom action
18004      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18005      * @param {Roo.dd.DragDrop} target The drop target
18006      * @param {Event} e The event object
18007      * @param {String} id The id of the dragged element
18008      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18009      */
18010     beforeDragOver : function(target, e, id){
18011         return true;
18012     },
18013
18014     // private
18015     onDragOut : function(e, id){
18016         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18017         if(this.beforeDragOut(target, e, id) !== false){
18018             if(target.isNotifyTarget){
18019                 target.notifyOut(this, e, this.dragData);
18020             }
18021             this.proxy.reset();
18022             if(this.afterDragOut){
18023                 /**
18024                  * An empty function by default, but provided so that you can perform a custom action
18025                  * after the dragged item is dragged out of the target without dropping.
18026                  * @param {Roo.dd.DragDrop} target The drop target
18027                  * @param {Event} e The event object
18028                  * @param {String} id The id of the dragged element
18029                  * @method afterDragOut
18030                  */
18031                 this.afterDragOut(target, e, id);
18032             }
18033         }
18034         this.cachedTarget = null;
18035     },
18036
18037     /**
18038      * An empty function by default, but provided so that you can perform a custom action before the dragged
18039      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18040      * @param {Roo.dd.DragDrop} target The drop target
18041      * @param {Event} e The event object
18042      * @param {String} id The id of the dragged element
18043      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18044      */
18045     beforeDragOut : function(target, e, id){
18046         return true;
18047     },
18048     
18049     // private
18050     onDragDrop : function(e, id){
18051         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18052         if(this.beforeDragDrop(target, e, id) !== false){
18053             if(target.isNotifyTarget){
18054                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18055                     this.onValidDrop(target, e, id);
18056                 }else{
18057                     this.onInvalidDrop(target, e, id);
18058                 }
18059             }else{
18060                 this.onValidDrop(target, e, id);
18061             }
18062             
18063             if(this.afterDragDrop){
18064                 /**
18065                  * An empty function by default, but provided so that you can perform a custom action
18066                  * after a valid drag drop has occurred by providing an implementation.
18067                  * @param {Roo.dd.DragDrop} target The drop target
18068                  * @param {Event} e The event object
18069                  * @param {String} id The id of the dropped element
18070                  * @method afterDragDrop
18071                  */
18072                 this.afterDragDrop(target, e, id);
18073             }
18074         }
18075         delete this.cachedTarget;
18076     },
18077
18078     /**
18079      * An empty function by default, but provided so that you can perform a custom action before the dragged
18080      * item is dropped onto the target and optionally cancel the onDragDrop.
18081      * @param {Roo.dd.DragDrop} target The drop target
18082      * @param {Event} e The event object
18083      * @param {String} id The id of the dragged element
18084      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18085      */
18086     beforeDragDrop : function(target, e, id){
18087         return true;
18088     },
18089
18090     // private
18091     onValidDrop : function(target, e, id){
18092         this.hideProxy();
18093         if(this.afterValidDrop){
18094             /**
18095              * An empty function by default, but provided so that you can perform a custom action
18096              * after a valid drop has occurred by providing an implementation.
18097              * @param {Object} target The target DD 
18098              * @param {Event} e The event object
18099              * @param {String} id The id of the dropped element
18100              * @method afterInvalidDrop
18101              */
18102             this.afterValidDrop(target, e, id);
18103         }
18104     },
18105
18106     // private
18107     getRepairXY : function(e, data){
18108         return this.el.getXY();  
18109     },
18110
18111     // private
18112     onInvalidDrop : function(target, e, id){
18113         this.beforeInvalidDrop(target, e, id);
18114         if(this.cachedTarget){
18115             if(this.cachedTarget.isNotifyTarget){
18116                 this.cachedTarget.notifyOut(this, e, this.dragData);
18117             }
18118             this.cacheTarget = null;
18119         }
18120         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18121
18122         if(this.afterInvalidDrop){
18123             /**
18124              * An empty function by default, but provided so that you can perform a custom action
18125              * after an invalid drop has occurred by providing an implementation.
18126              * @param {Event} e The event object
18127              * @param {String} id The id of the dropped element
18128              * @method afterInvalidDrop
18129              */
18130             this.afterInvalidDrop(e, id);
18131         }
18132     },
18133
18134     // private
18135     afterRepair : function(){
18136         if(Roo.enableFx){
18137             this.el.highlight(this.hlColor || "c3daf9");
18138         }
18139         this.dragging = false;
18140     },
18141
18142     /**
18143      * An empty function by default, but provided so that you can perform a custom action after an invalid
18144      * drop has occurred.
18145      * @param {Roo.dd.DragDrop} target The drop target
18146      * @param {Event} e The event object
18147      * @param {String} id The id of the dragged element
18148      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18149      */
18150     beforeInvalidDrop : function(target, e, id){
18151         return true;
18152     },
18153
18154     // private
18155     handleMouseDown : function(e){
18156         if(this.dragging) {
18157             return;
18158         }
18159         var data = this.getDragData(e);
18160         if(data && this.onBeforeDrag(data, e) !== false){
18161             this.dragData = data;
18162             this.proxy.stop();
18163             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18164         } 
18165     },
18166
18167     /**
18168      * An empty function by default, but provided so that you can perform a custom action before the initial
18169      * drag event begins and optionally cancel it.
18170      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18171      * @param {Event} e The event object
18172      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18173      */
18174     onBeforeDrag : function(data, e){
18175         return true;
18176     },
18177
18178     /**
18179      * An empty function by default, but provided so that you can perform a custom action once the initial
18180      * drag event has begun.  The drag cannot be canceled from this function.
18181      * @param {Number} x The x position of the click on the dragged object
18182      * @param {Number} y The y position of the click on the dragged object
18183      */
18184     onStartDrag : Roo.emptyFn,
18185
18186     // private - YUI override
18187     startDrag : function(x, y){
18188         this.proxy.reset();
18189         this.dragging = true;
18190         this.proxy.update("");
18191         this.onInitDrag(x, y);
18192         this.proxy.show();
18193     },
18194
18195     // private
18196     onInitDrag : function(x, y){
18197         var clone = this.el.dom.cloneNode(true);
18198         clone.id = Roo.id(); // prevent duplicate ids
18199         this.proxy.update(clone);
18200         this.onStartDrag(x, y);
18201         return true;
18202     },
18203
18204     /**
18205      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18206      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18207      */
18208     getProxy : function(){
18209         return this.proxy;  
18210     },
18211
18212     /**
18213      * Hides the drag source's {@link Roo.dd.StatusProxy}
18214      */
18215     hideProxy : function(){
18216         this.proxy.hide();  
18217         this.proxy.reset(true);
18218         this.dragging = false;
18219     },
18220
18221     // private
18222     triggerCacheRefresh : function(){
18223         Roo.dd.DDM.refreshCache(this.groups);
18224     },
18225
18226     // private - override to prevent hiding
18227     b4EndDrag: function(e) {
18228     },
18229
18230     // private - override to prevent moving
18231     endDrag : function(e){
18232         this.onEndDrag(this.dragData, e);
18233     },
18234
18235     // private
18236     onEndDrag : function(data, e){
18237     },
18238     
18239     // private - pin to cursor
18240     autoOffset : function(x, y) {
18241         this.setDelta(-12, -20);
18242     }    
18243 });/*
18244  * Based on:
18245  * Ext JS Library 1.1.1
18246  * Copyright(c) 2006-2007, Ext JS, LLC.
18247  *
18248  * Originally Released Under LGPL - original licence link has changed is not relivant.
18249  *
18250  * Fork - LGPL
18251  * <script type="text/javascript">
18252  */
18253
18254
18255 /**
18256  * @class Roo.dd.DropTarget
18257  * @extends Roo.dd.DDTarget
18258  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18259  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18260  * @constructor
18261  * @param {String/HTMLElement/Element} el The container element
18262  * @param {Object} config
18263  */
18264 Roo.dd.DropTarget = function(el, config){
18265     this.el = Roo.get(el);
18266     
18267     Roo.apply(this, config);
18268     
18269     if(this.containerScroll){
18270         Roo.dd.ScrollManager.register(this.el);
18271     }
18272     this.addEvents( {
18273          /**
18274          * @scope Roo.dd.DropTarget
18275          */
18276          
18277          /**
18278          * @event enter
18279          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18280          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18281          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18282          * 
18283          * IMPORTANT : it should set this.overClass and this.dropAllowed
18284          * 
18285          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18286          * @param {Event} e The event
18287          * @param {Object} data An object containing arbitrary data supplied by the drag source
18288          */
18289         "enter" : true,
18290         
18291          /**
18292          * @event over
18293          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18294          * This method will be called on every mouse movement while the drag source is over the drop target.
18295          * This default implementation simply returns the dropAllowed config value.
18296          * 
18297          * IMPORTANT : it should set this.dropAllowed
18298          * 
18299          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18300          * @param {Event} e The event
18301          * @param {Object} data An object containing arbitrary data supplied by the drag source
18302          
18303          */
18304         "over" : true,
18305         /**
18306          * @event out
18307          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18308          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18309          * overClass (if any) from the drop element.
18310          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18311          * @param {Event} e The event
18312          * @param {Object} data An object containing arbitrary data supplied by the drag source
18313          */
18314          "out" : true,
18315          
18316         /**
18317          * @event drop
18318          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18319          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18320          * implementation that does something to process the drop event and returns true so that the drag source's
18321          * repair action does not run.
18322          * 
18323          * IMPORTANT : it should set this.success
18324          * 
18325          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18326          * @param {Event} e The event
18327          * @param {Object} data An object containing arbitrary data supplied by the drag source
18328         */
18329          "drop" : true
18330     });
18331             
18332      
18333     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18334         this.el.dom, 
18335         this.ddGroup || this.group,
18336         {
18337             isTarget: true,
18338             listeners : config.listeners || {} 
18339            
18340         
18341         }
18342     );
18343
18344 };
18345
18346 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18347     /**
18348      * @cfg {String} overClass
18349      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18350      */
18351      /**
18352      * @cfg {String} ddGroup
18353      * The drag drop group to handle drop events for
18354      */
18355      
18356     /**
18357      * @cfg {String} dropAllowed
18358      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18359      */
18360     dropAllowed : "x-dd-drop-ok",
18361     /**
18362      * @cfg {String} dropNotAllowed
18363      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18364      */
18365     dropNotAllowed : "x-dd-drop-nodrop",
18366     /**
18367      * @cfg {boolean} success
18368      * set this after drop listener.. 
18369      */
18370     success : false,
18371     /**
18372      * @cfg {boolean} valid
18373      * if the drop point is valid for over/enter..
18374      */
18375     valid : false,
18376     // private
18377     isTarget : true,
18378
18379     // private
18380     isNotifyTarget : true,
18381     
18382     /**
18383      * @hide
18384      */
18385     notifyEnter : function(dd, e, data){
18386         this.valid = true;
18387         this.fireEvent('enter', this, dd, e, data);
18388         if(this.overClass){
18389             this.el.addClass(this.overClass);
18390         }
18391         return this.valid ? this.dropAllowed : this.dropNotAllowed;
18392     },
18393
18394     /**
18395      * @hide
18396      */
18397     notifyOver : function(dd, e, data){
18398         this.valid = true;
18399         this.fireEvent('over', this, dd, e, data);
18400         return this.valid ? this.dropAllowed : this.dropNotAllowed;
18401     },
18402
18403     /**
18404      * @hide
18405      */
18406     notifyOut : function(dd, e, data){
18407         this.fireEvent('out', this, dd, e, data);
18408         if(this.overClass){
18409             this.el.removeClass(this.overClass);
18410         }
18411     },
18412
18413     /**
18414      * @hide
18415      */
18416     notifyDrop : function(dd, e, data){
18417         this.success = false;
18418         this.fireEvent('drop', this, dd, e, data);
18419         return this.success;
18420     }
18421 });/*
18422  * Based on:
18423  * Ext JS Library 1.1.1
18424  * Copyright(c) 2006-2007, Ext JS, LLC.
18425  *
18426  * Originally Released Under LGPL - original licence link has changed is not relivant.
18427  *
18428  * Fork - LGPL
18429  * <script type="text/javascript">
18430  */
18431
18432
18433 /**
18434  * @class Roo.dd.DragZone
18435  * @extends Roo.dd.DragSource
18436  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18437  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18438  * @constructor
18439  * @param {String/HTMLElement/Element} el The container element
18440  * @param {Object} config
18441  */
18442 Roo.dd.DragZone = function(el, config){
18443     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18444     if(this.containerScroll){
18445         Roo.dd.ScrollManager.register(this.el);
18446     }
18447 };
18448
18449 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18450     /**
18451      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18452      * for auto scrolling during drag operations.
18453      */
18454     /**
18455      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18456      * method after a failed drop (defaults to "c3daf9" - light blue)
18457      */
18458
18459     /**
18460      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18461      * for a valid target to drag based on the mouse down. Override this method
18462      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18463      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18464      * @param {EventObject} e The mouse down event
18465      * @return {Object} The dragData
18466      */
18467     getDragData : function(e){
18468         return Roo.dd.Registry.getHandleFromEvent(e);
18469     },
18470     
18471     /**
18472      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18473      * this.dragData.ddel
18474      * @param {Number} x The x position of the click on the dragged object
18475      * @param {Number} y The y position of the click on the dragged object
18476      * @return {Boolean} true to continue the drag, false to cancel
18477      */
18478     onInitDrag : function(x, y){
18479         this.proxy.update(this.dragData.ddel.cloneNode(true));
18480         this.onStartDrag(x, y);
18481         return true;
18482     },
18483     
18484     /**
18485      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18486      */
18487     afterRepair : function(){
18488         if(Roo.enableFx){
18489             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18490         }
18491         this.dragging = false;
18492     },
18493
18494     /**
18495      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18496      * the XY of this.dragData.ddel
18497      * @param {EventObject} e The mouse up event
18498      * @return {Array} The xy location (e.g. [100, 200])
18499      */
18500     getRepairXY : function(e){
18501         return Roo.Element.fly(this.dragData.ddel).getXY();  
18502     }
18503 });/*
18504  * Based on:
18505  * Ext JS Library 1.1.1
18506  * Copyright(c) 2006-2007, Ext JS, LLC.
18507  *
18508  * Originally Released Under LGPL - original licence link has changed is not relivant.
18509  *
18510  * Fork - LGPL
18511  * <script type="text/javascript">
18512  */
18513 /**
18514  * @class Roo.dd.DropZone
18515  * @extends Roo.dd.DropTarget
18516  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18517  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18518  * @constructor
18519  * @param {String/HTMLElement/Element} el The container element
18520  * @param {Object} config
18521  */
18522 Roo.dd.DropZone = function(el, config){
18523     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18524 };
18525
18526 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18527     /**
18528      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18529      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18530      * provide your own custom lookup.
18531      * @param {Event} e The event
18532      * @return {Object} data The custom data
18533      */
18534     getTargetFromEvent : function(e){
18535         return Roo.dd.Registry.getTargetFromEvent(e);
18536     },
18537
18538     /**
18539      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18540      * that it has registered.  This method has no default implementation and should be overridden to provide
18541      * node-specific processing if necessary.
18542      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18543      * {@link #getTargetFromEvent} for this node)
18544      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18545      * @param {Event} e The event
18546      * @param {Object} data An object containing arbitrary data supplied by the drag source
18547      */
18548     onNodeEnter : function(n, dd, e, data){
18549         
18550     },
18551
18552     /**
18553      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18554      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18555      * overridden to provide the proper feedback.
18556      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18557      * {@link #getTargetFromEvent} for this node)
18558      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18559      * @param {Event} e The event
18560      * @param {Object} data An object containing arbitrary data supplied by the drag source
18561      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18562      * underlying {@link Roo.dd.StatusProxy} can be updated
18563      */
18564     onNodeOver : function(n, dd, e, data){
18565         return this.dropAllowed;
18566     },
18567
18568     /**
18569      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18570      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18571      * node-specific processing if necessary.
18572      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18573      * {@link #getTargetFromEvent} for this node)
18574      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18575      * @param {Event} e The event
18576      * @param {Object} data An object containing arbitrary data supplied by the drag source
18577      */
18578     onNodeOut : function(n, dd, e, data){
18579         
18580     },
18581
18582     /**
18583      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18584      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18585      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18586      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18587      * {@link #getTargetFromEvent} for this node)
18588      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18589      * @param {Event} e The event
18590      * @param {Object} data An object containing arbitrary data supplied by the drag source
18591      * @return {Boolean} True if the drop was valid, else false
18592      */
18593     onNodeDrop : function(n, dd, e, data){
18594         return false;
18595     },
18596
18597     /**
18598      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18599      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18600      * it should be overridden to provide the proper feedback if necessary.
18601      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18602      * @param {Event} e The event
18603      * @param {Object} data An object containing arbitrary data supplied by the drag source
18604      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18605      * underlying {@link Roo.dd.StatusProxy} can be updated
18606      */
18607     onContainerOver : function(dd, e, data){
18608         return this.dropNotAllowed;
18609     },
18610
18611     /**
18612      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18613      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18614      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18615      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18616      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18617      * @param {Event} e The event
18618      * @param {Object} data An object containing arbitrary data supplied by the drag source
18619      * @return {Boolean} True if the drop was valid, else false
18620      */
18621     onContainerDrop : function(dd, e, data){
18622         return false;
18623     },
18624
18625     /**
18626      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18627      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18628      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18629      * you should override this method and provide a custom implementation.
18630      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18631      * @param {Event} e The event
18632      * @param {Object} data An object containing arbitrary data supplied by the drag source
18633      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18634      * underlying {@link Roo.dd.StatusProxy} can be updated
18635      */
18636     notifyEnter : function(dd, e, data){
18637         return this.dropNotAllowed;
18638     },
18639
18640     /**
18641      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18642      * This method will be called on every mouse movement while the drag source is over the drop zone.
18643      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18644      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18645      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18646      * registered node, it will call {@link #onContainerOver}.
18647      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18648      * @param {Event} e The event
18649      * @param {Object} data An object containing arbitrary data supplied by the drag source
18650      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18651      * underlying {@link Roo.dd.StatusProxy} can be updated
18652      */
18653     notifyOver : function(dd, e, data){
18654         var n = this.getTargetFromEvent(e);
18655         if(!n){ // not over valid drop target
18656             if(this.lastOverNode){
18657                 this.onNodeOut(this.lastOverNode, dd, e, data);
18658                 this.lastOverNode = null;
18659             }
18660             return this.onContainerOver(dd, e, data);
18661         }
18662         if(this.lastOverNode != n){
18663             if(this.lastOverNode){
18664                 this.onNodeOut(this.lastOverNode, dd, e, data);
18665             }
18666             this.onNodeEnter(n, dd, e, data);
18667             this.lastOverNode = n;
18668         }
18669         return this.onNodeOver(n, dd, e, data);
18670     },
18671
18672     /**
18673      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18674      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18675      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18676      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18677      * @param {Event} e The event
18678      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18679      */
18680     notifyOut : function(dd, e, data){
18681         if(this.lastOverNode){
18682             this.onNodeOut(this.lastOverNode, dd, e, data);
18683             this.lastOverNode = null;
18684         }
18685     },
18686
18687     /**
18688      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18689      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18690      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18691      * otherwise it will call {@link #onContainerDrop}.
18692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18693      * @param {Event} e The event
18694      * @param {Object} data An object containing arbitrary data supplied by the drag source
18695      * @return {Boolean} True if the drop was valid, else false
18696      */
18697     notifyDrop : function(dd, e, data){
18698         if(this.lastOverNode){
18699             this.onNodeOut(this.lastOverNode, dd, e, data);
18700             this.lastOverNode = null;
18701         }
18702         var n = this.getTargetFromEvent(e);
18703         return n ?
18704             this.onNodeDrop(n, dd, e, data) :
18705             this.onContainerDrop(dd, e, data);
18706     },
18707
18708     // private
18709     triggerCacheRefresh : function(){
18710         Roo.dd.DDM.refreshCache(this.groups);
18711     }  
18712 });/*
18713  * Based on:
18714  * Ext JS Library 1.1.1
18715  * Copyright(c) 2006-2007, Ext JS, LLC.
18716  *
18717  * Originally Released Under LGPL - original licence link has changed is not relivant.
18718  *
18719  * Fork - LGPL
18720  * <script type="text/javascript">
18721  */
18722
18723
18724 /**
18725  * @class Roo.data.SortTypes
18726  * @singleton
18727  * Defines the default sorting (casting?) comparison functions used when sorting data.
18728  */
18729 Roo.data.SortTypes = {
18730     /**
18731      * Default sort that does nothing
18732      * @param {Mixed} s The value being converted
18733      * @return {Mixed} The comparison value
18734      */
18735     none : function(s){
18736         return s;
18737     },
18738     
18739     /**
18740      * The regular expression used to strip tags
18741      * @type {RegExp}
18742      * @property
18743      */
18744     stripTagsRE : /<\/?[^>]+>/gi,
18745     
18746     /**
18747      * Strips all HTML tags to sort on text only
18748      * @param {Mixed} s The value being converted
18749      * @return {String} The comparison value
18750      */
18751     asText : function(s){
18752         return String(s).replace(this.stripTagsRE, "");
18753     },
18754     
18755     /**
18756      * Strips all HTML tags to sort on text only - Case insensitive
18757      * @param {Mixed} s The value being converted
18758      * @return {String} The comparison value
18759      */
18760     asUCText : function(s){
18761         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18762     },
18763     
18764     /**
18765      * Case insensitive string
18766      * @param {Mixed} s The value being converted
18767      * @return {String} The comparison value
18768      */
18769     asUCString : function(s) {
18770         return String(s).toUpperCase();
18771     },
18772     
18773     /**
18774      * Date sorting
18775      * @param {Mixed} s The value being converted
18776      * @return {Number} The comparison value
18777      */
18778     asDate : function(s) {
18779         if(!s){
18780             return 0;
18781         }
18782         if(s instanceof Date){
18783             return s.getTime();
18784         }
18785         return Date.parse(String(s));
18786     },
18787     
18788     /**
18789      * Float sorting
18790      * @param {Mixed} s The value being converted
18791      * @return {Float} The comparison value
18792      */
18793     asFloat : function(s) {
18794         var val = parseFloat(String(s).replace(/,/g, ""));
18795         if(isNaN(val)) val = 0;
18796         return val;
18797     },
18798     
18799     /**
18800      * Integer sorting
18801      * @param {Mixed} s The value being converted
18802      * @return {Number} The comparison value
18803      */
18804     asInt : function(s) {
18805         var val = parseInt(String(s).replace(/,/g, ""));
18806         if(isNaN(val)) val = 0;
18807         return val;
18808     }
18809 };/*
18810  * Based on:
18811  * Ext JS Library 1.1.1
18812  * Copyright(c) 2006-2007, Ext JS, LLC.
18813  *
18814  * Originally Released Under LGPL - original licence link has changed is not relivant.
18815  *
18816  * Fork - LGPL
18817  * <script type="text/javascript">
18818  */
18819
18820 /**
18821 * @class Roo.data.Record
18822  * Instances of this class encapsulate both record <em>definition</em> information, and record
18823  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18824  * to access Records cached in an {@link Roo.data.Store} object.<br>
18825  * <p>
18826  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18827  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18828  * objects.<br>
18829  * <p>
18830  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18831  * @constructor
18832  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18833  * {@link #create}. The parameters are the same.
18834  * @param {Array} data An associative Array of data values keyed by the field name.
18835  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18836  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18837  * not specified an integer id is generated.
18838  */
18839 Roo.data.Record = function(data, id){
18840     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18841     this.data = data;
18842 };
18843
18844 /**
18845  * Generate a constructor for a specific record layout.
18846  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18847  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18848  * Each field definition object may contain the following properties: <ul>
18849  * <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,
18850  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18851  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18852  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18853  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18854  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18855  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18856  * this may be omitted.</p></li>
18857  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18858  * <ul><li>auto (Default, implies no conversion)</li>
18859  * <li>string</li>
18860  * <li>int</li>
18861  * <li>float</li>
18862  * <li>boolean</li>
18863  * <li>date</li></ul></p></li>
18864  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18865  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18866  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18867  * by the Reader into an object that will be stored in the Record. It is passed the
18868  * following parameters:<ul>
18869  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18870  * </ul></p></li>
18871  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18872  * </ul>
18873  * <br>usage:<br><pre><code>
18874 var TopicRecord = Roo.data.Record.create(
18875     {name: 'title', mapping: 'topic_title'},
18876     {name: 'author', mapping: 'username'},
18877     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18878     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18879     {name: 'lastPoster', mapping: 'user2'},
18880     {name: 'excerpt', mapping: 'post_text'}
18881 );
18882
18883 var myNewRecord = new TopicRecord({
18884     title: 'Do my job please',
18885     author: 'noobie',
18886     totalPosts: 1,
18887     lastPost: new Date(),
18888     lastPoster: 'Animal',
18889     excerpt: 'No way dude!'
18890 });
18891 myStore.add(myNewRecord);
18892 </code></pre>
18893  * @method create
18894  * @static
18895  */
18896 Roo.data.Record.create = function(o){
18897     var f = function(){
18898         f.superclass.constructor.apply(this, arguments);
18899     };
18900     Roo.extend(f, Roo.data.Record);
18901     var p = f.prototype;
18902     p.fields = new Roo.util.MixedCollection(false, function(field){
18903         return field.name;
18904     });
18905     for(var i = 0, len = o.length; i < len; i++){
18906         p.fields.add(new Roo.data.Field(o[i]));
18907     }
18908     f.getField = function(name){
18909         return p.fields.get(name);  
18910     };
18911     return f;
18912 };
18913
18914 Roo.data.Record.AUTO_ID = 1000;
18915 Roo.data.Record.EDIT = 'edit';
18916 Roo.data.Record.REJECT = 'reject';
18917 Roo.data.Record.COMMIT = 'commit';
18918
18919 Roo.data.Record.prototype = {
18920     /**
18921      * Readonly flag - true if this record has been modified.
18922      * @type Boolean
18923      */
18924     dirty : false,
18925     editing : false,
18926     error: null,
18927     modified: null,
18928
18929     // private
18930     join : function(store){
18931         this.store = store;
18932     },
18933
18934     /**
18935      * Set the named field to the specified value.
18936      * @param {String} name The name of the field to set.
18937      * @param {Object} value The value to set the field to.
18938      */
18939     set : function(name, value){
18940         if(this.data[name] == value){
18941             return;
18942         }
18943         this.dirty = true;
18944         if(!this.modified){
18945             this.modified = {};
18946         }
18947         if(typeof this.modified[name] == 'undefined'){
18948             this.modified[name] = this.data[name];
18949         }
18950         this.data[name] = value;
18951         if(!this.editing){
18952             this.store.afterEdit(this);
18953         }       
18954     },
18955
18956     /**
18957      * Get the value of the named field.
18958      * @param {String} name The name of the field to get the value of.
18959      * @return {Object} The value of the field.
18960      */
18961     get : function(name){
18962         return this.data[name]; 
18963     },
18964
18965     // private
18966     beginEdit : function(){
18967         this.editing = true;
18968         this.modified = {}; 
18969     },
18970
18971     // private
18972     cancelEdit : function(){
18973         this.editing = false;
18974         delete this.modified;
18975     },
18976
18977     // private
18978     endEdit : function(){
18979         this.editing = false;
18980         if(this.dirty && this.store){
18981             this.store.afterEdit(this);
18982         }
18983     },
18984
18985     /**
18986      * Usually called by the {@link Roo.data.Store} which owns the Record.
18987      * Rejects all changes made to the Record since either creation, or the last commit operation.
18988      * Modified fields are reverted to their original values.
18989      * <p>
18990      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18991      * of reject operations.
18992      */
18993     reject : function(){
18994         var m = this.modified;
18995         for(var n in m){
18996             if(typeof m[n] != "function"){
18997                 this.data[n] = m[n];
18998             }
18999         }
19000         this.dirty = false;
19001         delete this.modified;
19002         this.editing = false;
19003         if(this.store){
19004             this.store.afterReject(this);
19005         }
19006     },
19007
19008     /**
19009      * Usually called by the {@link Roo.data.Store} which owns the Record.
19010      * Commits all changes made to the Record since either creation, or the last commit operation.
19011      * <p>
19012      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19013      * of commit operations.
19014      */
19015     commit : function(){
19016         this.dirty = false;
19017         delete this.modified;
19018         this.editing = false;
19019         if(this.store){
19020             this.store.afterCommit(this);
19021         }
19022     },
19023
19024     // private
19025     hasError : function(){
19026         return this.error != null;
19027     },
19028
19029     // private
19030     clearError : function(){
19031         this.error = null;
19032     },
19033
19034     /**
19035      * Creates a copy of this record.
19036      * @param {String} id (optional) A new record id if you don't want to use this record's id
19037      * @return {Record}
19038      */
19039     copy : function(newId) {
19040         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19041     }
19042 };/*
19043  * Based on:
19044  * Ext JS Library 1.1.1
19045  * Copyright(c) 2006-2007, Ext JS, LLC.
19046  *
19047  * Originally Released Under LGPL - original licence link has changed is not relivant.
19048  *
19049  * Fork - LGPL
19050  * <script type="text/javascript">
19051  */
19052
19053
19054
19055 /**
19056  * @class Roo.data.Store
19057  * @extends Roo.util.Observable
19058  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19059  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19060  * <p>
19061  * 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
19062  * has no knowledge of the format of the data returned by the Proxy.<br>
19063  * <p>
19064  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19065  * instances from the data object. These records are cached and made available through accessor functions.
19066  * @constructor
19067  * Creates a new Store.
19068  * @param {Object} config A config object containing the objects needed for the Store to access data,
19069  * and read the data into Records.
19070  */
19071 Roo.data.Store = function(config){
19072     this.data = new Roo.util.MixedCollection(false);
19073     this.data.getKey = function(o){
19074         return o.id;
19075     };
19076     this.baseParams = {};
19077     // private
19078     this.paramNames = {
19079         "start" : "start",
19080         "limit" : "limit",
19081         "sort" : "sort",
19082         "dir" : "dir"
19083     };
19084
19085     if(config && config.data){
19086         this.inlineData = config.data;
19087         delete config.data;
19088     }
19089
19090     Roo.apply(this, config);
19091     
19092     if(this.reader){ // reader passed
19093         this.reader = Roo.factory(this.reader, Roo.data);
19094         this.reader.xmodule = this.xmodule || false;
19095         if(!this.recordType){
19096             this.recordType = this.reader.recordType;
19097         }
19098         if(this.reader.onMetaChange){
19099             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19100         }
19101     }
19102
19103     if(this.recordType){
19104         this.fields = this.recordType.prototype.fields;
19105     }
19106     this.modified = [];
19107
19108     this.addEvents({
19109         /**
19110          * @event datachanged
19111          * Fires when the data cache has changed, and a widget which is using this Store
19112          * as a Record cache should refresh its view.
19113          * @param {Store} this
19114          */
19115         datachanged : true,
19116         /**
19117          * @event metachange
19118          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19119          * @param {Store} this
19120          * @param {Object} meta The JSON metadata
19121          */
19122         metachange : true,
19123         /**
19124          * @event add
19125          * Fires when Records have been added to the Store
19126          * @param {Store} this
19127          * @param {Roo.data.Record[]} records The array of Records added
19128          * @param {Number} index The index at which the record(s) were added
19129          */
19130         add : true,
19131         /**
19132          * @event remove
19133          * Fires when a Record has been removed from the Store
19134          * @param {Store} this
19135          * @param {Roo.data.Record} record The Record that was removed
19136          * @param {Number} index The index at which the record was removed
19137          */
19138         remove : true,
19139         /**
19140          * @event update
19141          * Fires when a Record has been updated
19142          * @param {Store} this
19143          * @param {Roo.data.Record} record The Record that was updated
19144          * @param {String} operation The update operation being performed.  Value may be one of:
19145          * <pre><code>
19146  Roo.data.Record.EDIT
19147  Roo.data.Record.REJECT
19148  Roo.data.Record.COMMIT
19149          * </code></pre>
19150          */
19151         update : true,
19152         /**
19153          * @event clear
19154          * Fires when the data cache has been cleared.
19155          * @param {Store} this
19156          */
19157         clear : true,
19158         /**
19159          * @event beforeload
19160          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19161          * the load action will be canceled.
19162          * @param {Store} this
19163          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19164          */
19165         beforeload : true,
19166         /**
19167          * @event load
19168          * Fires after a new set of Records has been loaded.
19169          * @param {Store} this
19170          * @param {Roo.data.Record[]} records The Records that were loaded
19171          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19172          */
19173         load : true,
19174         /**
19175          * @event loadexception
19176          * Fires if an exception occurs in the Proxy during loading.
19177          * Called with the signature of the Proxy's "loadexception" event.
19178          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19179          * 
19180          * @param {Proxy} 
19181          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19182          * @param {Object} load options 
19183          * @param {Object} jsonData from your request (normally this contains the Exception)
19184          */
19185         loadexception : true
19186     });
19187     
19188     if(this.proxy){
19189         this.proxy = Roo.factory(this.proxy, Roo.data);
19190         this.proxy.xmodule = this.xmodule || false;
19191         this.relayEvents(this.proxy,  ["loadexception"]);
19192     }
19193     this.sortToggle = {};
19194
19195     Roo.data.Store.superclass.constructor.call(this);
19196
19197     if(this.inlineData){
19198         this.loadData(this.inlineData);
19199         delete this.inlineData;
19200     }
19201 };
19202 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19203      /**
19204     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19205     * without a remote query - used by combo/forms at present.
19206     */
19207     
19208     /**
19209     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19210     */
19211     /**
19212     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19213     */
19214     /**
19215     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19216     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19217     */
19218     /**
19219     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19220     * on any HTTP request
19221     */
19222     /**
19223     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19224     */
19225     /**
19226     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19227     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19228     */
19229     remoteSort : false,
19230
19231     /**
19232     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19233      * loaded or when a record is removed. (defaults to false).
19234     */
19235     pruneModifiedRecords : false,
19236
19237     // private
19238     lastOptions : null,
19239
19240     /**
19241      * Add Records to the Store and fires the add event.
19242      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19243      */
19244     add : function(records){
19245         records = [].concat(records);
19246         for(var i = 0, len = records.length; i < len; i++){
19247             records[i].join(this);
19248         }
19249         var index = this.data.length;
19250         this.data.addAll(records);
19251         this.fireEvent("add", this, records, index);
19252     },
19253
19254     /**
19255      * Remove a Record from the Store and fires the remove event.
19256      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19257      */
19258     remove : function(record){
19259         var index = this.data.indexOf(record);
19260         this.data.removeAt(index);
19261         if(this.pruneModifiedRecords){
19262             this.modified.remove(record);
19263         }
19264         this.fireEvent("remove", this, record, index);
19265     },
19266
19267     /**
19268      * Remove all Records from the Store and fires the clear event.
19269      */
19270     removeAll : function(){
19271         this.data.clear();
19272         if(this.pruneModifiedRecords){
19273             this.modified = [];
19274         }
19275         this.fireEvent("clear", this);
19276     },
19277
19278     /**
19279      * Inserts Records to the Store at the given index and fires the add event.
19280      * @param {Number} index The start index at which to insert the passed Records.
19281      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19282      */
19283     insert : function(index, records){
19284         records = [].concat(records);
19285         for(var i = 0, len = records.length; i < len; i++){
19286             this.data.insert(index, records[i]);
19287             records[i].join(this);
19288         }
19289         this.fireEvent("add", this, records, index);
19290     },
19291
19292     /**
19293      * Get the index within the cache of the passed Record.
19294      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19295      * @return {Number} The index of the passed Record. Returns -1 if not found.
19296      */
19297     indexOf : function(record){
19298         return this.data.indexOf(record);
19299     },
19300
19301     /**
19302      * Get the index within the cache of the Record with the passed id.
19303      * @param {String} id The id of the Record to find.
19304      * @return {Number} The index of the Record. Returns -1 if not found.
19305      */
19306     indexOfId : function(id){
19307         return this.data.indexOfKey(id);
19308     },
19309
19310     /**
19311      * Get the Record with the specified id.
19312      * @param {String} id The id of the Record to find.
19313      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19314      */
19315     getById : function(id){
19316         return this.data.key(id);
19317     },
19318
19319     /**
19320      * Get the Record at the specified index.
19321      * @param {Number} index The index of the Record to find.
19322      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19323      */
19324     getAt : function(index){
19325         return this.data.itemAt(index);
19326     },
19327
19328     /**
19329      * Returns a range of Records between specified indices.
19330      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19331      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19332      * @return {Roo.data.Record[]} An array of Records
19333      */
19334     getRange : function(start, end){
19335         return this.data.getRange(start, end);
19336     },
19337
19338     // private
19339     storeOptions : function(o){
19340         o = Roo.apply({}, o);
19341         delete o.callback;
19342         delete o.scope;
19343         this.lastOptions = o;
19344     },
19345
19346     /**
19347      * Loads the Record cache from the configured Proxy using the configured Reader.
19348      * <p>
19349      * If using remote paging, then the first load call must specify the <em>start</em>
19350      * and <em>limit</em> properties in the options.params property to establish the initial
19351      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19352      * <p>
19353      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19354      * and this call will return before the new data has been loaded. Perform any post-processing
19355      * in a callback function, or in a "load" event handler.</strong>
19356      * <p>
19357      * @param {Object} options An object containing properties which control loading options:<ul>
19358      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19359      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19360      * passed the following arguments:<ul>
19361      * <li>r : Roo.data.Record[]</li>
19362      * <li>options: Options object from the load call</li>
19363      * <li>success: Boolean success indicator</li></ul></li>
19364      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19365      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19366      * </ul>
19367      */
19368     load : function(options){
19369         options = options || {};
19370         if(this.fireEvent("beforeload", this, options) !== false){
19371             this.storeOptions(options);
19372             var p = Roo.apply(options.params || {}, this.baseParams);
19373             // if meta was not loaded from remote source.. try requesting it.
19374             if (!this.reader.metaFromRemote) {
19375                 p._requestMeta = 1;
19376             }
19377             if(this.sortInfo && this.remoteSort){
19378                 var pn = this.paramNames;
19379                 p[pn["sort"]] = this.sortInfo.field;
19380                 p[pn["dir"]] = this.sortInfo.direction;
19381             }
19382             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19383         }
19384     },
19385
19386     /**
19387      * Reloads the Record cache from the configured Proxy using the configured Reader and
19388      * the options from the last load operation performed.
19389      * @param {Object} options (optional) An object containing properties which may override the options
19390      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19391      * the most recently used options are reused).
19392      */
19393     reload : function(options){
19394         this.load(Roo.applyIf(options||{}, this.lastOptions));
19395     },
19396
19397     // private
19398     // Called as a callback by the Reader during a load operation.
19399     loadRecords : function(o, options, success){
19400         if(!o || success === false){
19401             if(success !== false){
19402                 this.fireEvent("load", this, [], options);
19403             }
19404             if(options.callback){
19405                 options.callback.call(options.scope || this, [], options, false);
19406             }
19407             return;
19408         }
19409         // if data returned failure - throw an exception.
19410         if (o.success === false) {
19411             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19412             return;
19413         }
19414         var r = o.records, t = o.totalRecords || r.length;
19415         if(!options || options.add !== true){
19416             if(this.pruneModifiedRecords){
19417                 this.modified = [];
19418             }
19419             for(var i = 0, len = r.length; i < len; i++){
19420                 r[i].join(this);
19421             }
19422             if(this.snapshot){
19423                 this.data = this.snapshot;
19424                 delete this.snapshot;
19425             }
19426             this.data.clear();
19427             this.data.addAll(r);
19428             this.totalLength = t;
19429             this.applySort();
19430             this.fireEvent("datachanged", this);
19431         }else{
19432             this.totalLength = Math.max(t, this.data.length+r.length);
19433             this.add(r);
19434         }
19435         this.fireEvent("load", this, r, options);
19436         if(options.callback){
19437             options.callback.call(options.scope || this, r, options, true);
19438         }
19439     },
19440
19441     /**
19442      * Loads data from a passed data block. A Reader which understands the format of the data
19443      * must have been configured in the constructor.
19444      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19445      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19446      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19447      */
19448     loadData : function(o, append){
19449         var r = this.reader.readRecords(o);
19450         this.loadRecords(r, {add: append}, true);
19451     },
19452
19453     /**
19454      * Gets the number of cached records.
19455      * <p>
19456      * <em>If using paging, this may not be the total size of the dataset. If the data object
19457      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19458      * the data set size</em>
19459      */
19460     getCount : function(){
19461         return this.data.length || 0;
19462     },
19463
19464     /**
19465      * Gets the total number of records in the dataset as returned by the server.
19466      * <p>
19467      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19468      * the dataset size</em>
19469      */
19470     getTotalCount : function(){
19471         return this.totalLength || 0;
19472     },
19473
19474     /**
19475      * Returns the sort state of the Store as an object with two properties:
19476      * <pre><code>
19477  field {String} The name of the field by which the Records are sorted
19478  direction {String} The sort order, "ASC" or "DESC"
19479      * </code></pre>
19480      */
19481     getSortState : function(){
19482         return this.sortInfo;
19483     },
19484
19485     // private
19486     applySort : function(){
19487         if(this.sortInfo && !this.remoteSort){
19488             var s = this.sortInfo, f = s.field;
19489             var st = this.fields.get(f).sortType;
19490             var fn = function(r1, r2){
19491                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19492                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19493             };
19494             this.data.sort(s.direction, fn);
19495             if(this.snapshot && this.snapshot != this.data){
19496                 this.snapshot.sort(s.direction, fn);
19497             }
19498         }
19499     },
19500
19501     /**
19502      * Sets the default sort column and order to be used by the next load operation.
19503      * @param {String} fieldName The name of the field to sort by.
19504      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19505      */
19506     setDefaultSort : function(field, dir){
19507         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19508     },
19509
19510     /**
19511      * Sort the Records.
19512      * If remote sorting is used, the sort is performed on the server, and the cache is
19513      * reloaded. If local sorting is used, the cache is sorted internally.
19514      * @param {String} fieldName The name of the field to sort by.
19515      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19516      */
19517     sort : function(fieldName, dir){
19518         var f = this.fields.get(fieldName);
19519         if(!dir){
19520             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19521                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19522             }else{
19523                 dir = f.sortDir;
19524             }
19525         }
19526         this.sortToggle[f.name] = dir;
19527         this.sortInfo = {field: f.name, direction: dir};
19528         if(!this.remoteSort){
19529             this.applySort();
19530             this.fireEvent("datachanged", this);
19531         }else{
19532             this.load(this.lastOptions);
19533         }
19534     },
19535
19536     /**
19537      * Calls the specified function for each of the Records in the cache.
19538      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19539      * Returning <em>false</em> aborts and exits the iteration.
19540      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19541      */
19542     each : function(fn, scope){
19543         this.data.each(fn, scope);
19544     },
19545
19546     /**
19547      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19548      * (e.g., during paging).
19549      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19550      */
19551     getModifiedRecords : function(){
19552         return this.modified;
19553     },
19554
19555     // private
19556     createFilterFn : function(property, value, anyMatch){
19557         if(!value.exec){ // not a regex
19558             value = String(value);
19559             if(value.length == 0){
19560                 return false;
19561             }
19562             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19563         }
19564         return function(r){
19565             return value.test(r.data[property]);
19566         };
19567     },
19568
19569     /**
19570      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19571      * @param {String} property A field on your records
19572      * @param {Number} start The record index to start at (defaults to 0)
19573      * @param {Number} end The last record index to include (defaults to length - 1)
19574      * @return {Number} The sum
19575      */
19576     sum : function(property, start, end){
19577         var rs = this.data.items, v = 0;
19578         start = start || 0;
19579         end = (end || end === 0) ? end : rs.length-1;
19580
19581         for(var i = start; i <= end; i++){
19582             v += (rs[i].data[property] || 0);
19583         }
19584         return v;
19585     },
19586
19587     /**
19588      * Filter the records by a specified property.
19589      * @param {String} field A field on your records
19590      * @param {String/RegExp} value Either a string that the field
19591      * should start with or a RegExp to test against the field
19592      * @param {Boolean} anyMatch True to match any part not just the beginning
19593      */
19594     filter : function(property, value, anyMatch){
19595         var fn = this.createFilterFn(property, value, anyMatch);
19596         return fn ? this.filterBy(fn) : this.clearFilter();
19597     },
19598
19599     /**
19600      * Filter by a function. The specified function will be called with each
19601      * record in this data source. If the function returns true the record is included,
19602      * otherwise it is filtered.
19603      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19604      * @param {Object} scope (optional) The scope of the function (defaults to this)
19605      */
19606     filterBy : function(fn, scope){
19607         this.snapshot = this.snapshot || this.data;
19608         this.data = this.queryBy(fn, scope||this);
19609         this.fireEvent("datachanged", this);
19610     },
19611
19612     /**
19613      * Query the records by a specified property.
19614      * @param {String} field A field on your records
19615      * @param {String/RegExp} value Either a string that the field
19616      * should start with or a RegExp to test against the field
19617      * @param {Boolean} anyMatch True to match any part not just the beginning
19618      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19619      */
19620     query : function(property, value, anyMatch){
19621         var fn = this.createFilterFn(property, value, anyMatch);
19622         return fn ? this.queryBy(fn) : this.data.clone();
19623     },
19624
19625     /**
19626      * Query by a function. The specified function will be called with each
19627      * record in this data source. If the function returns true the record is included
19628      * in the results.
19629      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19630      * @param {Object} scope (optional) The scope of the function (defaults to this)
19631       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19632      **/
19633     queryBy : function(fn, scope){
19634         var data = this.snapshot || this.data;
19635         return data.filterBy(fn, scope||this);
19636     },
19637
19638     /**
19639      * Collects unique values for a particular dataIndex from this store.
19640      * @param {String} dataIndex The property to collect
19641      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19642      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19643      * @return {Array} An array of the unique values
19644      **/
19645     collect : function(dataIndex, allowNull, bypassFilter){
19646         var d = (bypassFilter === true && this.snapshot) ?
19647                 this.snapshot.items : this.data.items;
19648         var v, sv, r = [], l = {};
19649         for(var i = 0, len = d.length; i < len; i++){
19650             v = d[i].data[dataIndex];
19651             sv = String(v);
19652             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19653                 l[sv] = true;
19654                 r[r.length] = v;
19655             }
19656         }
19657         return r;
19658     },
19659
19660     /**
19661      * Revert to a view of the Record cache with no filtering applied.
19662      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19663      */
19664     clearFilter : function(suppressEvent){
19665         if(this.snapshot && this.snapshot != this.data){
19666             this.data = this.snapshot;
19667             delete this.snapshot;
19668             if(suppressEvent !== true){
19669                 this.fireEvent("datachanged", this);
19670             }
19671         }
19672     },
19673
19674     // private
19675     afterEdit : function(record){
19676         if(this.modified.indexOf(record) == -1){
19677             this.modified.push(record);
19678         }
19679         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19680     },
19681
19682     // private
19683     afterReject : function(record){
19684         this.modified.remove(record);
19685         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19686     },
19687
19688     // private
19689     afterCommit : function(record){
19690         this.modified.remove(record);
19691         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19692     },
19693
19694     /**
19695      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19696      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19697      */
19698     commitChanges : function(){
19699         var m = this.modified.slice(0);
19700         this.modified = [];
19701         for(var i = 0, len = m.length; i < len; i++){
19702             m[i].commit();
19703         }
19704     },
19705
19706     /**
19707      * Cancel outstanding changes on all changed records.
19708      */
19709     rejectChanges : function(){
19710         var m = this.modified.slice(0);
19711         this.modified = [];
19712         for(var i = 0, len = m.length; i < len; i++){
19713             m[i].reject();
19714         }
19715     },
19716
19717     onMetaChange : function(meta, rtype, o){
19718         this.recordType = rtype;
19719         this.fields = rtype.prototype.fields;
19720         delete this.snapshot;
19721         this.sortInfo = meta.sortInfo || this.sortInfo;
19722         this.modified = [];
19723         this.fireEvent('metachange', this, this.reader.meta);
19724     }
19725 });/*
19726  * Based on:
19727  * Ext JS Library 1.1.1
19728  * Copyright(c) 2006-2007, Ext JS, LLC.
19729  *
19730  * Originally Released Under LGPL - original licence link has changed is not relivant.
19731  *
19732  * Fork - LGPL
19733  * <script type="text/javascript">
19734  */
19735
19736 /**
19737  * @class Roo.data.SimpleStore
19738  * @extends Roo.data.Store
19739  * Small helper class to make creating Stores from Array data easier.
19740  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19741  * @cfg {Array} fields An array of field definition objects, or field name strings.
19742  * @cfg {Array} data The multi-dimensional array of data
19743  * @constructor
19744  * @param {Object} config
19745  */
19746 Roo.data.SimpleStore = function(config){
19747     Roo.data.SimpleStore.superclass.constructor.call(this, {
19748         isLocal : true,
19749         reader: new Roo.data.ArrayReader({
19750                 id: config.id
19751             },
19752             Roo.data.Record.create(config.fields)
19753         ),
19754         proxy : new Roo.data.MemoryProxy(config.data)
19755     });
19756     this.load();
19757 };
19758 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19759  * Based on:
19760  * Ext JS Library 1.1.1
19761  * Copyright(c) 2006-2007, Ext JS, LLC.
19762  *
19763  * Originally Released Under LGPL - original licence link has changed is not relivant.
19764  *
19765  * Fork - LGPL
19766  * <script type="text/javascript">
19767  */
19768
19769 /**
19770 /**
19771  * @extends Roo.data.Store
19772  * @class Roo.data.JsonStore
19773  * Small helper class to make creating Stores for JSON data easier. <br/>
19774 <pre><code>
19775 var store = new Roo.data.JsonStore({
19776     url: 'get-images.php',
19777     root: 'images',
19778     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19779 });
19780 </code></pre>
19781  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19782  * JsonReader and HttpProxy (unless inline data is provided).</b>
19783  * @cfg {Array} fields An array of field definition objects, or field name strings.
19784  * @constructor
19785  * @param {Object} config
19786  */
19787 Roo.data.JsonStore = function(c){
19788     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19789         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19790         reader: new Roo.data.JsonReader(c, c.fields)
19791     }));
19792 };
19793 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19794  * Based on:
19795  * Ext JS Library 1.1.1
19796  * Copyright(c) 2006-2007, Ext JS, LLC.
19797  *
19798  * Originally Released Under LGPL - original licence link has changed is not relivant.
19799  *
19800  * Fork - LGPL
19801  * <script type="text/javascript">
19802  */
19803
19804  
19805 Roo.data.Field = function(config){
19806     if(typeof config == "string"){
19807         config = {name: config};
19808     }
19809     Roo.apply(this, config);
19810     
19811     if(!this.type){
19812         this.type = "auto";
19813     }
19814     
19815     var st = Roo.data.SortTypes;
19816     // named sortTypes are supported, here we look them up
19817     if(typeof this.sortType == "string"){
19818         this.sortType = st[this.sortType];
19819     }
19820     
19821     // set default sortType for strings and dates
19822     if(!this.sortType){
19823         switch(this.type){
19824             case "string":
19825                 this.sortType = st.asUCString;
19826                 break;
19827             case "date":
19828                 this.sortType = st.asDate;
19829                 break;
19830             default:
19831                 this.sortType = st.none;
19832         }
19833     }
19834
19835     // define once
19836     var stripRe = /[\$,%]/g;
19837
19838     // prebuilt conversion function for this field, instead of
19839     // switching every time we're reading a value
19840     if(!this.convert){
19841         var cv, dateFormat = this.dateFormat;
19842         switch(this.type){
19843             case "":
19844             case "auto":
19845             case undefined:
19846                 cv = function(v){ return v; };
19847                 break;
19848             case "string":
19849                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19850                 break;
19851             case "int":
19852                 cv = function(v){
19853                     return v !== undefined && v !== null && v !== '' ?
19854                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19855                     };
19856                 break;
19857             case "float":
19858                 cv = function(v){
19859                     return v !== undefined && v !== null && v !== '' ?
19860                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19861                     };
19862                 break;
19863             case "bool":
19864             case "boolean":
19865                 cv = function(v){ return v === true || v === "true" || v == 1; };
19866                 break;
19867             case "date":
19868                 cv = function(v){
19869                     if(!v){
19870                         return '';
19871                     }
19872                     if(v instanceof Date){
19873                         return v;
19874                     }
19875                     if(dateFormat){
19876                         if(dateFormat == "timestamp"){
19877                             return new Date(v*1000);
19878                         }
19879                         return Date.parseDate(v, dateFormat);
19880                     }
19881                     var parsed = Date.parse(v);
19882                     return parsed ? new Date(parsed) : null;
19883                 };
19884              break;
19885             
19886         }
19887         this.convert = cv;
19888     }
19889 };
19890
19891 Roo.data.Field.prototype = {
19892     dateFormat: null,
19893     defaultValue: "",
19894     mapping: null,
19895     sortType : null,
19896     sortDir : "ASC"
19897 };/*
19898  * Based on:
19899  * Ext JS Library 1.1.1
19900  * Copyright(c) 2006-2007, Ext JS, LLC.
19901  *
19902  * Originally Released Under LGPL - original licence link has changed is not relivant.
19903  *
19904  * Fork - LGPL
19905  * <script type="text/javascript">
19906  */
19907  
19908 // Base class for reading structured data from a data source.  This class is intended to be
19909 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19910
19911 /**
19912  * @class Roo.data.DataReader
19913  * Base class for reading structured data from a data source.  This class is intended to be
19914  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19915  */
19916
19917 Roo.data.DataReader = function(meta, recordType){
19918     
19919     this.meta = meta;
19920     
19921     this.recordType = recordType instanceof Array ? 
19922         Roo.data.Record.create(recordType) : recordType;
19923 };
19924
19925 Roo.data.DataReader.prototype = {
19926      /**
19927      * Create an empty record
19928      * @param {Object} data (optional) - overlay some values
19929      * @return {Roo.data.Record} record created.
19930      */
19931     newRow :  function(d) {
19932         var da =  {};
19933         this.recordType.prototype.fields.each(function(c) {
19934             switch( c.type) {
19935                 case 'int' : da[c.name] = 0; break;
19936                 case 'date' : da[c.name] = new Date(); break;
19937                 case 'float' : da[c.name] = 0.0; break;
19938                 case 'boolean' : da[c.name] = false; break;
19939                 default : da[c.name] = ""; break;
19940             }
19941             
19942         });
19943         return new this.recordType(Roo.apply(da, d));
19944     }
19945     
19946 };/*
19947  * Based on:
19948  * Ext JS Library 1.1.1
19949  * Copyright(c) 2006-2007, Ext JS, LLC.
19950  *
19951  * Originally Released Under LGPL - original licence link has changed is not relivant.
19952  *
19953  * Fork - LGPL
19954  * <script type="text/javascript">
19955  */
19956
19957 /**
19958  * @class Roo.data.DataProxy
19959  * @extends Roo.data.Observable
19960  * This class is an abstract base class for implementations which provide retrieval of
19961  * unformatted data objects.<br>
19962  * <p>
19963  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19964  * (of the appropriate type which knows how to parse the data object) to provide a block of
19965  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19966  * <p>
19967  * Custom implementations must implement the load method as described in
19968  * {@link Roo.data.HttpProxy#load}.
19969  */
19970 Roo.data.DataProxy = function(){
19971     this.addEvents({
19972         /**
19973          * @event beforeload
19974          * Fires before a network request is made to retrieve a data object.
19975          * @param {Object} This DataProxy object.
19976          * @param {Object} params The params parameter to the load function.
19977          */
19978         beforeload : true,
19979         /**
19980          * @event load
19981          * Fires before the load method's callback is called.
19982          * @param {Object} This DataProxy object.
19983          * @param {Object} o The data object.
19984          * @param {Object} arg The callback argument object passed to the load function.
19985          */
19986         load : true,
19987         /**
19988          * @event loadexception
19989          * Fires if an Exception occurs during data retrieval.
19990          * @param {Object} This DataProxy object.
19991          * @param {Object} o The data object.
19992          * @param {Object} arg The callback argument object passed to the load function.
19993          * @param {Object} e The Exception.
19994          */
19995         loadexception : true
19996     });
19997     Roo.data.DataProxy.superclass.constructor.call(this);
19998 };
19999
20000 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20001
20002     /**
20003      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20004      */
20005 /*
20006  * Based on:
20007  * Ext JS Library 1.1.1
20008  * Copyright(c) 2006-2007, Ext JS, LLC.
20009  *
20010  * Originally Released Under LGPL - original licence link has changed is not relivant.
20011  *
20012  * Fork - LGPL
20013  * <script type="text/javascript">
20014  */
20015 /**
20016  * @class Roo.data.MemoryProxy
20017  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20018  * to the Reader when its load method is called.
20019  * @constructor
20020  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20021  */
20022 Roo.data.MemoryProxy = function(data){
20023     if (data.data) {
20024         data = data.data;
20025     }
20026     Roo.data.MemoryProxy.superclass.constructor.call(this);
20027     this.data = data;
20028 };
20029
20030 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20031     /**
20032      * Load data from the requested source (in this case an in-memory
20033      * data object passed to the constructor), read the data object into
20034      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20035      * process that block using the passed callback.
20036      * @param {Object} params This parameter is not used by the MemoryProxy class.
20037      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20038      * object into a block of Roo.data.Records.
20039      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20040      * The function must be passed <ul>
20041      * <li>The Record block object</li>
20042      * <li>The "arg" argument from the load function</li>
20043      * <li>A boolean success indicator</li>
20044      * </ul>
20045      * @param {Object} scope The scope in which to call the callback
20046      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20047      */
20048     load : function(params, reader, callback, scope, arg){
20049         params = params || {};
20050         var result;
20051         try {
20052             result = reader.readRecords(this.data);
20053         }catch(e){
20054             this.fireEvent("loadexception", this, arg, null, e);
20055             callback.call(scope, null, arg, false);
20056             return;
20057         }
20058         callback.call(scope, result, arg, true);
20059     },
20060     
20061     // private
20062     update : function(params, records){
20063         
20064     }
20065 });/*
20066  * Based on:
20067  * Ext JS Library 1.1.1
20068  * Copyright(c) 2006-2007, Ext JS, LLC.
20069  *
20070  * Originally Released Under LGPL - original licence link has changed is not relivant.
20071  *
20072  * Fork - LGPL
20073  * <script type="text/javascript">
20074  */
20075 /**
20076  * @class Roo.data.HttpProxy
20077  * @extends Roo.data.DataProxy
20078  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20079  * configured to reference a certain URL.<br><br>
20080  * <p>
20081  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20082  * from which the running page was served.<br><br>
20083  * <p>
20084  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20085  * <p>
20086  * Be aware that to enable the browser to parse an XML document, the server must set
20087  * the Content-Type header in the HTTP response to "text/xml".
20088  * @constructor
20089  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20090  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20091  * will be used to make the request.
20092  */
20093 Roo.data.HttpProxy = function(conn){
20094     Roo.data.HttpProxy.superclass.constructor.call(this);
20095     // is conn a conn config or a real conn?
20096     this.conn = conn;
20097     this.useAjax = !conn || !conn.events;
20098   
20099 };
20100
20101 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20102     // thse are take from connection...
20103     
20104     /**
20105      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20106      */
20107     /**
20108      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20109      * extra parameters to each request made by this object. (defaults to undefined)
20110      */
20111     /**
20112      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20113      *  to each request made by this object. (defaults to undefined)
20114      */
20115     /**
20116      * @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)
20117      */
20118     /**
20119      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20120      */
20121      /**
20122      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20123      * @type Boolean
20124      */
20125   
20126
20127     /**
20128      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20129      * @type Boolean
20130      */
20131     /**
20132      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20133      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20134      * a finer-grained basis than the DataProxy events.
20135      */
20136     getConnection : function(){
20137         return this.useAjax ? Roo.Ajax : this.conn;
20138     },
20139
20140     /**
20141      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20142      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20143      * process that block using the passed callback.
20144      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20145      * for the request to the remote server.
20146      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20147      * object into a block of Roo.data.Records.
20148      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20149      * The function must be passed <ul>
20150      * <li>The Record block object</li>
20151      * <li>The "arg" argument from the load function</li>
20152      * <li>A boolean success indicator</li>
20153      * </ul>
20154      * @param {Object} scope The scope in which to call the callback
20155      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20156      */
20157     load : function(params, reader, callback, scope, arg){
20158         if(this.fireEvent("beforeload", this, params) !== false){
20159             var  o = {
20160                 params : params || {},
20161                 request: {
20162                     callback : callback,
20163                     scope : scope,
20164                     arg : arg
20165                 },
20166                 reader: reader,
20167                 callback : this.loadResponse,
20168                 scope: this
20169             };
20170             if(this.useAjax){
20171                 Roo.applyIf(o, this.conn);
20172                 if(this.activeRequest){
20173                     Roo.Ajax.abort(this.activeRequest);
20174                 }
20175                 this.activeRequest = Roo.Ajax.request(o);
20176             }else{
20177                 this.conn.request(o);
20178             }
20179         }else{
20180             callback.call(scope||this, null, arg, false);
20181         }
20182     },
20183
20184     // private
20185     loadResponse : function(o, success, response){
20186         delete this.activeRequest;
20187         if(!success){
20188             this.fireEvent("loadexception", this, o, response);
20189             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20190             return;
20191         }
20192         var result;
20193         try {
20194             result = o.reader.read(response);
20195         }catch(e){
20196             this.fireEvent("loadexception", this, o, response, e);
20197             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20198             return;
20199         }
20200         
20201         this.fireEvent("load", this, o, o.request.arg);
20202         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20203     },
20204
20205     // private
20206     update : function(dataSet){
20207
20208     },
20209
20210     // private
20211     updateResponse : function(dataSet){
20212
20213     }
20214 });/*
20215  * Based on:
20216  * Ext JS Library 1.1.1
20217  * Copyright(c) 2006-2007, Ext JS, LLC.
20218  *
20219  * Originally Released Under LGPL - original licence link has changed is not relivant.
20220  *
20221  * Fork - LGPL
20222  * <script type="text/javascript">
20223  */
20224
20225 /**
20226  * @class Roo.data.ScriptTagProxy
20227  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20228  * other than the originating domain of the running page.<br><br>
20229  * <p>
20230  * <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
20231  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20232  * <p>
20233  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20234  * source code that is used as the source inside a &lt;script> tag.<br><br>
20235  * <p>
20236  * In order for the browser to process the returned data, the server must wrap the data object
20237  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20238  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20239  * depending on whether the callback name was passed:
20240  * <p>
20241  * <pre><code>
20242 boolean scriptTag = false;
20243 String cb = request.getParameter("callback");
20244 if (cb != null) {
20245     scriptTag = true;
20246     response.setContentType("text/javascript");
20247 } else {
20248     response.setContentType("application/x-json");
20249 }
20250 Writer out = response.getWriter();
20251 if (scriptTag) {
20252     out.write(cb + "(");
20253 }
20254 out.print(dataBlock.toJsonString());
20255 if (scriptTag) {
20256     out.write(");");
20257 }
20258 </pre></code>
20259  *
20260  * @constructor
20261  * @param {Object} config A configuration object.
20262  */
20263 Roo.data.ScriptTagProxy = function(config){
20264     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20265     Roo.apply(this, config);
20266     this.head = document.getElementsByTagName("head")[0];
20267 };
20268
20269 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20270
20271 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20272     /**
20273      * @cfg {String} url The URL from which to request the data object.
20274      */
20275     /**
20276      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20277      */
20278     timeout : 30000,
20279     /**
20280      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20281      * the server the name of the callback function set up by the load call to process the returned data object.
20282      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20283      * javascript output which calls this named function passing the data object as its only parameter.
20284      */
20285     callbackParam : "callback",
20286     /**
20287      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20288      * name to the request.
20289      */
20290     nocache : true,
20291
20292     /**
20293      * Load data from the configured URL, read the data object into
20294      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20295      * process that block using the passed callback.
20296      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20297      * for the request to the remote server.
20298      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20299      * object into a block of Roo.data.Records.
20300      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20301      * The function must be passed <ul>
20302      * <li>The Record block object</li>
20303      * <li>The "arg" argument from the load function</li>
20304      * <li>A boolean success indicator</li>
20305      * </ul>
20306      * @param {Object} scope The scope in which to call the callback
20307      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20308      */
20309     load : function(params, reader, callback, scope, arg){
20310         if(this.fireEvent("beforeload", this, params) !== false){
20311
20312             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20313
20314             var url = this.url;
20315             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20316             if(this.nocache){
20317                 url += "&_dc=" + (new Date().getTime());
20318             }
20319             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20320             var trans = {
20321                 id : transId,
20322                 cb : "stcCallback"+transId,
20323                 scriptId : "stcScript"+transId,
20324                 params : params,
20325                 arg : arg,
20326                 url : url,
20327                 callback : callback,
20328                 scope : scope,
20329                 reader : reader
20330             };
20331             var conn = this;
20332
20333             window[trans.cb] = function(o){
20334                 conn.handleResponse(o, trans);
20335             };
20336
20337             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20338
20339             if(this.autoAbort !== false){
20340                 this.abort();
20341             }
20342
20343             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20344
20345             var script = document.createElement("script");
20346             script.setAttribute("src", url);
20347             script.setAttribute("type", "text/javascript");
20348             script.setAttribute("id", trans.scriptId);
20349             this.head.appendChild(script);
20350
20351             this.trans = trans;
20352         }else{
20353             callback.call(scope||this, null, arg, false);
20354         }
20355     },
20356
20357     // private
20358     isLoading : function(){
20359         return this.trans ? true : false;
20360     },
20361
20362     /**
20363      * Abort the current server request.
20364      */
20365     abort : function(){
20366         if(this.isLoading()){
20367             this.destroyTrans(this.trans);
20368         }
20369     },
20370
20371     // private
20372     destroyTrans : function(trans, isLoaded){
20373         this.head.removeChild(document.getElementById(trans.scriptId));
20374         clearTimeout(trans.timeoutId);
20375         if(isLoaded){
20376             window[trans.cb] = undefined;
20377             try{
20378                 delete window[trans.cb];
20379             }catch(e){}
20380         }else{
20381             // if hasn't been loaded, wait for load to remove it to prevent script error
20382             window[trans.cb] = function(){
20383                 window[trans.cb] = undefined;
20384                 try{
20385                     delete window[trans.cb];
20386                 }catch(e){}
20387             };
20388         }
20389     },
20390
20391     // private
20392     handleResponse : function(o, trans){
20393         this.trans = false;
20394         this.destroyTrans(trans, true);
20395         var result;
20396         try {
20397             result = trans.reader.readRecords(o);
20398         }catch(e){
20399             this.fireEvent("loadexception", this, o, trans.arg, e);
20400             trans.callback.call(trans.scope||window, null, trans.arg, false);
20401             return;
20402         }
20403         this.fireEvent("load", this, o, trans.arg);
20404         trans.callback.call(trans.scope||window, result, trans.arg, true);
20405     },
20406
20407     // private
20408     handleFailure : function(trans){
20409         this.trans = false;
20410         this.destroyTrans(trans, false);
20411         this.fireEvent("loadexception", this, null, trans.arg);
20412         trans.callback.call(trans.scope||window, null, trans.arg, false);
20413     }
20414 });/*
20415  * Based on:
20416  * Ext JS Library 1.1.1
20417  * Copyright(c) 2006-2007, Ext JS, LLC.
20418  *
20419  * Originally Released Under LGPL - original licence link has changed is not relivant.
20420  *
20421  * Fork - LGPL
20422  * <script type="text/javascript">
20423  */
20424
20425 /**
20426  * @class Roo.data.JsonReader
20427  * @extends Roo.data.DataReader
20428  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20429  * based on mappings in a provided Roo.data.Record constructor.
20430  * 
20431  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20432  * in the reply previously. 
20433  * 
20434  * <p>
20435  * Example code:
20436  * <pre><code>
20437 var RecordDef = Roo.data.Record.create([
20438     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20439     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20440 ]);
20441 var myReader = new Roo.data.JsonReader({
20442     totalProperty: "results",    // The property which contains the total dataset size (optional)
20443     root: "rows",                // The property which contains an Array of row objects
20444     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20445 }, RecordDef);
20446 </code></pre>
20447  * <p>
20448  * This would consume a JSON file like this:
20449  * <pre><code>
20450 { 'results': 2, 'rows': [
20451     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20452     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20453 }
20454 </code></pre>
20455  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20456  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20457  * paged from the remote server.
20458  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20459  * @cfg {String} root name of the property which contains the Array of row objects.
20460  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20461  * @constructor
20462  * Create a new JsonReader
20463  * @param {Object} meta Metadata configuration options
20464  * @param {Object} recordType Either an Array of field definition objects,
20465  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20466  */
20467 Roo.data.JsonReader = function(meta, recordType){
20468     
20469     meta = meta || {};
20470     // set some defaults:
20471     Roo.applyIf(meta, {
20472         totalProperty: 'total',
20473         successProperty : 'success',
20474         root : 'data',
20475         id : 'id'
20476     });
20477     
20478     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20479 };
20480 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20481     
20482     /**
20483      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20484      * Used by Store query builder to append _requestMeta to params.
20485      * 
20486      */
20487     metaFromRemote : false,
20488     /**
20489      * This method is only used by a DataProxy which has retrieved data from a remote server.
20490      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20491      * @return {Object} data A data block which is used by an Roo.data.Store object as
20492      * a cache of Roo.data.Records.
20493      */
20494     read : function(response){
20495         var json = response.responseText;
20496        
20497         var o = /* eval:var:o */ eval("("+json+")");
20498         if(!o) {
20499             throw {message: "JsonReader.read: Json object not found"};
20500         }
20501         
20502         if(o.metaData){
20503             
20504             delete this.ef;
20505             this.metaFromRemote = true;
20506             this.meta = o.metaData;
20507             this.recordType = Roo.data.Record.create(o.metaData.fields);
20508             this.onMetaChange(this.meta, this.recordType, o);
20509         }
20510         return this.readRecords(o);
20511     },
20512
20513     // private function a store will implement
20514     onMetaChange : function(meta, recordType, o){
20515
20516     },
20517
20518     /**
20519          * @ignore
20520          */
20521     simpleAccess: function(obj, subsc) {
20522         return obj[subsc];
20523     },
20524
20525         /**
20526          * @ignore
20527          */
20528     getJsonAccessor: function(){
20529         var re = /[\[\.]/;
20530         return function(expr) {
20531             try {
20532                 return(re.test(expr))
20533                     ? new Function("obj", "return obj." + expr)
20534                     : function(obj){
20535                         return obj[expr];
20536                     };
20537             } catch(e){}
20538             return Roo.emptyFn;
20539         };
20540     }(),
20541
20542     /**
20543      * Create a data block containing Roo.data.Records from an XML document.
20544      * @param {Object} o An object which contains an Array of row objects in the property specified
20545      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20546      * which contains the total size of the dataset.
20547      * @return {Object} data A data block which is used by an Roo.data.Store object as
20548      * a cache of Roo.data.Records.
20549      */
20550     readRecords : function(o){
20551         /**
20552          * After any data loads, the raw JSON data is available for further custom processing.
20553          * @type Object
20554          */
20555         this.jsonData = o;
20556         var s = this.meta, Record = this.recordType,
20557             f = Record.prototype.fields, fi = f.items, fl = f.length;
20558
20559 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20560         if (!this.ef) {
20561             if(s.totalProperty) {
20562                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20563                 }
20564                 if(s.successProperty) {
20565                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20566                 }
20567                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20568                 if (s.id) {
20569                         var g = this.getJsonAccessor(s.id);
20570                         this.getId = function(rec) {
20571                                 var r = g(rec);
20572                                 return (r === undefined || r === "") ? null : r;
20573                         };
20574                 } else {
20575                         this.getId = function(){return null;};
20576                 }
20577             this.ef = [];
20578             for(var jj = 0; jj < fl; jj++){
20579                 f = fi[jj];
20580                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20581                 this.ef[jj] = this.getJsonAccessor(map);
20582             }
20583         }
20584
20585         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20586         if(s.totalProperty){
20587             var vt = parseInt(this.getTotal(o), 10);
20588             if(!isNaN(vt)){
20589                 totalRecords = vt;
20590             }
20591         }
20592         if(s.successProperty){
20593             var vs = this.getSuccess(o);
20594             if(vs === false || vs === 'false'){
20595                 success = false;
20596             }
20597         }
20598         var records = [];
20599             for(var i = 0; i < c; i++){
20600                     var n = root[i];
20601                 var values = {};
20602                 var id = this.getId(n);
20603                 for(var j = 0; j < fl; j++){
20604                     f = fi[j];
20605                 var v = this.ef[j](n);
20606                 if (!f.convert) {
20607                     Roo.log('missing convert for ' + f.name);
20608                     Roo.log(f);
20609                     continue;
20610                 }
20611                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20612                 }
20613                 var record = new Record(values, id);
20614                 record.json = n;
20615                 records[i] = record;
20616             }
20617             return {
20618                 success : success,
20619                 records : records,
20620                 totalRecords : totalRecords
20621             };
20622     }
20623 });/*
20624  * Based on:
20625  * Ext JS Library 1.1.1
20626  * Copyright(c) 2006-2007, Ext JS, LLC.
20627  *
20628  * Originally Released Under LGPL - original licence link has changed is not relivant.
20629  *
20630  * Fork - LGPL
20631  * <script type="text/javascript">
20632  */
20633
20634 /**
20635  * @class Roo.data.XmlReader
20636  * @extends Roo.data.DataReader
20637  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20638  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20639  * <p>
20640  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20641  * header in the HTTP response must be set to "text/xml".</em>
20642  * <p>
20643  * Example code:
20644  * <pre><code>
20645 var RecordDef = Roo.data.Record.create([
20646    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20647    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20648 ]);
20649 var myReader = new Roo.data.XmlReader({
20650    totalRecords: "results", // The element which contains the total dataset size (optional)
20651    record: "row",           // The repeated element which contains row information
20652    id: "id"                 // The element within the row that provides an ID for the record (optional)
20653 }, RecordDef);
20654 </code></pre>
20655  * <p>
20656  * This would consume an XML file like this:
20657  * <pre><code>
20658 &lt;?xml?>
20659 &lt;dataset>
20660  &lt;results>2&lt;/results>
20661  &lt;row>
20662    &lt;id>1&lt;/id>
20663    &lt;name>Bill&lt;/name>
20664    &lt;occupation>Gardener&lt;/occupation>
20665  &lt;/row>
20666  &lt;row>
20667    &lt;id>2&lt;/id>
20668    &lt;name>Ben&lt;/name>
20669    &lt;occupation>Horticulturalist&lt;/occupation>
20670  &lt;/row>
20671 &lt;/dataset>
20672 </code></pre>
20673  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20674  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20675  * paged from the remote server.
20676  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20677  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20678  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20679  * a record identifier value.
20680  * @constructor
20681  * Create a new XmlReader
20682  * @param {Object} meta Metadata configuration options
20683  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20684  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20685  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20686  */
20687 Roo.data.XmlReader = function(meta, recordType){
20688     meta = meta || {};
20689     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20690 };
20691 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20692     /**
20693      * This method is only used by a DataProxy which has retrieved data from a remote server.
20694          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20695          * to contain a method called 'responseXML' that returns an XML document object.
20696      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20697      * a cache of Roo.data.Records.
20698      */
20699     read : function(response){
20700         var doc = response.responseXML;
20701         if(!doc) {
20702             throw {message: "XmlReader.read: XML Document not available"};
20703         }
20704         return this.readRecords(doc);
20705     },
20706
20707     /**
20708      * Create a data block containing Roo.data.Records from an XML document.
20709          * @param {Object} doc A parsed XML document.
20710      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20711      * a cache of Roo.data.Records.
20712      */
20713     readRecords : function(doc){
20714         /**
20715          * After any data loads/reads, the raw XML Document is available for further custom processing.
20716          * @type XMLDocument
20717          */
20718         this.xmlData = doc;
20719         var root = doc.documentElement || doc;
20720         var q = Roo.DomQuery;
20721         var recordType = this.recordType, fields = recordType.prototype.fields;
20722         var sid = this.meta.id;
20723         var totalRecords = 0, success = true;
20724         if(this.meta.totalRecords){
20725             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20726         }
20727         
20728         if(this.meta.success){
20729             var sv = q.selectValue(this.meta.success, root, true);
20730             success = sv !== false && sv !== 'false';
20731         }
20732         var records = [];
20733         var ns = q.select(this.meta.record, root);
20734         for(var i = 0, len = ns.length; i < len; i++) {
20735                 var n = ns[i];
20736                 var values = {};
20737                 var id = sid ? q.selectValue(sid, n) : undefined;
20738                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20739                     var f = fields.items[j];
20740                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20741                     v = f.convert(v);
20742                     values[f.name] = v;
20743                 }
20744                 var record = new recordType(values, id);
20745                 record.node = n;
20746                 records[records.length] = record;
20747             }
20748
20749             return {
20750                 success : success,
20751                 records : records,
20752                 totalRecords : totalRecords || records.length
20753             };
20754     }
20755 });/*
20756  * Based on:
20757  * Ext JS Library 1.1.1
20758  * Copyright(c) 2006-2007, Ext JS, LLC.
20759  *
20760  * Originally Released Under LGPL - original licence link has changed is not relivant.
20761  *
20762  * Fork - LGPL
20763  * <script type="text/javascript">
20764  */
20765
20766 /**
20767  * @class Roo.data.ArrayReader
20768  * @extends Roo.data.DataReader
20769  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20770  * Each element of that Array represents a row of data fields. The
20771  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20772  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20773  * <p>
20774  * Example code:.
20775  * <pre><code>
20776 var RecordDef = Roo.data.Record.create([
20777     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20778     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20779 ]);
20780 var myReader = new Roo.data.ArrayReader({
20781     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20782 }, RecordDef);
20783 </code></pre>
20784  * <p>
20785  * This would consume an Array like this:
20786  * <pre><code>
20787 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20788   </code></pre>
20789  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20790  * @constructor
20791  * Create a new JsonReader
20792  * @param {Object} meta Metadata configuration options.
20793  * @param {Object} recordType Either an Array of field definition objects
20794  * as specified to {@link Roo.data.Record#create},
20795  * or an {@link Roo.data.Record} object
20796  * created using {@link Roo.data.Record#create}.
20797  */
20798 Roo.data.ArrayReader = function(meta, recordType){
20799     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20800 };
20801
20802 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20803     /**
20804      * Create a data block containing Roo.data.Records from an XML document.
20805      * @param {Object} o An Array of row objects which represents the dataset.
20806      * @return {Object} data A data block which is used by an Roo.data.Store object as
20807      * a cache of Roo.data.Records.
20808      */
20809     readRecords : function(o){
20810         var sid = this.meta ? this.meta.id : null;
20811         var recordType = this.recordType, fields = recordType.prototype.fields;
20812         var records = [];
20813         var root = o;
20814             for(var i = 0; i < root.length; i++){
20815                     var n = root[i];
20816                 var values = {};
20817                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20818                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20819                 var f = fields.items[j];
20820                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20821                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20822                 v = f.convert(v);
20823                 values[f.name] = v;
20824             }
20825                 var record = new recordType(values, id);
20826                 record.json = n;
20827                 records[records.length] = record;
20828             }
20829             return {
20830                 records : records,
20831                 totalRecords : records.length
20832             };
20833     }
20834 });/*
20835  * Based on:
20836  * Ext JS Library 1.1.1
20837  * Copyright(c) 2006-2007, Ext JS, LLC.
20838  *
20839  * Originally Released Under LGPL - original licence link has changed is not relivant.
20840  *
20841  * Fork - LGPL
20842  * <script type="text/javascript">
20843  */
20844
20845
20846 /**
20847  * @class Roo.data.Tree
20848  * @extends Roo.util.Observable
20849  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20850  * in the tree have most standard DOM functionality.
20851  * @constructor
20852  * @param {Node} root (optional) The root node
20853  */
20854 Roo.data.Tree = function(root){
20855    this.nodeHash = {};
20856    /**
20857     * The root node for this tree
20858     * @type Node
20859     */
20860    this.root = null;
20861    if(root){
20862        this.setRootNode(root);
20863    }
20864    this.addEvents({
20865        /**
20866         * @event append
20867         * Fires when a new child node is appended to a node in this tree.
20868         * @param {Tree} tree The owner tree
20869         * @param {Node} parent The parent node
20870         * @param {Node} node The newly appended node
20871         * @param {Number} index The index of the newly appended node
20872         */
20873        "append" : true,
20874        /**
20875         * @event remove
20876         * Fires when a child node is removed from a node in this tree.
20877         * @param {Tree} tree The owner tree
20878         * @param {Node} parent The parent node
20879         * @param {Node} node The child node removed
20880         */
20881        "remove" : true,
20882        /**
20883         * @event move
20884         * Fires when a node is moved to a new location in the tree
20885         * @param {Tree} tree The owner tree
20886         * @param {Node} node The node moved
20887         * @param {Node} oldParent The old parent of this node
20888         * @param {Node} newParent The new parent of this node
20889         * @param {Number} index The index it was moved to
20890         */
20891        "move" : true,
20892        /**
20893         * @event insert
20894         * Fires when a new child node is inserted in a node in this tree.
20895         * @param {Tree} tree The owner tree
20896         * @param {Node} parent The parent node
20897         * @param {Node} node The child node inserted
20898         * @param {Node} refNode The child node the node was inserted before
20899         */
20900        "insert" : true,
20901        /**
20902         * @event beforeappend
20903         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20904         * @param {Tree} tree The owner tree
20905         * @param {Node} parent The parent node
20906         * @param {Node} node The child node to be appended
20907         */
20908        "beforeappend" : true,
20909        /**
20910         * @event beforeremove
20911         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20912         * @param {Tree} tree The owner tree
20913         * @param {Node} parent The parent node
20914         * @param {Node} node The child node to be removed
20915         */
20916        "beforeremove" : true,
20917        /**
20918         * @event beforemove
20919         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20920         * @param {Tree} tree The owner tree
20921         * @param {Node} node The node being moved
20922         * @param {Node} oldParent The parent of the node
20923         * @param {Node} newParent The new parent the node is moving to
20924         * @param {Number} index The index it is being moved to
20925         */
20926        "beforemove" : true,
20927        /**
20928         * @event beforeinsert
20929         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20930         * @param {Tree} tree The owner tree
20931         * @param {Node} parent The parent node
20932         * @param {Node} node The child node to be inserted
20933         * @param {Node} refNode The child node the node is being inserted before
20934         */
20935        "beforeinsert" : true
20936    });
20937
20938     Roo.data.Tree.superclass.constructor.call(this);
20939 };
20940
20941 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20942     pathSeparator: "/",
20943
20944     proxyNodeEvent : function(){
20945         return this.fireEvent.apply(this, arguments);
20946     },
20947
20948     /**
20949      * Returns the root node for this tree.
20950      * @return {Node}
20951      */
20952     getRootNode : function(){
20953         return this.root;
20954     },
20955
20956     /**
20957      * Sets the root node for this tree.
20958      * @param {Node} node
20959      * @return {Node}
20960      */
20961     setRootNode : function(node){
20962         this.root = node;
20963         node.ownerTree = this;
20964         node.isRoot = true;
20965         this.registerNode(node);
20966         return node;
20967     },
20968
20969     /**
20970      * Gets a node in this tree by its id.
20971      * @param {String} id
20972      * @return {Node}
20973      */
20974     getNodeById : function(id){
20975         return this.nodeHash[id];
20976     },
20977
20978     registerNode : function(node){
20979         this.nodeHash[node.id] = node;
20980     },
20981
20982     unregisterNode : function(node){
20983         delete this.nodeHash[node.id];
20984     },
20985
20986     toString : function(){
20987         return "[Tree"+(this.id?" "+this.id:"")+"]";
20988     }
20989 });
20990
20991 /**
20992  * @class Roo.data.Node
20993  * @extends Roo.util.Observable
20994  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20995  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20996  * @constructor
20997  * @param {Object} attributes The attributes/config for the node
20998  */
20999 Roo.data.Node = function(attributes){
21000     /**
21001      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21002      * @type {Object}
21003      */
21004     this.attributes = attributes || {};
21005     this.leaf = this.attributes.leaf;
21006     /**
21007      * The node id. @type String
21008      */
21009     this.id = this.attributes.id;
21010     if(!this.id){
21011         this.id = Roo.id(null, "ynode-");
21012         this.attributes.id = this.id;
21013     }
21014     /**
21015      * All child nodes of this node. @type Array
21016      */
21017     this.childNodes = [];
21018     if(!this.childNodes.indexOf){ // indexOf is a must
21019         this.childNodes.indexOf = function(o){
21020             for(var i = 0, len = this.length; i < len; i++){
21021                 if(this[i] == o) {
21022                     return i;
21023                 }
21024             }
21025             return -1;
21026         };
21027     }
21028     /**
21029      * The parent node for this node. @type Node
21030      */
21031     this.parentNode = null;
21032     /**
21033      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21034      */
21035     this.firstChild = null;
21036     /**
21037      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21038      */
21039     this.lastChild = null;
21040     /**
21041      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21042      */
21043     this.previousSibling = null;
21044     /**
21045      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21046      */
21047     this.nextSibling = null;
21048
21049     this.addEvents({
21050        /**
21051         * @event append
21052         * Fires when a new child node is appended
21053         * @param {Tree} tree The owner tree
21054         * @param {Node} this This node
21055         * @param {Node} node The newly appended node
21056         * @param {Number} index The index of the newly appended node
21057         */
21058        "append" : true,
21059        /**
21060         * @event remove
21061         * Fires when a child node is removed
21062         * @param {Tree} tree The owner tree
21063         * @param {Node} this This node
21064         * @param {Node} node The removed node
21065         */
21066        "remove" : true,
21067        /**
21068         * @event move
21069         * Fires when this node is moved to a new location in the tree
21070         * @param {Tree} tree The owner tree
21071         * @param {Node} this This node
21072         * @param {Node} oldParent The old parent of this node
21073         * @param {Node} newParent The new parent of this node
21074         * @param {Number} index The index it was moved to
21075         */
21076        "move" : true,
21077        /**
21078         * @event insert
21079         * Fires when a new child node is inserted.
21080         * @param {Tree} tree The owner tree
21081         * @param {Node} this This node
21082         * @param {Node} node The child node inserted
21083         * @param {Node} refNode The child node the node was inserted before
21084         */
21085        "insert" : true,
21086        /**
21087         * @event beforeappend
21088         * Fires before a new child is appended, return false to cancel the append.
21089         * @param {Tree} tree The owner tree
21090         * @param {Node} this This node
21091         * @param {Node} node The child node to be appended
21092         */
21093        "beforeappend" : true,
21094        /**
21095         * @event beforeremove
21096         * Fires before a child is removed, return false to cancel the remove.
21097         * @param {Tree} tree The owner tree
21098         * @param {Node} this This node
21099         * @param {Node} node The child node to be removed
21100         */
21101        "beforeremove" : true,
21102        /**
21103         * @event beforemove
21104         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21105         * @param {Tree} tree The owner tree
21106         * @param {Node} this This node
21107         * @param {Node} oldParent The parent of this node
21108         * @param {Node} newParent The new parent this node is moving to
21109         * @param {Number} index The index it is being moved to
21110         */
21111        "beforemove" : true,
21112        /**
21113         * @event beforeinsert
21114         * Fires before a new child is inserted, return false to cancel the insert.
21115         * @param {Tree} tree The owner tree
21116         * @param {Node} this This node
21117         * @param {Node} node The child node to be inserted
21118         * @param {Node} refNode The child node the node is being inserted before
21119         */
21120        "beforeinsert" : true
21121    });
21122     this.listeners = this.attributes.listeners;
21123     Roo.data.Node.superclass.constructor.call(this);
21124 };
21125
21126 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21127     fireEvent : function(evtName){
21128         // first do standard event for this node
21129         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21130             return false;
21131         }
21132         // then bubble it up to the tree if the event wasn't cancelled
21133         var ot = this.getOwnerTree();
21134         if(ot){
21135             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21136                 return false;
21137             }
21138         }
21139         return true;
21140     },
21141
21142     /**
21143      * Returns true if this node is a leaf
21144      * @return {Boolean}
21145      */
21146     isLeaf : function(){
21147         return this.leaf === true;
21148     },
21149
21150     // private
21151     setFirstChild : function(node){
21152         this.firstChild = node;
21153     },
21154
21155     //private
21156     setLastChild : function(node){
21157         this.lastChild = node;
21158     },
21159
21160
21161     /**
21162      * Returns true if this node is the last child of its parent
21163      * @return {Boolean}
21164      */
21165     isLast : function(){
21166        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21167     },
21168
21169     /**
21170      * Returns true if this node is the first child of its parent
21171      * @return {Boolean}
21172      */
21173     isFirst : function(){
21174        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21175     },
21176
21177     hasChildNodes : function(){
21178         return !this.isLeaf() && this.childNodes.length > 0;
21179     },
21180
21181     /**
21182      * Insert node(s) as the last child node of this node.
21183      * @param {Node/Array} node The node or Array of nodes to append
21184      * @return {Node} The appended node if single append, or null if an array was passed
21185      */
21186     appendChild : function(node){
21187         var multi = false;
21188         if(node instanceof Array){
21189             multi = node;
21190         }else if(arguments.length > 1){
21191             multi = arguments;
21192         }
21193         // if passed an array or multiple args do them one by one
21194         if(multi){
21195             for(var i = 0, len = multi.length; i < len; i++) {
21196                 this.appendChild(multi[i]);
21197             }
21198         }else{
21199             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21200                 return false;
21201             }
21202             var index = this.childNodes.length;
21203             var oldParent = node.parentNode;
21204             // it's a move, make sure we move it cleanly
21205             if(oldParent){
21206                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21207                     return false;
21208                 }
21209                 oldParent.removeChild(node);
21210             }
21211             index = this.childNodes.length;
21212             if(index == 0){
21213                 this.setFirstChild(node);
21214             }
21215             this.childNodes.push(node);
21216             node.parentNode = this;
21217             var ps = this.childNodes[index-1];
21218             if(ps){
21219                 node.previousSibling = ps;
21220                 ps.nextSibling = node;
21221             }else{
21222                 node.previousSibling = null;
21223             }
21224             node.nextSibling = null;
21225             this.setLastChild(node);
21226             node.setOwnerTree(this.getOwnerTree());
21227             this.fireEvent("append", this.ownerTree, this, node, index);
21228             if(oldParent){
21229                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21230             }
21231             return node;
21232         }
21233     },
21234
21235     /**
21236      * Removes a child node from this node.
21237      * @param {Node} node The node to remove
21238      * @return {Node} The removed node
21239      */
21240     removeChild : function(node){
21241         var index = this.childNodes.indexOf(node);
21242         if(index == -1){
21243             return false;
21244         }
21245         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21246             return false;
21247         }
21248
21249         // remove it from childNodes collection
21250         this.childNodes.splice(index, 1);
21251
21252         // update siblings
21253         if(node.previousSibling){
21254             node.previousSibling.nextSibling = node.nextSibling;
21255         }
21256         if(node.nextSibling){
21257             node.nextSibling.previousSibling = node.previousSibling;
21258         }
21259
21260         // update child refs
21261         if(this.firstChild == node){
21262             this.setFirstChild(node.nextSibling);
21263         }
21264         if(this.lastChild == node){
21265             this.setLastChild(node.previousSibling);
21266         }
21267
21268         node.setOwnerTree(null);
21269         // clear any references from the node
21270         node.parentNode = null;
21271         node.previousSibling = null;
21272         node.nextSibling = null;
21273         this.fireEvent("remove", this.ownerTree, this, node);
21274         return node;
21275     },
21276
21277     /**
21278      * Inserts the first node before the second node in this nodes childNodes collection.
21279      * @param {Node} node The node to insert
21280      * @param {Node} refNode The node to insert before (if null the node is appended)
21281      * @return {Node} The inserted node
21282      */
21283     insertBefore : function(node, refNode){
21284         if(!refNode){ // like standard Dom, refNode can be null for append
21285             return this.appendChild(node);
21286         }
21287         // nothing to do
21288         if(node == refNode){
21289             return false;
21290         }
21291
21292         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21293             return false;
21294         }
21295         var index = this.childNodes.indexOf(refNode);
21296         var oldParent = node.parentNode;
21297         var refIndex = index;
21298
21299         // when moving internally, indexes will change after remove
21300         if(oldParent == this && this.childNodes.indexOf(node) < index){
21301             refIndex--;
21302         }
21303
21304         // it's a move, make sure we move it cleanly
21305         if(oldParent){
21306             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21307                 return false;
21308             }
21309             oldParent.removeChild(node);
21310         }
21311         if(refIndex == 0){
21312             this.setFirstChild(node);
21313         }
21314         this.childNodes.splice(refIndex, 0, node);
21315         node.parentNode = this;
21316         var ps = this.childNodes[refIndex-1];
21317         if(ps){
21318             node.previousSibling = ps;
21319             ps.nextSibling = node;
21320         }else{
21321             node.previousSibling = null;
21322         }
21323         node.nextSibling = refNode;
21324         refNode.previousSibling = node;
21325         node.setOwnerTree(this.getOwnerTree());
21326         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21327         if(oldParent){
21328             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21329         }
21330         return node;
21331     },
21332
21333     /**
21334      * Returns the child node at the specified index.
21335      * @param {Number} index
21336      * @return {Node}
21337      */
21338     item : function(index){
21339         return this.childNodes[index];
21340     },
21341
21342     /**
21343      * Replaces one child node in this node with another.
21344      * @param {Node} newChild The replacement node
21345      * @param {Node} oldChild The node to replace
21346      * @return {Node} The replaced node
21347      */
21348     replaceChild : function(newChild, oldChild){
21349         this.insertBefore(newChild, oldChild);
21350         this.removeChild(oldChild);
21351         return oldChild;
21352     },
21353
21354     /**
21355      * Returns the index of a child node
21356      * @param {Node} node
21357      * @return {Number} The index of the node or -1 if it was not found
21358      */
21359     indexOf : function(child){
21360         return this.childNodes.indexOf(child);
21361     },
21362
21363     /**
21364      * Returns the tree this node is in.
21365      * @return {Tree}
21366      */
21367     getOwnerTree : function(){
21368         // if it doesn't have one, look for one
21369         if(!this.ownerTree){
21370             var p = this;
21371             while(p){
21372                 if(p.ownerTree){
21373                     this.ownerTree = p.ownerTree;
21374                     break;
21375                 }
21376                 p = p.parentNode;
21377             }
21378         }
21379         return this.ownerTree;
21380     },
21381
21382     /**
21383      * Returns depth of this node (the root node has a depth of 0)
21384      * @return {Number}
21385      */
21386     getDepth : function(){
21387         var depth = 0;
21388         var p = this;
21389         while(p.parentNode){
21390             ++depth;
21391             p = p.parentNode;
21392         }
21393         return depth;
21394     },
21395
21396     // private
21397     setOwnerTree : function(tree){
21398         // if it's move, we need to update everyone
21399         if(tree != this.ownerTree){
21400             if(this.ownerTree){
21401                 this.ownerTree.unregisterNode(this);
21402             }
21403             this.ownerTree = tree;
21404             var cs = this.childNodes;
21405             for(var i = 0, len = cs.length; i < len; i++) {
21406                 cs[i].setOwnerTree(tree);
21407             }
21408             if(tree){
21409                 tree.registerNode(this);
21410             }
21411         }
21412     },
21413
21414     /**
21415      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21416      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21417      * @return {String} The path
21418      */
21419     getPath : function(attr){
21420         attr = attr || "id";
21421         var p = this.parentNode;
21422         var b = [this.attributes[attr]];
21423         while(p){
21424             b.unshift(p.attributes[attr]);
21425             p = p.parentNode;
21426         }
21427         var sep = this.getOwnerTree().pathSeparator;
21428         return sep + b.join(sep);
21429     },
21430
21431     /**
21432      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21433      * function call will be the scope provided or the current node. The arguments to the function
21434      * will be the args provided or the current node. If the function returns false at any point,
21435      * the bubble is stopped.
21436      * @param {Function} fn The function to call
21437      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21438      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21439      */
21440     bubble : function(fn, scope, args){
21441         var p = this;
21442         while(p){
21443             if(fn.call(scope || p, args || p) === false){
21444                 break;
21445             }
21446             p = p.parentNode;
21447         }
21448     },
21449
21450     /**
21451      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21452      * function call will be the scope provided or the current node. The arguments to the function
21453      * will be the args provided or the current node. If the function returns false at any point,
21454      * the cascade is stopped on that branch.
21455      * @param {Function} fn The function to call
21456      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21457      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21458      */
21459     cascade : function(fn, scope, args){
21460         if(fn.call(scope || this, args || this) !== false){
21461             var cs = this.childNodes;
21462             for(var i = 0, len = cs.length; i < len; i++) {
21463                 cs[i].cascade(fn, scope, args);
21464             }
21465         }
21466     },
21467
21468     /**
21469      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21470      * function call will be the scope provided or the current node. The arguments to the function
21471      * will be the args provided or the current node. If the function returns false at any point,
21472      * the iteration stops.
21473      * @param {Function} fn The function to call
21474      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21475      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21476      */
21477     eachChild : function(fn, scope, args){
21478         var cs = this.childNodes;
21479         for(var i = 0, len = cs.length; i < len; i++) {
21480                 if(fn.call(scope || this, args || cs[i]) === false){
21481                     break;
21482                 }
21483         }
21484     },
21485
21486     /**
21487      * Finds the first child that has the attribute with the specified value.
21488      * @param {String} attribute The attribute name
21489      * @param {Mixed} value The value to search for
21490      * @return {Node} The found child or null if none was found
21491      */
21492     findChild : function(attribute, value){
21493         var cs = this.childNodes;
21494         for(var i = 0, len = cs.length; i < len; i++) {
21495                 if(cs[i].attributes[attribute] == value){
21496                     return cs[i];
21497                 }
21498         }
21499         return null;
21500     },
21501
21502     /**
21503      * Finds the first child by a custom function. The child matches if the function passed
21504      * returns true.
21505      * @param {Function} fn
21506      * @param {Object} scope (optional)
21507      * @return {Node} The found child or null if none was found
21508      */
21509     findChildBy : function(fn, scope){
21510         var cs = this.childNodes;
21511         for(var i = 0, len = cs.length; i < len; i++) {
21512                 if(fn.call(scope||cs[i], cs[i]) === true){
21513                     return cs[i];
21514                 }
21515         }
21516         return null;
21517     },
21518
21519     /**
21520      * Sorts this nodes children using the supplied sort function
21521      * @param {Function} fn
21522      * @param {Object} scope (optional)
21523      */
21524     sort : function(fn, scope){
21525         var cs = this.childNodes;
21526         var len = cs.length;
21527         if(len > 0){
21528             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21529             cs.sort(sortFn);
21530             for(var i = 0; i < len; i++){
21531                 var n = cs[i];
21532                 n.previousSibling = cs[i-1];
21533                 n.nextSibling = cs[i+1];
21534                 if(i == 0){
21535                     this.setFirstChild(n);
21536                 }
21537                 if(i == len-1){
21538                     this.setLastChild(n);
21539                 }
21540             }
21541         }
21542     },
21543
21544     /**
21545      * Returns true if this node is an ancestor (at any point) of the passed node.
21546      * @param {Node} node
21547      * @return {Boolean}
21548      */
21549     contains : function(node){
21550         return node.isAncestor(this);
21551     },
21552
21553     /**
21554      * Returns true if the passed node is an ancestor (at any point) of this node.
21555      * @param {Node} node
21556      * @return {Boolean}
21557      */
21558     isAncestor : function(node){
21559         var p = this.parentNode;
21560         while(p){
21561             if(p == node){
21562                 return true;
21563             }
21564             p = p.parentNode;
21565         }
21566         return false;
21567     },
21568
21569     toString : function(){
21570         return "[Node"+(this.id?" "+this.id:"")+"]";
21571     }
21572 });/*
21573  * Based on:
21574  * Ext JS Library 1.1.1
21575  * Copyright(c) 2006-2007, Ext JS, LLC.
21576  *
21577  * Originally Released Under LGPL - original licence link has changed is not relivant.
21578  *
21579  * Fork - LGPL
21580  * <script type="text/javascript">
21581  */
21582  
21583
21584 /**
21585  * @class Roo.ComponentMgr
21586  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21587  * @singleton
21588  */
21589 Roo.ComponentMgr = function(){
21590     var all = new Roo.util.MixedCollection();
21591
21592     return {
21593         /**
21594          * Registers a component.
21595          * @param {Roo.Component} c The component
21596          */
21597         register : function(c){
21598             all.add(c);
21599         },
21600
21601         /**
21602          * Unregisters a component.
21603          * @param {Roo.Component} c The component
21604          */
21605         unregister : function(c){
21606             all.remove(c);
21607         },
21608
21609         /**
21610          * Returns a component by id
21611          * @param {String} id The component id
21612          */
21613         get : function(id){
21614             return all.get(id);
21615         },
21616
21617         /**
21618          * Registers a function that will be called when a specified component is added to ComponentMgr
21619          * @param {String} id The component id
21620          * @param {Funtction} fn The callback function
21621          * @param {Object} scope The scope of the callback
21622          */
21623         onAvailable : function(id, fn, scope){
21624             all.on("add", function(index, o){
21625                 if(o.id == id){
21626                     fn.call(scope || o, o);
21627                     all.un("add", fn, scope);
21628                 }
21629             });
21630         }
21631     };
21632 }();/*
21633  * Based on:
21634  * Ext JS Library 1.1.1
21635  * Copyright(c) 2006-2007, Ext JS, LLC.
21636  *
21637  * Originally Released Under LGPL - original licence link has changed is not relivant.
21638  *
21639  * Fork - LGPL
21640  * <script type="text/javascript">
21641  */
21642  
21643 /**
21644  * @class Roo.Component
21645  * @extends Roo.util.Observable
21646  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21647  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21648  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21649  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21650  * All visual components (widgets) that require rendering into a layout should subclass Component.
21651  * @constructor
21652  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21653  * 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
21654  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21655  */
21656 Roo.Component = function(config){
21657     config = config || {};
21658     if(config.tagName || config.dom || typeof config == "string"){ // element object
21659         config = {el: config, id: config.id || config};
21660     }
21661     this.initialConfig = config;
21662
21663     Roo.apply(this, config);
21664     this.addEvents({
21665         /**
21666          * @event disable
21667          * Fires after the component is disabled.
21668              * @param {Roo.Component} this
21669              */
21670         disable : true,
21671         /**
21672          * @event enable
21673          * Fires after the component is enabled.
21674              * @param {Roo.Component} this
21675              */
21676         enable : true,
21677         /**
21678          * @event beforeshow
21679          * Fires before the component is shown.  Return false to stop the show.
21680              * @param {Roo.Component} this
21681              */
21682         beforeshow : true,
21683         /**
21684          * @event show
21685          * Fires after the component is shown.
21686              * @param {Roo.Component} this
21687              */
21688         show : true,
21689         /**
21690          * @event beforehide
21691          * Fires before the component is hidden. Return false to stop the hide.
21692              * @param {Roo.Component} this
21693              */
21694         beforehide : true,
21695         /**
21696          * @event hide
21697          * Fires after the component is hidden.
21698              * @param {Roo.Component} this
21699              */
21700         hide : true,
21701         /**
21702          * @event beforerender
21703          * Fires before the component is rendered. Return false to stop the render.
21704              * @param {Roo.Component} this
21705              */
21706         beforerender : true,
21707         /**
21708          * @event render
21709          * Fires after the component is rendered.
21710              * @param {Roo.Component} this
21711              */
21712         render : true,
21713         /**
21714          * @event beforedestroy
21715          * Fires before the component is destroyed. Return false to stop the destroy.
21716              * @param {Roo.Component} this
21717              */
21718         beforedestroy : true,
21719         /**
21720          * @event destroy
21721          * Fires after the component is destroyed.
21722              * @param {Roo.Component} this
21723              */
21724         destroy : true
21725     });
21726     if(!this.id){
21727         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21728     }
21729     Roo.ComponentMgr.register(this);
21730     Roo.Component.superclass.constructor.call(this);
21731     this.initComponent();
21732     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21733         this.render(this.renderTo);
21734         delete this.renderTo;
21735     }
21736 };
21737
21738 // private
21739 Roo.Component.AUTO_ID = 1000;
21740
21741 Roo.extend(Roo.Component, Roo.util.Observable, {
21742     /**
21743      * @property {Boolean} hidden
21744      * true if this component is hidden. Read-only.
21745      */
21746     hidden : false,
21747     /**
21748      * true if this component is disabled. Read-only.
21749      */
21750     disabled : false,
21751     /**
21752      * true if this component has been rendered. Read-only.
21753      */
21754     rendered : false,
21755     
21756     /** @cfg {String} disableClass
21757      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21758      */
21759     disabledClass : "x-item-disabled",
21760         /** @cfg {Boolean} allowDomMove
21761          * Whether the component can move the Dom node when rendering (defaults to true).
21762          */
21763     allowDomMove : true,
21764     /** @cfg {String} hideMode
21765      * How this component should hidden. Supported values are
21766      * "visibility" (css visibility), "offsets" (negative offset position) and
21767      * "display" (css display) - defaults to "display".
21768      */
21769     hideMode: 'display',
21770
21771     // private
21772     ctype : "Roo.Component",
21773
21774     /** @cfg {String} actionMode 
21775      * which property holds the element that used for  hide() / show() / disable() / enable()
21776      * default is 'el' 
21777      */
21778     actionMode : "el",
21779
21780     // private
21781     getActionEl : function(){
21782         return this[this.actionMode];
21783     },
21784
21785     initComponent : Roo.emptyFn,
21786     /**
21787      * If this is a lazy rendering component, render it to its container element.
21788      * @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.
21789      */
21790     render : function(container, position){
21791         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21792             if(!container && this.el){
21793                 this.el = Roo.get(this.el);
21794                 container = this.el.dom.parentNode;
21795                 this.allowDomMove = false;
21796             }
21797             this.container = Roo.get(container);
21798             this.rendered = true;
21799             if(position !== undefined){
21800                 if(typeof position == 'number'){
21801                     position = this.container.dom.childNodes[position];
21802                 }else{
21803                     position = Roo.getDom(position);
21804                 }
21805             }
21806             this.onRender(this.container, position || null);
21807             if(this.cls){
21808                 this.el.addClass(this.cls);
21809                 delete this.cls;
21810             }
21811             if(this.style){
21812                 this.el.applyStyles(this.style);
21813                 delete this.style;
21814             }
21815             this.fireEvent("render", this);
21816             this.afterRender(this.container);
21817             if(this.hidden){
21818                 this.hide();
21819             }
21820             if(this.disabled){
21821                 this.disable();
21822             }
21823         }
21824         return this;
21825     },
21826
21827     // private
21828     // default function is not really useful
21829     onRender : function(ct, position){
21830         if(this.el){
21831             this.el = Roo.get(this.el);
21832             if(this.allowDomMove !== false){
21833                 ct.dom.insertBefore(this.el.dom, position);
21834             }
21835         }
21836     },
21837
21838     // private
21839     getAutoCreate : function(){
21840         var cfg = typeof this.autoCreate == "object" ?
21841                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21842         if(this.id && !cfg.id){
21843             cfg.id = this.id;
21844         }
21845         return cfg;
21846     },
21847
21848     // private
21849     afterRender : Roo.emptyFn,
21850
21851     /**
21852      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21853      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21854      */
21855     destroy : function(){
21856         if(this.fireEvent("beforedestroy", this) !== false){
21857             this.purgeListeners();
21858             this.beforeDestroy();
21859             if(this.rendered){
21860                 this.el.removeAllListeners();
21861                 this.el.remove();
21862                 if(this.actionMode == "container"){
21863                     this.container.remove();
21864                 }
21865             }
21866             this.onDestroy();
21867             Roo.ComponentMgr.unregister(this);
21868             this.fireEvent("destroy", this);
21869         }
21870     },
21871
21872         // private
21873     beforeDestroy : function(){
21874
21875     },
21876
21877         // private
21878         onDestroy : function(){
21879
21880     },
21881
21882     /**
21883      * Returns the underlying {@link Roo.Element}.
21884      * @return {Roo.Element} The element
21885      */
21886     getEl : function(){
21887         return this.el;
21888     },
21889
21890     /**
21891      * Returns the id of this component.
21892      * @return {String}
21893      */
21894     getId : function(){
21895         return this.id;
21896     },
21897
21898     /**
21899      * Try to focus this component.
21900      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21901      * @return {Roo.Component} this
21902      */
21903     focus : function(selectText){
21904         if(this.rendered){
21905             this.el.focus();
21906             if(selectText === true){
21907                 this.el.dom.select();
21908             }
21909         }
21910         return this;
21911     },
21912
21913     // private
21914     blur : function(){
21915         if(this.rendered){
21916             this.el.blur();
21917         }
21918         return this;
21919     },
21920
21921     /**
21922      * Disable this component.
21923      * @return {Roo.Component} this
21924      */
21925     disable : function(){
21926         if(this.rendered){
21927             this.onDisable();
21928         }
21929         this.disabled = true;
21930         this.fireEvent("disable", this);
21931         return this;
21932     },
21933
21934         // private
21935     onDisable : function(){
21936         this.getActionEl().addClass(this.disabledClass);
21937         this.el.dom.disabled = true;
21938     },
21939
21940     /**
21941      * Enable this component.
21942      * @return {Roo.Component} this
21943      */
21944     enable : function(){
21945         if(this.rendered){
21946             this.onEnable();
21947         }
21948         this.disabled = false;
21949         this.fireEvent("enable", this);
21950         return this;
21951     },
21952
21953         // private
21954     onEnable : function(){
21955         this.getActionEl().removeClass(this.disabledClass);
21956         this.el.dom.disabled = false;
21957     },
21958
21959     /**
21960      * Convenience function for setting disabled/enabled by boolean.
21961      * @param {Boolean} disabled
21962      */
21963     setDisabled : function(disabled){
21964         this[disabled ? "disable" : "enable"]();
21965     },
21966
21967     /**
21968      * Show this component.
21969      * @return {Roo.Component} this
21970      */
21971     show: function(){
21972         if(this.fireEvent("beforeshow", this) !== false){
21973             this.hidden = false;
21974             if(this.rendered){
21975                 this.onShow();
21976             }
21977             this.fireEvent("show", this);
21978         }
21979         return this;
21980     },
21981
21982     // private
21983     onShow : function(){
21984         var ae = this.getActionEl();
21985         if(this.hideMode == 'visibility'){
21986             ae.dom.style.visibility = "visible";
21987         }else if(this.hideMode == 'offsets'){
21988             ae.removeClass('x-hidden');
21989         }else{
21990             ae.dom.style.display = "";
21991         }
21992     },
21993
21994     /**
21995      * Hide this component.
21996      * @return {Roo.Component} this
21997      */
21998     hide: function(){
21999         if(this.fireEvent("beforehide", this) !== false){
22000             this.hidden = true;
22001             if(this.rendered){
22002                 this.onHide();
22003             }
22004             this.fireEvent("hide", this);
22005         }
22006         return this;
22007     },
22008
22009     // private
22010     onHide : function(){
22011         var ae = this.getActionEl();
22012         if(this.hideMode == 'visibility'){
22013             ae.dom.style.visibility = "hidden";
22014         }else if(this.hideMode == 'offsets'){
22015             ae.addClass('x-hidden');
22016         }else{
22017             ae.dom.style.display = "none";
22018         }
22019     },
22020
22021     /**
22022      * Convenience function to hide or show this component by boolean.
22023      * @param {Boolean} visible True to show, false to hide
22024      * @return {Roo.Component} this
22025      */
22026     setVisible: function(visible){
22027         if(visible) {
22028             this.show();
22029         }else{
22030             this.hide();
22031         }
22032         return this;
22033     },
22034
22035     /**
22036      * Returns true if this component is visible.
22037      */
22038     isVisible : function(){
22039         return this.getActionEl().isVisible();
22040     },
22041
22042     cloneConfig : function(overrides){
22043         overrides = overrides || {};
22044         var id = overrides.id || Roo.id();
22045         var cfg = Roo.applyIf(overrides, this.initialConfig);
22046         cfg.id = id; // prevent dup id
22047         return new this.constructor(cfg);
22048     }
22049 });/*
22050  * Based on:
22051  * Ext JS Library 1.1.1
22052  * Copyright(c) 2006-2007, Ext JS, LLC.
22053  *
22054  * Originally Released Under LGPL - original licence link has changed is not relivant.
22055  *
22056  * Fork - LGPL
22057  * <script type="text/javascript">
22058  */
22059  (function(){ 
22060 /**
22061  * @class Roo.Layer
22062  * @extends Roo.Element
22063  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22064  * automatic maintaining of shadow/shim positions.
22065  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22066  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22067  * you can pass a string with a CSS class name. False turns off the shadow.
22068  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22069  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22070  * @cfg {String} cls CSS class to add to the element
22071  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22072  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22073  * @constructor
22074  * @param {Object} config An object with config options.
22075  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22076  */
22077
22078 Roo.Layer = function(config, existingEl){
22079     config = config || {};
22080     var dh = Roo.DomHelper;
22081     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22082     if(existingEl){
22083         this.dom = Roo.getDom(existingEl);
22084     }
22085     if(!this.dom){
22086         var o = config.dh || {tag: "div", cls: "x-layer"};
22087         this.dom = dh.append(pel, o);
22088     }
22089     if(config.cls){
22090         this.addClass(config.cls);
22091     }
22092     this.constrain = config.constrain !== false;
22093     this.visibilityMode = Roo.Element.VISIBILITY;
22094     if(config.id){
22095         this.id = this.dom.id = config.id;
22096     }else{
22097         this.id = Roo.id(this.dom);
22098     }
22099     this.zindex = config.zindex || this.getZIndex();
22100     this.position("absolute", this.zindex);
22101     if(config.shadow){
22102         this.shadowOffset = config.shadowOffset || 4;
22103         this.shadow = new Roo.Shadow({
22104             offset : this.shadowOffset,
22105             mode : config.shadow
22106         });
22107     }else{
22108         this.shadowOffset = 0;
22109     }
22110     this.useShim = config.shim !== false && Roo.useShims;
22111     this.useDisplay = config.useDisplay;
22112     this.hide();
22113 };
22114
22115 var supr = Roo.Element.prototype;
22116
22117 // shims are shared among layer to keep from having 100 iframes
22118 var shims = [];
22119
22120 Roo.extend(Roo.Layer, Roo.Element, {
22121
22122     getZIndex : function(){
22123         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22124     },
22125
22126     getShim : function(){
22127         if(!this.useShim){
22128             return null;
22129         }
22130         if(this.shim){
22131             return this.shim;
22132         }
22133         var shim = shims.shift();
22134         if(!shim){
22135             shim = this.createShim();
22136             shim.enableDisplayMode('block');
22137             shim.dom.style.display = 'none';
22138             shim.dom.style.visibility = 'visible';
22139         }
22140         var pn = this.dom.parentNode;
22141         if(shim.dom.parentNode != pn){
22142             pn.insertBefore(shim.dom, this.dom);
22143         }
22144         shim.setStyle('z-index', this.getZIndex()-2);
22145         this.shim = shim;
22146         return shim;
22147     },
22148
22149     hideShim : function(){
22150         if(this.shim){
22151             this.shim.setDisplayed(false);
22152             shims.push(this.shim);
22153             delete this.shim;
22154         }
22155     },
22156
22157     disableShadow : function(){
22158         if(this.shadow){
22159             this.shadowDisabled = true;
22160             this.shadow.hide();
22161             this.lastShadowOffset = this.shadowOffset;
22162             this.shadowOffset = 0;
22163         }
22164     },
22165
22166     enableShadow : function(show){
22167         if(this.shadow){
22168             this.shadowDisabled = false;
22169             this.shadowOffset = this.lastShadowOffset;
22170             delete this.lastShadowOffset;
22171             if(show){
22172                 this.sync(true);
22173             }
22174         }
22175     },
22176
22177     // private
22178     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22179     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22180     sync : function(doShow){
22181         var sw = this.shadow;
22182         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22183             var sh = this.getShim();
22184
22185             var w = this.getWidth(),
22186                 h = this.getHeight();
22187
22188             var l = this.getLeft(true),
22189                 t = this.getTop(true);
22190
22191             if(sw && !this.shadowDisabled){
22192                 if(doShow && !sw.isVisible()){
22193                     sw.show(this);
22194                 }else{
22195                     sw.realign(l, t, w, h);
22196                 }
22197                 if(sh){
22198                     if(doShow){
22199                        sh.show();
22200                     }
22201                     // fit the shim behind the shadow, so it is shimmed too
22202                     var a = sw.adjusts, s = sh.dom.style;
22203                     s.left = (Math.min(l, l+a.l))+"px";
22204                     s.top = (Math.min(t, t+a.t))+"px";
22205                     s.width = (w+a.w)+"px";
22206                     s.height = (h+a.h)+"px";
22207                 }
22208             }else if(sh){
22209                 if(doShow){
22210                    sh.show();
22211                 }
22212                 sh.setSize(w, h);
22213                 sh.setLeftTop(l, t);
22214             }
22215             
22216         }
22217     },
22218
22219     // private
22220     destroy : function(){
22221         this.hideShim();
22222         if(this.shadow){
22223             this.shadow.hide();
22224         }
22225         this.removeAllListeners();
22226         var pn = this.dom.parentNode;
22227         if(pn){
22228             pn.removeChild(this.dom);
22229         }
22230         Roo.Element.uncache(this.id);
22231     },
22232
22233     remove : function(){
22234         this.destroy();
22235     },
22236
22237     // private
22238     beginUpdate : function(){
22239         this.updating = true;
22240     },
22241
22242     // private
22243     endUpdate : function(){
22244         this.updating = false;
22245         this.sync(true);
22246     },
22247
22248     // private
22249     hideUnders : function(negOffset){
22250         if(this.shadow){
22251             this.shadow.hide();
22252         }
22253         this.hideShim();
22254     },
22255
22256     // private
22257     constrainXY : function(){
22258         if(this.constrain){
22259             var vw = Roo.lib.Dom.getViewWidth(),
22260                 vh = Roo.lib.Dom.getViewHeight();
22261             var s = Roo.get(document).getScroll();
22262
22263             var xy = this.getXY();
22264             var x = xy[0], y = xy[1];   
22265             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22266             // only move it if it needs it
22267             var moved = false;
22268             // first validate right/bottom
22269             if((x + w) > vw+s.left){
22270                 x = vw - w - this.shadowOffset;
22271                 moved = true;
22272             }
22273             if((y + h) > vh+s.top){
22274                 y = vh - h - this.shadowOffset;
22275                 moved = true;
22276             }
22277             // then make sure top/left isn't negative
22278             if(x < s.left){
22279                 x = s.left;
22280                 moved = true;
22281             }
22282             if(y < s.top){
22283                 y = s.top;
22284                 moved = true;
22285             }
22286             if(moved){
22287                 if(this.avoidY){
22288                     var ay = this.avoidY;
22289                     if(y <= ay && (y+h) >= ay){
22290                         y = ay-h-5;   
22291                     }
22292                 }
22293                 xy = [x, y];
22294                 this.storeXY(xy);
22295                 supr.setXY.call(this, xy);
22296                 this.sync();
22297             }
22298         }
22299     },
22300
22301     isVisible : function(){
22302         return this.visible;    
22303     },
22304
22305     // private
22306     showAction : function(){
22307         this.visible = true; // track visibility to prevent getStyle calls
22308         if(this.useDisplay === true){
22309             this.setDisplayed("");
22310         }else if(this.lastXY){
22311             supr.setXY.call(this, this.lastXY);
22312         }else if(this.lastLT){
22313             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22314         }
22315     },
22316
22317     // private
22318     hideAction : function(){
22319         this.visible = false;
22320         if(this.useDisplay === true){
22321             this.setDisplayed(false);
22322         }else{
22323             this.setLeftTop(-10000,-10000);
22324         }
22325     },
22326
22327     // overridden Element method
22328     setVisible : function(v, a, d, c, e){
22329         if(v){
22330             this.showAction();
22331         }
22332         if(a && v){
22333             var cb = function(){
22334                 this.sync(true);
22335                 if(c){
22336                     c();
22337                 }
22338             }.createDelegate(this);
22339             supr.setVisible.call(this, true, true, d, cb, e);
22340         }else{
22341             if(!v){
22342                 this.hideUnders(true);
22343             }
22344             var cb = c;
22345             if(a){
22346                 cb = function(){
22347                     this.hideAction();
22348                     if(c){
22349                         c();
22350                     }
22351                 }.createDelegate(this);
22352             }
22353             supr.setVisible.call(this, v, a, d, cb, e);
22354             if(v){
22355                 this.sync(true);
22356             }else if(!a){
22357                 this.hideAction();
22358             }
22359         }
22360     },
22361
22362     storeXY : function(xy){
22363         delete this.lastLT;
22364         this.lastXY = xy;
22365     },
22366
22367     storeLeftTop : function(left, top){
22368         delete this.lastXY;
22369         this.lastLT = [left, top];
22370     },
22371
22372     // private
22373     beforeFx : function(){
22374         this.beforeAction();
22375         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22376     },
22377
22378     // private
22379     afterFx : function(){
22380         Roo.Layer.superclass.afterFx.apply(this, arguments);
22381         this.sync(this.isVisible());
22382     },
22383
22384     // private
22385     beforeAction : function(){
22386         if(!this.updating && this.shadow){
22387             this.shadow.hide();
22388         }
22389     },
22390
22391     // overridden Element method
22392     setLeft : function(left){
22393         this.storeLeftTop(left, this.getTop(true));
22394         supr.setLeft.apply(this, arguments);
22395         this.sync();
22396     },
22397
22398     setTop : function(top){
22399         this.storeLeftTop(this.getLeft(true), top);
22400         supr.setTop.apply(this, arguments);
22401         this.sync();
22402     },
22403
22404     setLeftTop : function(left, top){
22405         this.storeLeftTop(left, top);
22406         supr.setLeftTop.apply(this, arguments);
22407         this.sync();
22408     },
22409
22410     setXY : function(xy, a, d, c, e){
22411         this.fixDisplay();
22412         this.beforeAction();
22413         this.storeXY(xy);
22414         var cb = this.createCB(c);
22415         supr.setXY.call(this, xy, a, d, cb, e);
22416         if(!a){
22417             cb();
22418         }
22419     },
22420
22421     // private
22422     createCB : function(c){
22423         var el = this;
22424         return function(){
22425             el.constrainXY();
22426             el.sync(true);
22427             if(c){
22428                 c();
22429             }
22430         };
22431     },
22432
22433     // overridden Element method
22434     setX : function(x, a, d, c, e){
22435         this.setXY([x, this.getY()], a, d, c, e);
22436     },
22437
22438     // overridden Element method
22439     setY : function(y, a, d, c, e){
22440         this.setXY([this.getX(), y], a, d, c, e);
22441     },
22442
22443     // overridden Element method
22444     setSize : function(w, h, a, d, c, e){
22445         this.beforeAction();
22446         var cb = this.createCB(c);
22447         supr.setSize.call(this, w, h, a, d, cb, e);
22448         if(!a){
22449             cb();
22450         }
22451     },
22452
22453     // overridden Element method
22454     setWidth : function(w, a, d, c, e){
22455         this.beforeAction();
22456         var cb = this.createCB(c);
22457         supr.setWidth.call(this, w, a, d, cb, e);
22458         if(!a){
22459             cb();
22460         }
22461     },
22462
22463     // overridden Element method
22464     setHeight : function(h, a, d, c, e){
22465         this.beforeAction();
22466         var cb = this.createCB(c);
22467         supr.setHeight.call(this, h, a, d, cb, e);
22468         if(!a){
22469             cb();
22470         }
22471     },
22472
22473     // overridden Element method
22474     setBounds : function(x, y, w, h, a, d, c, e){
22475         this.beforeAction();
22476         var cb = this.createCB(c);
22477         if(!a){
22478             this.storeXY([x, y]);
22479             supr.setXY.call(this, [x, y]);
22480             supr.setSize.call(this, w, h, a, d, cb, e);
22481             cb();
22482         }else{
22483             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22484         }
22485         return this;
22486     },
22487     
22488     /**
22489      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22490      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22491      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22492      * @param {Number} zindex The new z-index to set
22493      * @return {this} The Layer
22494      */
22495     setZIndex : function(zindex){
22496         this.zindex = zindex;
22497         this.setStyle("z-index", zindex + 2);
22498         if(this.shadow){
22499             this.shadow.setZIndex(zindex + 1);
22500         }
22501         if(this.shim){
22502             this.shim.setStyle("z-index", zindex);
22503         }
22504     }
22505 });
22506 })();/*
22507  * Based on:
22508  * Ext JS Library 1.1.1
22509  * Copyright(c) 2006-2007, Ext JS, LLC.
22510  *
22511  * Originally Released Under LGPL - original licence link has changed is not relivant.
22512  *
22513  * Fork - LGPL
22514  * <script type="text/javascript">
22515  */
22516
22517
22518 /**
22519  * @class Roo.Shadow
22520  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22521  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22522  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22523  * @constructor
22524  * Create a new Shadow
22525  * @param {Object} config The config object
22526  */
22527 Roo.Shadow = function(config){
22528     Roo.apply(this, config);
22529     if(typeof this.mode != "string"){
22530         this.mode = this.defaultMode;
22531     }
22532     var o = this.offset, a = {h: 0};
22533     var rad = Math.floor(this.offset/2);
22534     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22535         case "drop":
22536             a.w = 0;
22537             a.l = a.t = o;
22538             a.t -= 1;
22539             if(Roo.isIE){
22540                 a.l -= this.offset + rad;
22541                 a.t -= this.offset + rad;
22542                 a.w -= rad;
22543                 a.h -= rad;
22544                 a.t += 1;
22545             }
22546         break;
22547         case "sides":
22548             a.w = (o*2);
22549             a.l = -o;
22550             a.t = o-1;
22551             if(Roo.isIE){
22552                 a.l -= (this.offset - rad);
22553                 a.t -= this.offset + rad;
22554                 a.l += 1;
22555                 a.w -= (this.offset - rad)*2;
22556                 a.w -= rad + 1;
22557                 a.h -= 1;
22558             }
22559         break;
22560         case "frame":
22561             a.w = a.h = (o*2);
22562             a.l = a.t = -o;
22563             a.t += 1;
22564             a.h -= 2;
22565             if(Roo.isIE){
22566                 a.l -= (this.offset - rad);
22567                 a.t -= (this.offset - rad);
22568                 a.l += 1;
22569                 a.w -= (this.offset + rad + 1);
22570                 a.h -= (this.offset + rad);
22571                 a.h += 1;
22572             }
22573         break;
22574     };
22575
22576     this.adjusts = a;
22577 };
22578
22579 Roo.Shadow.prototype = {
22580     /**
22581      * @cfg {String} mode
22582      * The shadow display mode.  Supports the following options:<br />
22583      * sides: Shadow displays on both sides and bottom only<br />
22584      * frame: Shadow displays equally on all four sides<br />
22585      * drop: Traditional bottom-right drop shadow (default)
22586      */
22587     /**
22588      * @cfg {String} offset
22589      * The number of pixels to offset the shadow from the element (defaults to 4)
22590      */
22591     offset: 4,
22592
22593     // private
22594     defaultMode: "drop",
22595
22596     /**
22597      * Displays the shadow under the target element
22598      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22599      */
22600     show : function(target){
22601         target = Roo.get(target);
22602         if(!this.el){
22603             this.el = Roo.Shadow.Pool.pull();
22604             if(this.el.dom.nextSibling != target.dom){
22605                 this.el.insertBefore(target);
22606             }
22607         }
22608         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22609         if(Roo.isIE){
22610             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22611         }
22612         this.realign(
22613             target.getLeft(true),
22614             target.getTop(true),
22615             target.getWidth(),
22616             target.getHeight()
22617         );
22618         this.el.dom.style.display = "block";
22619     },
22620
22621     /**
22622      * Returns true if the shadow is visible, else false
22623      */
22624     isVisible : function(){
22625         return this.el ? true : false;  
22626     },
22627
22628     /**
22629      * Direct alignment when values are already available. Show must be called at least once before
22630      * calling this method to ensure it is initialized.
22631      * @param {Number} left The target element left position
22632      * @param {Number} top The target element top position
22633      * @param {Number} width The target element width
22634      * @param {Number} height The target element height
22635      */
22636     realign : function(l, t, w, h){
22637         if(!this.el){
22638             return;
22639         }
22640         var a = this.adjusts, d = this.el.dom, s = d.style;
22641         var iea = 0;
22642         s.left = (l+a.l)+"px";
22643         s.top = (t+a.t)+"px";
22644         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22645  
22646         if(s.width != sws || s.height != shs){
22647             s.width = sws;
22648             s.height = shs;
22649             if(!Roo.isIE){
22650                 var cn = d.childNodes;
22651                 var sww = Math.max(0, (sw-12))+"px";
22652                 cn[0].childNodes[1].style.width = sww;
22653                 cn[1].childNodes[1].style.width = sww;
22654                 cn[2].childNodes[1].style.width = sww;
22655                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22656             }
22657         }
22658     },
22659
22660     /**
22661      * Hides this shadow
22662      */
22663     hide : function(){
22664         if(this.el){
22665             this.el.dom.style.display = "none";
22666             Roo.Shadow.Pool.push(this.el);
22667             delete this.el;
22668         }
22669     },
22670
22671     /**
22672      * Adjust the z-index of this shadow
22673      * @param {Number} zindex The new z-index
22674      */
22675     setZIndex : function(z){
22676         this.zIndex = z;
22677         if(this.el){
22678             this.el.setStyle("z-index", z);
22679         }
22680     }
22681 };
22682
22683 // Private utility class that manages the internal Shadow cache
22684 Roo.Shadow.Pool = function(){
22685     var p = [];
22686     var markup = Roo.isIE ?
22687                  '<div class="x-ie-shadow"></div>' :
22688                  '<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>';
22689     return {
22690         pull : function(){
22691             var sh = p.shift();
22692             if(!sh){
22693                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22694                 sh.autoBoxAdjust = false;
22695             }
22696             return sh;
22697         },
22698
22699         push : function(sh){
22700             p.push(sh);
22701         }
22702     };
22703 }();/*
22704  * Based on:
22705  * Ext JS Library 1.1.1
22706  * Copyright(c) 2006-2007, Ext JS, LLC.
22707  *
22708  * Originally Released Under LGPL - original licence link has changed is not relivant.
22709  *
22710  * Fork - LGPL
22711  * <script type="text/javascript">
22712  */
22713
22714 /**
22715  * @class Roo.BoxComponent
22716  * @extends Roo.Component
22717  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22718  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22719  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22720  * layout containers.
22721  * @constructor
22722  * @param {Roo.Element/String/Object} config The configuration options.
22723  */
22724 Roo.BoxComponent = function(config){
22725     Roo.Component.call(this, config);
22726     this.addEvents({
22727         /**
22728          * @event resize
22729          * Fires after the component is resized.
22730              * @param {Roo.Component} this
22731              * @param {Number} adjWidth The box-adjusted width that was set
22732              * @param {Number} adjHeight The box-adjusted height that was set
22733              * @param {Number} rawWidth The width that was originally specified
22734              * @param {Number} rawHeight The height that was originally specified
22735              */
22736         resize : true,
22737         /**
22738          * @event move
22739          * Fires after the component is moved.
22740              * @param {Roo.Component} this
22741              * @param {Number} x The new x position
22742              * @param {Number} y The new y position
22743              */
22744         move : true
22745     });
22746 };
22747
22748 Roo.extend(Roo.BoxComponent, Roo.Component, {
22749     // private, set in afterRender to signify that the component has been rendered
22750     boxReady : false,
22751     // private, used to defer height settings to subclasses
22752     deferHeight: false,
22753     /** @cfg {Number} width
22754      * width (optional) size of component
22755      */
22756      /** @cfg {Number} height
22757      * height (optional) size of component
22758      */
22759      
22760     /**
22761      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22762      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22763      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22764      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22765      * @return {Roo.BoxComponent} this
22766      */
22767     setSize : function(w, h){
22768         // support for standard size objects
22769         if(typeof w == 'object'){
22770             h = w.height;
22771             w = w.width;
22772         }
22773         // not rendered
22774         if(!this.boxReady){
22775             this.width = w;
22776             this.height = h;
22777             return this;
22778         }
22779
22780         // prevent recalcs when not needed
22781         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22782             return this;
22783         }
22784         this.lastSize = {width: w, height: h};
22785
22786         var adj = this.adjustSize(w, h);
22787         var aw = adj.width, ah = adj.height;
22788         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22789             var rz = this.getResizeEl();
22790             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22791                 rz.setSize(aw, ah);
22792             }else if(!this.deferHeight && ah !== undefined){
22793                 rz.setHeight(ah);
22794             }else if(aw !== undefined){
22795                 rz.setWidth(aw);
22796             }
22797             this.onResize(aw, ah, w, h);
22798             this.fireEvent('resize', this, aw, ah, w, h);
22799         }
22800         return this;
22801     },
22802
22803     /**
22804      * Gets the current size of the component's underlying element.
22805      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22806      */
22807     getSize : function(){
22808         return this.el.getSize();
22809     },
22810
22811     /**
22812      * Gets the current XY position of the component's underlying element.
22813      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22814      * @return {Array} The XY position of the element (e.g., [100, 200])
22815      */
22816     getPosition : function(local){
22817         if(local === true){
22818             return [this.el.getLeft(true), this.el.getTop(true)];
22819         }
22820         return this.xy || this.el.getXY();
22821     },
22822
22823     /**
22824      * Gets the current box measurements of the component's underlying element.
22825      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22826      * @returns {Object} box An object in the format {x, y, width, height}
22827      */
22828     getBox : function(local){
22829         var s = this.el.getSize();
22830         if(local){
22831             s.x = this.el.getLeft(true);
22832             s.y = this.el.getTop(true);
22833         }else{
22834             var xy = this.xy || this.el.getXY();
22835             s.x = xy[0];
22836             s.y = xy[1];
22837         }
22838         return s;
22839     },
22840
22841     /**
22842      * Sets the current box measurements of the component's underlying element.
22843      * @param {Object} box An object in the format {x, y, width, height}
22844      * @returns {Roo.BoxComponent} this
22845      */
22846     updateBox : function(box){
22847         this.setSize(box.width, box.height);
22848         this.setPagePosition(box.x, box.y);
22849         return this;
22850     },
22851
22852     // protected
22853     getResizeEl : function(){
22854         return this.resizeEl || this.el;
22855     },
22856
22857     // protected
22858     getPositionEl : function(){
22859         return this.positionEl || this.el;
22860     },
22861
22862     /**
22863      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22864      * This method fires the move event.
22865      * @param {Number} left The new left
22866      * @param {Number} top The new top
22867      * @returns {Roo.BoxComponent} this
22868      */
22869     setPosition : function(x, y){
22870         this.x = x;
22871         this.y = y;
22872         if(!this.boxReady){
22873             return this;
22874         }
22875         var adj = this.adjustPosition(x, y);
22876         var ax = adj.x, ay = adj.y;
22877
22878         var el = this.getPositionEl();
22879         if(ax !== undefined || ay !== undefined){
22880             if(ax !== undefined && ay !== undefined){
22881                 el.setLeftTop(ax, ay);
22882             }else if(ax !== undefined){
22883                 el.setLeft(ax);
22884             }else if(ay !== undefined){
22885                 el.setTop(ay);
22886             }
22887             this.onPosition(ax, ay);
22888             this.fireEvent('move', this, ax, ay);
22889         }
22890         return this;
22891     },
22892
22893     /**
22894      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22895      * This method fires the move event.
22896      * @param {Number} x The new x position
22897      * @param {Number} y The new y position
22898      * @returns {Roo.BoxComponent} this
22899      */
22900     setPagePosition : function(x, y){
22901         this.pageX = x;
22902         this.pageY = y;
22903         if(!this.boxReady){
22904             return;
22905         }
22906         if(x === undefined || y === undefined){ // cannot translate undefined points
22907             return;
22908         }
22909         var p = this.el.translatePoints(x, y);
22910         this.setPosition(p.left, p.top);
22911         return this;
22912     },
22913
22914     // private
22915     onRender : function(ct, position){
22916         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22917         if(this.resizeEl){
22918             this.resizeEl = Roo.get(this.resizeEl);
22919         }
22920         if(this.positionEl){
22921             this.positionEl = Roo.get(this.positionEl);
22922         }
22923     },
22924
22925     // private
22926     afterRender : function(){
22927         Roo.BoxComponent.superclass.afterRender.call(this);
22928         this.boxReady = true;
22929         this.setSize(this.width, this.height);
22930         if(this.x || this.y){
22931             this.setPosition(this.x, this.y);
22932         }
22933         if(this.pageX || this.pageY){
22934             this.setPagePosition(this.pageX, this.pageY);
22935         }
22936     },
22937
22938     /**
22939      * Force the component's size to recalculate based on the underlying element's current height and width.
22940      * @returns {Roo.BoxComponent} this
22941      */
22942     syncSize : function(){
22943         delete this.lastSize;
22944         this.setSize(this.el.getWidth(), this.el.getHeight());
22945         return this;
22946     },
22947
22948     /**
22949      * Called after the component is resized, this method is empty by default but can be implemented by any
22950      * subclass that needs to perform custom logic after a resize occurs.
22951      * @param {Number} adjWidth The box-adjusted width that was set
22952      * @param {Number} adjHeight The box-adjusted height that was set
22953      * @param {Number} rawWidth The width that was originally specified
22954      * @param {Number} rawHeight The height that was originally specified
22955      */
22956     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22957
22958     },
22959
22960     /**
22961      * Called after the component is moved, this method is empty by default but can be implemented by any
22962      * subclass that needs to perform custom logic after a move occurs.
22963      * @param {Number} x The new x position
22964      * @param {Number} y The new y position
22965      */
22966     onPosition : function(x, y){
22967
22968     },
22969
22970     // private
22971     adjustSize : function(w, h){
22972         if(this.autoWidth){
22973             w = 'auto';
22974         }
22975         if(this.autoHeight){
22976             h = 'auto';
22977         }
22978         return {width : w, height: h};
22979     },
22980
22981     // private
22982     adjustPosition : function(x, y){
22983         return {x : x, y: y};
22984     }
22985 });/*
22986  * Based on:
22987  * Ext JS Library 1.1.1
22988  * Copyright(c) 2006-2007, Ext JS, LLC.
22989  *
22990  * Originally Released Under LGPL - original licence link has changed is not relivant.
22991  *
22992  * Fork - LGPL
22993  * <script type="text/javascript">
22994  */
22995
22996
22997 /**
22998  * @class Roo.SplitBar
22999  * @extends Roo.util.Observable
23000  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23001  * <br><br>
23002  * Usage:
23003  * <pre><code>
23004 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23005                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23006 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23007 split.minSize = 100;
23008 split.maxSize = 600;
23009 split.animate = true;
23010 split.on('moved', splitterMoved);
23011 </code></pre>
23012  * @constructor
23013  * Create a new SplitBar
23014  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23015  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23016  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23017  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23018                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23019                         position of the SplitBar).
23020  */
23021 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23022     
23023     /** @private */
23024     this.el = Roo.get(dragElement, true);
23025     this.el.dom.unselectable = "on";
23026     /** @private */
23027     this.resizingEl = Roo.get(resizingElement, true);
23028
23029     /**
23030      * @private
23031      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23032      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23033      * @type Number
23034      */
23035     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23036     
23037     /**
23038      * The minimum size of the resizing element. (Defaults to 0)
23039      * @type Number
23040      */
23041     this.minSize = 0;
23042     
23043     /**
23044      * The maximum size of the resizing element. (Defaults to 2000)
23045      * @type Number
23046      */
23047     this.maxSize = 2000;
23048     
23049     /**
23050      * Whether to animate the transition to the new size
23051      * @type Boolean
23052      */
23053     this.animate = false;
23054     
23055     /**
23056      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23057      * @type Boolean
23058      */
23059     this.useShim = false;
23060     
23061     /** @private */
23062     this.shim = null;
23063     
23064     if(!existingProxy){
23065         /** @private */
23066         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23067     }else{
23068         this.proxy = Roo.get(existingProxy).dom;
23069     }
23070     /** @private */
23071     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23072     
23073     /** @private */
23074     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23075     
23076     /** @private */
23077     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23078     
23079     /** @private */
23080     this.dragSpecs = {};
23081     
23082     /**
23083      * @private The adapter to use to positon and resize elements
23084      */
23085     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23086     this.adapter.init(this);
23087     
23088     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23089         /** @private */
23090         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23091         this.el.addClass("x-splitbar-h");
23092     }else{
23093         /** @private */
23094         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23095         this.el.addClass("x-splitbar-v");
23096     }
23097     
23098     this.addEvents({
23099         /**
23100          * @event resize
23101          * Fires when the splitter is moved (alias for {@link #event-moved})
23102          * @param {Roo.SplitBar} this
23103          * @param {Number} newSize the new width or height
23104          */
23105         "resize" : true,
23106         /**
23107          * @event moved
23108          * Fires when the splitter is moved
23109          * @param {Roo.SplitBar} this
23110          * @param {Number} newSize the new width or height
23111          */
23112         "moved" : true,
23113         /**
23114          * @event beforeresize
23115          * Fires before the splitter is dragged
23116          * @param {Roo.SplitBar} this
23117          */
23118         "beforeresize" : true,
23119
23120         "beforeapply" : true
23121     });
23122
23123     Roo.util.Observable.call(this);
23124 };
23125
23126 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23127     onStartProxyDrag : function(x, y){
23128         this.fireEvent("beforeresize", this);
23129         if(!this.overlay){
23130             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23131             o.unselectable();
23132             o.enableDisplayMode("block");
23133             // all splitbars share the same overlay
23134             Roo.SplitBar.prototype.overlay = o;
23135         }
23136         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23137         this.overlay.show();
23138         Roo.get(this.proxy).setDisplayed("block");
23139         var size = this.adapter.getElementSize(this);
23140         this.activeMinSize = this.getMinimumSize();;
23141         this.activeMaxSize = this.getMaximumSize();;
23142         var c1 = size - this.activeMinSize;
23143         var c2 = Math.max(this.activeMaxSize - size, 0);
23144         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23145             this.dd.resetConstraints();
23146             this.dd.setXConstraint(
23147                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23148                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23149             );
23150             this.dd.setYConstraint(0, 0);
23151         }else{
23152             this.dd.resetConstraints();
23153             this.dd.setXConstraint(0, 0);
23154             this.dd.setYConstraint(
23155                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23156                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23157             );
23158          }
23159         this.dragSpecs.startSize = size;
23160         this.dragSpecs.startPoint = [x, y];
23161         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23162     },
23163     
23164     /** 
23165      * @private Called after the drag operation by the DDProxy
23166      */
23167     onEndProxyDrag : function(e){
23168         Roo.get(this.proxy).setDisplayed(false);
23169         var endPoint = Roo.lib.Event.getXY(e);
23170         if(this.overlay){
23171             this.overlay.hide();
23172         }
23173         var newSize;
23174         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23175             newSize = this.dragSpecs.startSize + 
23176                 (this.placement == Roo.SplitBar.LEFT ?
23177                     endPoint[0] - this.dragSpecs.startPoint[0] :
23178                     this.dragSpecs.startPoint[0] - endPoint[0]
23179                 );
23180         }else{
23181             newSize = this.dragSpecs.startSize + 
23182                 (this.placement == Roo.SplitBar.TOP ?
23183                     endPoint[1] - this.dragSpecs.startPoint[1] :
23184                     this.dragSpecs.startPoint[1] - endPoint[1]
23185                 );
23186         }
23187         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23188         if(newSize != this.dragSpecs.startSize){
23189             if(this.fireEvent('beforeapply', this, newSize) !== false){
23190                 this.adapter.setElementSize(this, newSize);
23191                 this.fireEvent("moved", this, newSize);
23192                 this.fireEvent("resize", this, newSize);
23193             }
23194         }
23195     },
23196     
23197     /**
23198      * Get the adapter this SplitBar uses
23199      * @return The adapter object
23200      */
23201     getAdapter : function(){
23202         return this.adapter;
23203     },
23204     
23205     /**
23206      * Set the adapter this SplitBar uses
23207      * @param {Object} adapter A SplitBar adapter object
23208      */
23209     setAdapter : function(adapter){
23210         this.adapter = adapter;
23211         this.adapter.init(this);
23212     },
23213     
23214     /**
23215      * Gets the minimum size for the resizing element
23216      * @return {Number} The minimum size
23217      */
23218     getMinimumSize : function(){
23219         return this.minSize;
23220     },
23221     
23222     /**
23223      * Sets the minimum size for the resizing element
23224      * @param {Number} minSize The minimum size
23225      */
23226     setMinimumSize : function(minSize){
23227         this.minSize = minSize;
23228     },
23229     
23230     /**
23231      * Gets the maximum size for the resizing element
23232      * @return {Number} The maximum size
23233      */
23234     getMaximumSize : function(){
23235         return this.maxSize;
23236     },
23237     
23238     /**
23239      * Sets the maximum size for the resizing element
23240      * @param {Number} maxSize The maximum size
23241      */
23242     setMaximumSize : function(maxSize){
23243         this.maxSize = maxSize;
23244     },
23245     
23246     /**
23247      * Sets the initialize size for the resizing element
23248      * @param {Number} size The initial size
23249      */
23250     setCurrentSize : function(size){
23251         var oldAnimate = this.animate;
23252         this.animate = false;
23253         this.adapter.setElementSize(this, size);
23254         this.animate = oldAnimate;
23255     },
23256     
23257     /**
23258      * Destroy this splitbar. 
23259      * @param {Boolean} removeEl True to remove the element
23260      */
23261     destroy : function(removeEl){
23262         if(this.shim){
23263             this.shim.remove();
23264         }
23265         this.dd.unreg();
23266         this.proxy.parentNode.removeChild(this.proxy);
23267         if(removeEl){
23268             this.el.remove();
23269         }
23270     }
23271 });
23272
23273 /**
23274  * @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.
23275  */
23276 Roo.SplitBar.createProxy = function(dir){
23277     var proxy = new Roo.Element(document.createElement("div"));
23278     proxy.unselectable();
23279     var cls = 'x-splitbar-proxy';
23280     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23281     document.body.appendChild(proxy.dom);
23282     return proxy.dom;
23283 };
23284
23285 /** 
23286  * @class Roo.SplitBar.BasicLayoutAdapter
23287  * Default Adapter. It assumes the splitter and resizing element are not positioned
23288  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23289  */
23290 Roo.SplitBar.BasicLayoutAdapter = function(){
23291 };
23292
23293 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23294     // do nothing for now
23295     init : function(s){
23296     
23297     },
23298     /**
23299      * Called before drag operations to get the current size of the resizing element. 
23300      * @param {Roo.SplitBar} s The SplitBar using this adapter
23301      */
23302      getElementSize : function(s){
23303         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23304             return s.resizingEl.getWidth();
23305         }else{
23306             return s.resizingEl.getHeight();
23307         }
23308     },
23309     
23310     /**
23311      * Called after drag operations to set the size of the resizing element.
23312      * @param {Roo.SplitBar} s The SplitBar using this adapter
23313      * @param {Number} newSize The new size to set
23314      * @param {Function} onComplete A function to be invoked when resizing is complete
23315      */
23316     setElementSize : function(s, newSize, onComplete){
23317         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23318             if(!s.animate){
23319                 s.resizingEl.setWidth(newSize);
23320                 if(onComplete){
23321                     onComplete(s, newSize);
23322                 }
23323             }else{
23324                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23325             }
23326         }else{
23327             
23328             if(!s.animate){
23329                 s.resizingEl.setHeight(newSize);
23330                 if(onComplete){
23331                     onComplete(s, newSize);
23332                 }
23333             }else{
23334                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23335             }
23336         }
23337     }
23338 };
23339
23340 /** 
23341  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23342  * @extends Roo.SplitBar.BasicLayoutAdapter
23343  * Adapter that  moves the splitter element to align with the resized sizing element. 
23344  * Used with an absolute positioned SplitBar.
23345  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23346  * document.body, make sure you assign an id to the body element.
23347  */
23348 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23349     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23350     this.container = Roo.get(container);
23351 };
23352
23353 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23354     init : function(s){
23355         this.basic.init(s);
23356     },
23357     
23358     getElementSize : function(s){
23359         return this.basic.getElementSize(s);
23360     },
23361     
23362     setElementSize : function(s, newSize, onComplete){
23363         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23364     },
23365     
23366     moveSplitter : function(s){
23367         var yes = Roo.SplitBar;
23368         switch(s.placement){
23369             case yes.LEFT:
23370                 s.el.setX(s.resizingEl.getRight());
23371                 break;
23372             case yes.RIGHT:
23373                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23374                 break;
23375             case yes.TOP:
23376                 s.el.setY(s.resizingEl.getBottom());
23377                 break;
23378             case yes.BOTTOM:
23379                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23380                 break;
23381         }
23382     }
23383 };
23384
23385 /**
23386  * Orientation constant - Create a vertical SplitBar
23387  * @static
23388  * @type Number
23389  */
23390 Roo.SplitBar.VERTICAL = 1;
23391
23392 /**
23393  * Orientation constant - Create a horizontal SplitBar
23394  * @static
23395  * @type Number
23396  */
23397 Roo.SplitBar.HORIZONTAL = 2;
23398
23399 /**
23400  * Placement constant - The resizing element is to the left of the splitter element
23401  * @static
23402  * @type Number
23403  */
23404 Roo.SplitBar.LEFT = 1;
23405
23406 /**
23407  * Placement constant - The resizing element is to the right of the splitter element
23408  * @static
23409  * @type Number
23410  */
23411 Roo.SplitBar.RIGHT = 2;
23412
23413 /**
23414  * Placement constant - The resizing element is positioned above the splitter element
23415  * @static
23416  * @type Number
23417  */
23418 Roo.SplitBar.TOP = 3;
23419
23420 /**
23421  * Placement constant - The resizing element is positioned under splitter element
23422  * @static
23423  * @type Number
23424  */
23425 Roo.SplitBar.BOTTOM = 4;
23426 /*
23427  * Based on:
23428  * Ext JS Library 1.1.1
23429  * Copyright(c) 2006-2007, Ext JS, LLC.
23430  *
23431  * Originally Released Under LGPL - original licence link has changed is not relivant.
23432  *
23433  * Fork - LGPL
23434  * <script type="text/javascript">
23435  */
23436
23437 /**
23438  * @class Roo.View
23439  * @extends Roo.util.Observable
23440  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23441  * This class also supports single and multi selection modes. <br>
23442  * Create a data model bound view:
23443  <pre><code>
23444  var store = new Roo.data.Store(...);
23445
23446  var view = new Roo.View({
23447     el : "my-element",
23448     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23449  
23450     singleSelect: true,
23451     selectedClass: "ydataview-selected",
23452     store: store
23453  });
23454
23455  // listen for node click?
23456  view.on("click", function(vw, index, node, e){
23457  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23458  });
23459
23460  // load XML data
23461  dataModel.load("foobar.xml");
23462  </code></pre>
23463  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23464  * <br><br>
23465  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23466  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23467  * 
23468  * Note: old style constructor is still suported (container, template, config)
23469  * 
23470  * @constructor
23471  * Create a new View
23472  * @param {Object} config The config object
23473  * 
23474  */
23475 Roo.View = function(config, depreciated_tpl, depreciated_config){
23476     
23477     if (typeof(depreciated_tpl) == 'undefined') {
23478         // new way.. - universal constructor.
23479         Roo.apply(this, config);
23480         this.el  = Roo.get(this.el);
23481     } else {
23482         // old format..
23483         this.el  = Roo.get(config);
23484         this.tpl = depreciated_tpl;
23485         Roo.apply(this, depreciated_config);
23486     }
23487      
23488     
23489     if(typeof(this.tpl) == "string"){
23490         this.tpl = new Roo.Template(this.tpl);
23491     } else {
23492         // support xtype ctors..
23493         this.tpl = new Roo.factory(this.tpl, Roo);
23494     }
23495     
23496     
23497     this.tpl.compile();
23498    
23499
23500      
23501     /** @private */
23502     this.addEvents({
23503     /**
23504      * @event beforeclick
23505      * Fires before a click is processed. Returns false to cancel the default action.
23506      * @param {Roo.View} this
23507      * @param {Number} index The index of the target node
23508      * @param {HTMLElement} node The target node
23509      * @param {Roo.EventObject} e The raw event object
23510      */
23511         "beforeclick" : true,
23512     /**
23513      * @event click
23514      * Fires when a template node is clicked.
23515      * @param {Roo.View} this
23516      * @param {Number} index The index of the target node
23517      * @param {HTMLElement} node The target node
23518      * @param {Roo.EventObject} e The raw event object
23519      */
23520         "click" : true,
23521     /**
23522      * @event dblclick
23523      * Fires when a template node is double clicked.
23524      * @param {Roo.View} this
23525      * @param {Number} index The index of the target node
23526      * @param {HTMLElement} node The target node
23527      * @param {Roo.EventObject} e The raw event object
23528      */
23529         "dblclick" : true,
23530     /**
23531      * @event contextmenu
23532      * Fires when a template node is right clicked.
23533      * @param {Roo.View} this
23534      * @param {Number} index The index of the target node
23535      * @param {HTMLElement} node The target node
23536      * @param {Roo.EventObject} e The raw event object
23537      */
23538         "contextmenu" : true,
23539     /**
23540      * @event selectionchange
23541      * Fires when the selected nodes change.
23542      * @param {Roo.View} this
23543      * @param {Array} selections Array of the selected nodes
23544      */
23545         "selectionchange" : true,
23546
23547     /**
23548      * @event beforeselect
23549      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23550      * @param {Roo.View} this
23551      * @param {HTMLElement} node The node to be selected
23552      * @param {Array} selections Array of currently selected nodes
23553      */
23554         "beforeselect" : true
23555     });
23556
23557     this.el.on({
23558         "click": this.onClick,
23559         "dblclick": this.onDblClick,
23560         "contextmenu": this.onContextMenu,
23561         scope:this
23562     });
23563
23564     this.selections = [];
23565     this.nodes = [];
23566     this.cmp = new Roo.CompositeElementLite([]);
23567     if(this.store){
23568         this.store = Roo.factory(this.store, Roo.data);
23569         this.setStore(this.store, true);
23570     }
23571     Roo.View.superclass.constructor.call(this);
23572 };
23573
23574 Roo.extend(Roo.View, Roo.util.Observable, {
23575     
23576      /**
23577      * @cfg {Roo.data.Store} store Data store to load data from.
23578      */
23579     store : false,
23580     
23581     /**
23582      * @cfg {String|Roo.Element} el The container element.
23583      */
23584     el : '',
23585     
23586     /**
23587      * @cfg {String|Roo.Template} tpl The template used by this View 
23588      */
23589     tpl : false,
23590     
23591     /**
23592      * @cfg {String} selectedClass The css class to add to selected nodes
23593      */
23594     selectedClass : "x-view-selected",
23595      /**
23596      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23597      */
23598     emptyText : "",
23599     /**
23600      * @cfg {Boolean} multiSelect Allow multiple selection
23601      */
23602     
23603     multiSelect : false,
23604     /**
23605      * @cfg {Boolean} singleSelect Allow single selection
23606      */
23607     singleSelect:  false,
23608     
23609     /**
23610      * Returns the element this view is bound to.
23611      * @return {Roo.Element}
23612      */
23613     getEl : function(){
23614         return this.el;
23615     },
23616
23617     /**
23618      * Refreshes the view.
23619      */
23620     refresh : function(){
23621         var t = this.tpl;
23622         this.clearSelections();
23623         this.el.update("");
23624         var html = [];
23625         var records = this.store.getRange();
23626         if(records.length < 1){
23627             this.el.update(this.emptyText);
23628             return;
23629         }
23630         for(var i = 0, len = records.length; i < len; i++){
23631             var data = this.prepareData(records[i].data, i, records[i]);
23632             html[html.length] = t.apply(data);
23633         }
23634         this.el.update(html.join(""));
23635         this.nodes = this.el.dom.childNodes;
23636         this.updateIndexes(0);
23637     },
23638
23639     /**
23640      * Function to override to reformat the data that is sent to
23641      * the template for each node.
23642      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23643      * a JSON object for an UpdateManager bound view).
23644      */
23645     prepareData : function(data){
23646         return data;
23647     },
23648
23649     onUpdate : function(ds, record){
23650         this.clearSelections();
23651         var index = this.store.indexOf(record);
23652         var n = this.nodes[index];
23653         this.tpl.insertBefore(n, this.prepareData(record.data));
23654         n.parentNode.removeChild(n);
23655         this.updateIndexes(index, index);
23656     },
23657
23658     onAdd : function(ds, records, index){
23659         this.clearSelections();
23660         if(this.nodes.length == 0){
23661             this.refresh();
23662             return;
23663         }
23664         var n = this.nodes[index];
23665         for(var i = 0, len = records.length; i < len; i++){
23666             var d = this.prepareData(records[i].data);
23667             if(n){
23668                 this.tpl.insertBefore(n, d);
23669             }else{
23670                 this.tpl.append(this.el, d);
23671             }
23672         }
23673         this.updateIndexes(index);
23674     },
23675
23676     onRemove : function(ds, record, index){
23677         this.clearSelections();
23678         this.el.dom.removeChild(this.nodes[index]);
23679         this.updateIndexes(index);
23680     },
23681
23682     /**
23683      * Refresh an individual node.
23684      * @param {Number} index
23685      */
23686     refreshNode : function(index){
23687         this.onUpdate(this.store, this.store.getAt(index));
23688     },
23689
23690     updateIndexes : function(startIndex, endIndex){
23691         var ns = this.nodes;
23692         startIndex = startIndex || 0;
23693         endIndex = endIndex || ns.length - 1;
23694         for(var i = startIndex; i <= endIndex; i++){
23695             ns[i].nodeIndex = i;
23696         }
23697     },
23698
23699     /**
23700      * Changes the data store this view uses and refresh the view.
23701      * @param {Store} store
23702      */
23703     setStore : function(store, initial){
23704         if(!initial && this.store){
23705             this.store.un("datachanged", this.refresh);
23706             this.store.un("add", this.onAdd);
23707             this.store.un("remove", this.onRemove);
23708             this.store.un("update", this.onUpdate);
23709             this.store.un("clear", this.refresh);
23710         }
23711         if(store){
23712           
23713             store.on("datachanged", this.refresh, this);
23714             store.on("add", this.onAdd, this);
23715             store.on("remove", this.onRemove, this);
23716             store.on("update", this.onUpdate, this);
23717             store.on("clear", this.refresh, this);
23718         }
23719         
23720         if(store){
23721             this.refresh();
23722         }
23723     },
23724
23725     /**
23726      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23727      * @param {HTMLElement} node
23728      * @return {HTMLElement} The template node
23729      */
23730     findItemFromChild : function(node){
23731         var el = this.el.dom;
23732         if(!node || node.parentNode == el){
23733                     return node;
23734             }
23735             var p = node.parentNode;
23736             while(p && p != el){
23737             if(p.parentNode == el){
23738                 return p;
23739             }
23740             p = p.parentNode;
23741         }
23742             return null;
23743     },
23744
23745     /** @ignore */
23746     onClick : function(e){
23747         var item = this.findItemFromChild(e.getTarget());
23748         if(item){
23749             var index = this.indexOf(item);
23750             if(this.onItemClick(item, index, e) !== false){
23751                 this.fireEvent("click", this, index, item, e);
23752             }
23753         }else{
23754             this.clearSelections();
23755         }
23756     },
23757
23758     /** @ignore */
23759     onContextMenu : function(e){
23760         var item = this.findItemFromChild(e.getTarget());
23761         if(item){
23762             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23763         }
23764     },
23765
23766     /** @ignore */
23767     onDblClick : function(e){
23768         var item = this.findItemFromChild(e.getTarget());
23769         if(item){
23770             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23771         }
23772     },
23773
23774     onItemClick : function(item, index, e){
23775         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23776             return false;
23777         }
23778         if(this.multiSelect || this.singleSelect){
23779             if(this.multiSelect && e.shiftKey && this.lastSelection){
23780                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23781             }else{
23782                 this.select(item, this.multiSelect && e.ctrlKey);
23783                 this.lastSelection = item;
23784             }
23785             e.preventDefault();
23786         }
23787         return true;
23788     },
23789
23790     /**
23791      * Get the number of selected nodes.
23792      * @return {Number}
23793      */
23794     getSelectionCount : function(){
23795         return this.selections.length;
23796     },
23797
23798     /**
23799      * Get the currently selected nodes.
23800      * @return {Array} An array of HTMLElements
23801      */
23802     getSelectedNodes : function(){
23803         return this.selections;
23804     },
23805
23806     /**
23807      * Get the indexes of the selected nodes.
23808      * @return {Array}
23809      */
23810     getSelectedIndexes : function(){
23811         var indexes = [], s = this.selections;
23812         for(var i = 0, len = s.length; i < len; i++){
23813             indexes.push(s[i].nodeIndex);
23814         }
23815         return indexes;
23816     },
23817
23818     /**
23819      * Clear all selections
23820      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23821      */
23822     clearSelections : function(suppressEvent){
23823         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23824             this.cmp.elements = this.selections;
23825             this.cmp.removeClass(this.selectedClass);
23826             this.selections = [];
23827             if(!suppressEvent){
23828                 this.fireEvent("selectionchange", this, this.selections);
23829             }
23830         }
23831     },
23832
23833     /**
23834      * Returns true if the passed node is selected
23835      * @param {HTMLElement/Number} node The node or node index
23836      * @return {Boolean}
23837      */
23838     isSelected : function(node){
23839         var s = this.selections;
23840         if(s.length < 1){
23841             return false;
23842         }
23843         node = this.getNode(node);
23844         return s.indexOf(node) !== -1;
23845     },
23846
23847     /**
23848      * Selects nodes.
23849      * @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
23850      * @param {Boolean} keepExisting (optional) true to keep existing selections
23851      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23852      */
23853     select : function(nodeInfo, keepExisting, suppressEvent){
23854         if(nodeInfo instanceof Array){
23855             if(!keepExisting){
23856                 this.clearSelections(true);
23857             }
23858             for(var i = 0, len = nodeInfo.length; i < len; i++){
23859                 this.select(nodeInfo[i], true, true);
23860             }
23861         } else{
23862             var node = this.getNode(nodeInfo);
23863             if(node && !this.isSelected(node)){
23864                 if(!keepExisting){
23865                     this.clearSelections(true);
23866                 }
23867                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23868                     Roo.fly(node).addClass(this.selectedClass);
23869                     this.selections.push(node);
23870                     if(!suppressEvent){
23871                         this.fireEvent("selectionchange", this, this.selections);
23872                     }
23873                 }
23874             }
23875         }
23876     },
23877
23878     /**
23879      * Gets a template node.
23880      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23881      * @return {HTMLElement} The node or null if it wasn't found
23882      */
23883     getNode : function(nodeInfo){
23884         if(typeof nodeInfo == "string"){
23885             return document.getElementById(nodeInfo);
23886         }else if(typeof nodeInfo == "number"){
23887             return this.nodes[nodeInfo];
23888         }
23889         return nodeInfo;
23890     },
23891
23892     /**
23893      * Gets a range template nodes.
23894      * @param {Number} startIndex
23895      * @param {Number} endIndex
23896      * @return {Array} An array of nodes
23897      */
23898     getNodes : function(start, end){
23899         var ns = this.nodes;
23900         start = start || 0;
23901         end = typeof end == "undefined" ? ns.length - 1 : end;
23902         var nodes = [];
23903         if(start <= end){
23904             for(var i = start; i <= end; i++){
23905                 nodes.push(ns[i]);
23906             }
23907         } else{
23908             for(var i = start; i >= end; i--){
23909                 nodes.push(ns[i]);
23910             }
23911         }
23912         return nodes;
23913     },
23914
23915     /**
23916      * Finds the index of the passed node
23917      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23918      * @return {Number} The index of the node or -1
23919      */
23920     indexOf : function(node){
23921         node = this.getNode(node);
23922         if(typeof node.nodeIndex == "number"){
23923             return node.nodeIndex;
23924         }
23925         var ns = this.nodes;
23926         for(var i = 0, len = ns.length; i < len; i++){
23927             if(ns[i] == node){
23928                 return i;
23929             }
23930         }
23931         return -1;
23932     }
23933 });
23934 /*
23935  * Based on:
23936  * Ext JS Library 1.1.1
23937  * Copyright(c) 2006-2007, Ext JS, LLC.
23938  *
23939  * Originally Released Under LGPL - original licence link has changed is not relivant.
23940  *
23941  * Fork - LGPL
23942  * <script type="text/javascript">
23943  */
23944
23945 /**
23946  * @class Roo.JsonView
23947  * @extends Roo.View
23948  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23949 <pre><code>
23950 var view = new Roo.JsonView({
23951     container: "my-element",
23952     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23953     multiSelect: true, 
23954     jsonRoot: "data" 
23955 });
23956
23957 // listen for node click?
23958 view.on("click", function(vw, index, node, e){
23959     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23960 });
23961
23962 // direct load of JSON data
23963 view.load("foobar.php");
23964
23965 // Example from my blog list
23966 var tpl = new Roo.Template(
23967     '&lt;div class="entry"&gt;' +
23968     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23969     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23970     "&lt;/div&gt;&lt;hr /&gt;"
23971 );
23972
23973 var moreView = new Roo.JsonView({
23974     container :  "entry-list", 
23975     template : tpl,
23976     jsonRoot: "posts"
23977 });
23978 moreView.on("beforerender", this.sortEntries, this);
23979 moreView.load({
23980     url: "/blog/get-posts.php",
23981     params: "allposts=true",
23982     text: "Loading Blog Entries..."
23983 });
23984 </code></pre>
23985
23986 * Note: old code is supported with arguments : (container, template, config)
23987
23988
23989  * @constructor
23990  * Create a new JsonView
23991  * 
23992  * @param {Object} config The config object
23993  * 
23994  */
23995 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23996     
23997     
23998     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23999
24000     var um = this.el.getUpdateManager();
24001     um.setRenderer(this);
24002     um.on("update", this.onLoad, this);
24003     um.on("failure", this.onLoadException, this);
24004
24005     /**
24006      * @event beforerender
24007      * Fires before rendering of the downloaded JSON data.
24008      * @param {Roo.JsonView} this
24009      * @param {Object} data The JSON data loaded
24010      */
24011     /**
24012      * @event load
24013      * Fires when data is loaded.
24014      * @param {Roo.JsonView} this
24015      * @param {Object} data The JSON data loaded
24016      * @param {Object} response The raw Connect response object
24017      */
24018     /**
24019      * @event loadexception
24020      * Fires when loading fails.
24021      * @param {Roo.JsonView} this
24022      * @param {Object} response The raw Connect response object
24023      */
24024     this.addEvents({
24025         'beforerender' : true,
24026         'load' : true,
24027         'loadexception' : true
24028     });
24029 };
24030 Roo.extend(Roo.JsonView, Roo.View, {
24031     /**
24032      * @type {String} The root property in the loaded JSON object that contains the data
24033      */
24034     jsonRoot : "",
24035
24036     /**
24037      * Refreshes the view.
24038      */
24039     refresh : function(){
24040         this.clearSelections();
24041         this.el.update("");
24042         var html = [];
24043         var o = this.jsonData;
24044         if(o && o.length > 0){
24045             for(var i = 0, len = o.length; i < len; i++){
24046                 var data = this.prepareData(o[i], i, o);
24047                 html[html.length] = this.tpl.apply(data);
24048             }
24049         }else{
24050             html.push(this.emptyText);
24051         }
24052         this.el.update(html.join(""));
24053         this.nodes = this.el.dom.childNodes;
24054         this.updateIndexes(0);
24055     },
24056
24057     /**
24058      * 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.
24059      * @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:
24060      <pre><code>
24061      view.load({
24062          url: "your-url.php",
24063          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24064          callback: yourFunction,
24065          scope: yourObject, //(optional scope)
24066          discardUrl: false,
24067          nocache: false,
24068          text: "Loading...",
24069          timeout: 30,
24070          scripts: false
24071      });
24072      </code></pre>
24073      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24074      * 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.
24075      * @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}
24076      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24077      * @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.
24078      */
24079     load : function(){
24080         var um = this.el.getUpdateManager();
24081         um.update.apply(um, arguments);
24082     },
24083
24084     render : function(el, response){
24085         this.clearSelections();
24086         this.el.update("");
24087         var o;
24088         try{
24089             o = Roo.util.JSON.decode(response.responseText);
24090             if(this.jsonRoot){
24091                 
24092                 o = o[this.jsonRoot];
24093             }
24094         } catch(e){
24095         }
24096         /**
24097          * The current JSON data or null
24098          */
24099         this.jsonData = o;
24100         this.beforeRender();
24101         this.refresh();
24102     },
24103
24104 /**
24105  * Get the number of records in the current JSON dataset
24106  * @return {Number}
24107  */
24108     getCount : function(){
24109         return this.jsonData ? this.jsonData.length : 0;
24110     },
24111
24112 /**
24113  * Returns the JSON object for the specified node(s)
24114  * @param {HTMLElement/Array} node The node or an array of nodes
24115  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24116  * you get the JSON object for the node
24117  */
24118     getNodeData : function(node){
24119         if(node instanceof Array){
24120             var data = [];
24121             for(var i = 0, len = node.length; i < len; i++){
24122                 data.push(this.getNodeData(node[i]));
24123             }
24124             return data;
24125         }
24126         return this.jsonData[this.indexOf(node)] || null;
24127     },
24128
24129     beforeRender : function(){
24130         this.snapshot = this.jsonData;
24131         if(this.sortInfo){
24132             this.sort.apply(this, this.sortInfo);
24133         }
24134         this.fireEvent("beforerender", this, this.jsonData);
24135     },
24136
24137     onLoad : function(el, o){
24138         this.fireEvent("load", this, this.jsonData, o);
24139     },
24140
24141     onLoadException : function(el, o){
24142         this.fireEvent("loadexception", this, o);
24143     },
24144
24145 /**
24146  * Filter the data by a specific property.
24147  * @param {String} property A property on your JSON objects
24148  * @param {String/RegExp} value Either string that the property values
24149  * should start with, or a RegExp to test against the property
24150  */
24151     filter : function(property, value){
24152         if(this.jsonData){
24153             var data = [];
24154             var ss = this.snapshot;
24155             if(typeof value == "string"){
24156                 var vlen = value.length;
24157                 if(vlen == 0){
24158                     this.clearFilter();
24159                     return;
24160                 }
24161                 value = value.toLowerCase();
24162                 for(var i = 0, len = ss.length; i < len; i++){
24163                     var o = ss[i];
24164                     if(o[property].substr(0, vlen).toLowerCase() == value){
24165                         data.push(o);
24166                     }
24167                 }
24168             } else if(value.exec){ // regex?
24169                 for(var i = 0, len = ss.length; i < len; i++){
24170                     var o = ss[i];
24171                     if(value.test(o[property])){
24172                         data.push(o);
24173                     }
24174                 }
24175             } else{
24176                 return;
24177             }
24178             this.jsonData = data;
24179             this.refresh();
24180         }
24181     },
24182
24183 /**
24184  * Filter by a function. The passed function will be called with each
24185  * object in the current dataset. If the function returns true the value is kept,
24186  * otherwise it is filtered.
24187  * @param {Function} fn
24188  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24189  */
24190     filterBy : function(fn, scope){
24191         if(this.jsonData){
24192             var data = [];
24193             var ss = this.snapshot;
24194             for(var i = 0, len = ss.length; i < len; i++){
24195                 var o = ss[i];
24196                 if(fn.call(scope || this, o)){
24197                     data.push(o);
24198                 }
24199             }
24200             this.jsonData = data;
24201             this.refresh();
24202         }
24203     },
24204
24205 /**
24206  * Clears the current filter.
24207  */
24208     clearFilter : function(){
24209         if(this.snapshot && this.jsonData != this.snapshot){
24210             this.jsonData = this.snapshot;
24211             this.refresh();
24212         }
24213     },
24214
24215
24216 /**
24217  * Sorts the data for this view and refreshes it.
24218  * @param {String} property A property on your JSON objects to sort on
24219  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24220  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24221  */
24222     sort : function(property, dir, sortType){
24223         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24224         if(this.jsonData){
24225             var p = property;
24226             var dsc = dir && dir.toLowerCase() == "desc";
24227             var f = function(o1, o2){
24228                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24229                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24230                 ;
24231                 if(v1 < v2){
24232                     return dsc ? +1 : -1;
24233                 } else if(v1 > v2){
24234                     return dsc ? -1 : +1;
24235                 } else{
24236                     return 0;
24237                 }
24238             };
24239             this.jsonData.sort(f);
24240             this.refresh();
24241             if(this.jsonData != this.snapshot){
24242                 this.snapshot.sort(f);
24243             }
24244         }
24245     }
24246 });/*
24247  * Based on:
24248  * Ext JS Library 1.1.1
24249  * Copyright(c) 2006-2007, Ext JS, LLC.
24250  *
24251  * Originally Released Under LGPL - original licence link has changed is not relivant.
24252  *
24253  * Fork - LGPL
24254  * <script type="text/javascript">
24255  */
24256  
24257
24258 /**
24259  * @class Roo.ColorPalette
24260  * @extends Roo.Component
24261  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24262  * Here's an example of typical usage:
24263  * <pre><code>
24264 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24265 cp.render('my-div');
24266
24267 cp.on('select', function(palette, selColor){
24268     // do something with selColor
24269 });
24270 </code></pre>
24271  * @constructor
24272  * Create a new ColorPalette
24273  * @param {Object} config The config object
24274  */
24275 Roo.ColorPalette = function(config){
24276     Roo.ColorPalette.superclass.constructor.call(this, config);
24277     this.addEvents({
24278         /**
24279              * @event select
24280              * Fires when a color is selected
24281              * @param {ColorPalette} this
24282              * @param {String} color The 6-digit color hex code (without the # symbol)
24283              */
24284         select: true
24285     });
24286
24287     if(this.handler){
24288         this.on("select", this.handler, this.scope, true);
24289     }
24290 };
24291 Roo.extend(Roo.ColorPalette, Roo.Component, {
24292     /**
24293      * @cfg {String} itemCls
24294      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24295      */
24296     itemCls : "x-color-palette",
24297     /**
24298      * @cfg {String} value
24299      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24300      * the hex codes are case-sensitive.
24301      */
24302     value : null,
24303     clickEvent:'click',
24304     // private
24305     ctype: "Roo.ColorPalette",
24306
24307     /**
24308      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24309      */
24310     allowReselect : false,
24311
24312     /**
24313      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24314      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24315      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24316      * of colors with the width setting until the box is symmetrical.</p>
24317      * <p>You can override individual colors if needed:</p>
24318      * <pre><code>
24319 var cp = new Roo.ColorPalette();
24320 cp.colors[0] = "FF0000";  // change the first box to red
24321 </code></pre>
24322
24323 Or you can provide a custom array of your own for complete control:
24324 <pre><code>
24325 var cp = new Roo.ColorPalette();
24326 cp.colors = ["000000", "993300", "333300"];
24327 </code></pre>
24328      * @type Array
24329      */
24330     colors : [
24331         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24332         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24333         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24334         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24335         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24336     ],
24337
24338     // private
24339     onRender : function(container, position){
24340         var t = new Roo.MasterTemplate(
24341             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24342         );
24343         var c = this.colors;
24344         for(var i = 0, len = c.length; i < len; i++){
24345             t.add([c[i]]);
24346         }
24347         var el = document.createElement("div");
24348         el.className = this.itemCls;
24349         t.overwrite(el);
24350         container.dom.insertBefore(el, position);
24351         this.el = Roo.get(el);
24352         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24353         if(this.clickEvent != 'click'){
24354             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24355         }
24356     },
24357
24358     // private
24359     afterRender : function(){
24360         Roo.ColorPalette.superclass.afterRender.call(this);
24361         if(this.value){
24362             var s = this.value;
24363             this.value = null;
24364             this.select(s);
24365         }
24366     },
24367
24368     // private
24369     handleClick : function(e, t){
24370         e.preventDefault();
24371         if(!this.disabled){
24372             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24373             this.select(c.toUpperCase());
24374         }
24375     },
24376
24377     /**
24378      * Selects the specified color in the palette (fires the select event)
24379      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24380      */
24381     select : function(color){
24382         color = color.replace("#", "");
24383         if(color != this.value || this.allowReselect){
24384             var el = this.el;
24385             if(this.value){
24386                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24387             }
24388             el.child("a.color-"+color).addClass("x-color-palette-sel");
24389             this.value = color;
24390             this.fireEvent("select", this, color);
24391         }
24392     }
24393 });/*
24394  * Based on:
24395  * Ext JS Library 1.1.1
24396  * Copyright(c) 2006-2007, Ext JS, LLC.
24397  *
24398  * Originally Released Under LGPL - original licence link has changed is not relivant.
24399  *
24400  * Fork - LGPL
24401  * <script type="text/javascript">
24402  */
24403  
24404 /**
24405  * @class Roo.DatePicker
24406  * @extends Roo.Component
24407  * Simple date picker class.
24408  * @constructor
24409  * Create a new DatePicker
24410  * @param {Object} config The config object
24411  */
24412 Roo.DatePicker = function(config){
24413     Roo.DatePicker.superclass.constructor.call(this, config);
24414
24415     this.value = config && config.value ?
24416                  config.value.clearTime() : new Date().clearTime();
24417
24418     this.addEvents({
24419         /**
24420              * @event select
24421              * Fires when a date is selected
24422              * @param {DatePicker} this
24423              * @param {Date} date The selected date
24424              */
24425         select: true
24426     });
24427
24428     if(this.handler){
24429         this.on("select", this.handler,  this.scope || this);
24430     }
24431     // build the disabledDatesRE
24432     if(!this.disabledDatesRE && this.disabledDates){
24433         var dd = this.disabledDates;
24434         var re = "(?:";
24435         for(var i = 0; i < dd.length; i++){
24436             re += dd[i];
24437             if(i != dd.length-1) re += "|";
24438         }
24439         this.disabledDatesRE = new RegExp(re + ")");
24440     }
24441 };
24442
24443 Roo.extend(Roo.DatePicker, Roo.Component, {
24444     /**
24445      * @cfg {String} todayText
24446      * The text to display on the button that selects the current date (defaults to "Today")
24447      */
24448     todayText : "Today",
24449     /**
24450      * @cfg {String} okText
24451      * The text to display on the ok button
24452      */
24453     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24454     /**
24455      * @cfg {String} cancelText
24456      * The text to display on the cancel button
24457      */
24458     cancelText : "Cancel",
24459     /**
24460      * @cfg {String} todayTip
24461      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24462      */
24463     todayTip : "{0} (Spacebar)",
24464     /**
24465      * @cfg {Date} minDate
24466      * Minimum allowable date (JavaScript date object, defaults to null)
24467      */
24468     minDate : null,
24469     /**
24470      * @cfg {Date} maxDate
24471      * Maximum allowable date (JavaScript date object, defaults to null)
24472      */
24473     maxDate : null,
24474     /**
24475      * @cfg {String} minText
24476      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24477      */
24478     minText : "This date is before the minimum date",
24479     /**
24480      * @cfg {String} maxText
24481      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24482      */
24483     maxText : "This date is after the maximum date",
24484     /**
24485      * @cfg {String} format
24486      * The default date format string which can be overriden for localization support.  The format must be
24487      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24488      */
24489     format : "m/d/y",
24490     /**
24491      * @cfg {Array} disabledDays
24492      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24493      */
24494     disabledDays : null,
24495     /**
24496      * @cfg {String} disabledDaysText
24497      * The tooltip to display when the date falls on a disabled day (defaults to "")
24498      */
24499     disabledDaysText : "",
24500     /**
24501      * @cfg {RegExp} disabledDatesRE
24502      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24503      */
24504     disabledDatesRE : null,
24505     /**
24506      * @cfg {String} disabledDatesText
24507      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24508      */
24509     disabledDatesText : "",
24510     /**
24511      * @cfg {Boolean} constrainToViewport
24512      * True to constrain the date picker to the viewport (defaults to true)
24513      */
24514     constrainToViewport : true,
24515     /**
24516      * @cfg {Array} monthNames
24517      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24518      */
24519     monthNames : Date.monthNames,
24520     /**
24521      * @cfg {Array} dayNames
24522      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24523      */
24524     dayNames : Date.dayNames,
24525     /**
24526      * @cfg {String} nextText
24527      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24528      */
24529     nextText: 'Next Month (Control+Right)',
24530     /**
24531      * @cfg {String} prevText
24532      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24533      */
24534     prevText: 'Previous Month (Control+Left)',
24535     /**
24536      * @cfg {String} monthYearText
24537      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24538      */
24539     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24540     /**
24541      * @cfg {Number} startDay
24542      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24543      */
24544     startDay : 0,
24545     /**
24546      * @cfg {Bool} showClear
24547      * Show a clear button (usefull for date form elements that can be blank.)
24548      */
24549     
24550     showClear: false,
24551     
24552     /**
24553      * Sets the value of the date field
24554      * @param {Date} value The date to set
24555      */
24556     setValue : function(value){
24557         var old = this.value;
24558         this.value = value.clearTime(true);
24559         if(this.el){
24560             this.update(this.value);
24561         }
24562     },
24563
24564     /**
24565      * Gets the current selected value of the date field
24566      * @return {Date} The selected date
24567      */
24568     getValue : function(){
24569         return this.value;
24570     },
24571
24572     // private
24573     focus : function(){
24574         if(this.el){
24575             this.update(this.activeDate);
24576         }
24577     },
24578
24579     // private
24580     onRender : function(container, position){
24581         var m = [
24582              '<table cellspacing="0">',
24583                 '<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>',
24584                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24585         var dn = this.dayNames;
24586         for(var i = 0; i < 7; i++){
24587             var d = this.startDay+i;
24588             if(d > 6){
24589                 d = d-7;
24590             }
24591             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24592         }
24593         m[m.length] = "</tr></thead><tbody><tr>";
24594         for(var i = 0; i < 42; i++) {
24595             if(i % 7 == 0 && i != 0){
24596                 m[m.length] = "</tr><tr>";
24597             }
24598             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24599         }
24600         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24601             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24602
24603         var el = document.createElement("div");
24604         el.className = "x-date-picker";
24605         el.innerHTML = m.join("");
24606
24607         container.dom.insertBefore(el, position);
24608
24609         this.el = Roo.get(el);
24610         this.eventEl = Roo.get(el.firstChild);
24611
24612         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24613             handler: this.showPrevMonth,
24614             scope: this,
24615             preventDefault:true,
24616             stopDefault:true
24617         });
24618
24619         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24620             handler: this.showNextMonth,
24621             scope: this,
24622             preventDefault:true,
24623             stopDefault:true
24624         });
24625
24626         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24627
24628         this.monthPicker = this.el.down('div.x-date-mp');
24629         this.monthPicker.enableDisplayMode('block');
24630         
24631         var kn = new Roo.KeyNav(this.eventEl, {
24632             "left" : function(e){
24633                 e.ctrlKey ?
24634                     this.showPrevMonth() :
24635                     this.update(this.activeDate.add("d", -1));
24636             },
24637
24638             "right" : function(e){
24639                 e.ctrlKey ?
24640                     this.showNextMonth() :
24641                     this.update(this.activeDate.add("d", 1));
24642             },
24643
24644             "up" : function(e){
24645                 e.ctrlKey ?
24646                     this.showNextYear() :
24647                     this.update(this.activeDate.add("d", -7));
24648             },
24649
24650             "down" : function(e){
24651                 e.ctrlKey ?
24652                     this.showPrevYear() :
24653                     this.update(this.activeDate.add("d", 7));
24654             },
24655
24656             "pageUp" : function(e){
24657                 this.showNextMonth();
24658             },
24659
24660             "pageDown" : function(e){
24661                 this.showPrevMonth();
24662             },
24663
24664             "enter" : function(e){
24665                 e.stopPropagation();
24666                 return true;
24667             },
24668
24669             scope : this
24670         });
24671
24672         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24673
24674         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24675
24676         this.el.unselectable();
24677         
24678         this.cells = this.el.select("table.x-date-inner tbody td");
24679         this.textNodes = this.el.query("table.x-date-inner tbody span");
24680
24681         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24682             text: "&#160;",
24683             tooltip: this.monthYearText
24684         });
24685
24686         this.mbtn.on('click', this.showMonthPicker, this);
24687         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24688
24689
24690         var today = (new Date()).dateFormat(this.format);
24691         
24692         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24693         if (this.showClear) {
24694             baseTb.add( new Roo.Toolbar.Fill());
24695         }
24696         baseTb.add({
24697             text: String.format(this.todayText, today),
24698             tooltip: String.format(this.todayTip, today),
24699             handler: this.selectToday,
24700             scope: this
24701         });
24702         
24703         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24704             
24705         //});
24706         if (this.showClear) {
24707             
24708             baseTb.add( new Roo.Toolbar.Fill());
24709             baseTb.add({
24710                 text: '&#160;',
24711                 cls: 'x-btn-icon x-btn-clear',
24712                 handler: function() {
24713                     //this.value = '';
24714                     this.fireEvent("select", this, '');
24715                 },
24716                 scope: this
24717             });
24718         }
24719         
24720         
24721         if(Roo.isIE){
24722             this.el.repaint();
24723         }
24724         this.update(this.value);
24725     },
24726
24727     createMonthPicker : function(){
24728         if(!this.monthPicker.dom.firstChild){
24729             var buf = ['<table border="0" cellspacing="0">'];
24730             for(var i = 0; i < 6; i++){
24731                 buf.push(
24732                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24733                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24734                     i == 0 ?
24735                     '<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>' :
24736                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24737                 );
24738             }
24739             buf.push(
24740                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24741                     this.okText,
24742                     '</button><button type="button" class="x-date-mp-cancel">',
24743                     this.cancelText,
24744                     '</button></td></tr>',
24745                 '</table>'
24746             );
24747             this.monthPicker.update(buf.join(''));
24748             this.monthPicker.on('click', this.onMonthClick, this);
24749             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24750
24751             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24752             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24753
24754             this.mpMonths.each(function(m, a, i){
24755                 i += 1;
24756                 if((i%2) == 0){
24757                     m.dom.xmonth = 5 + Math.round(i * .5);
24758                 }else{
24759                     m.dom.xmonth = Math.round((i-1) * .5);
24760                 }
24761             });
24762         }
24763     },
24764
24765     showMonthPicker : function(){
24766         this.createMonthPicker();
24767         var size = this.el.getSize();
24768         this.monthPicker.setSize(size);
24769         this.monthPicker.child('table').setSize(size);
24770
24771         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24772         this.updateMPMonth(this.mpSelMonth);
24773         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24774         this.updateMPYear(this.mpSelYear);
24775
24776         this.monthPicker.slideIn('t', {duration:.2});
24777     },
24778
24779     updateMPYear : function(y){
24780         this.mpyear = y;
24781         var ys = this.mpYears.elements;
24782         for(var i = 1; i <= 10; i++){
24783             var td = ys[i-1], y2;
24784             if((i%2) == 0){
24785                 y2 = y + Math.round(i * .5);
24786                 td.firstChild.innerHTML = y2;
24787                 td.xyear = y2;
24788             }else{
24789                 y2 = y - (5-Math.round(i * .5));
24790                 td.firstChild.innerHTML = y2;
24791                 td.xyear = y2;
24792             }
24793             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24794         }
24795     },
24796
24797     updateMPMonth : function(sm){
24798         this.mpMonths.each(function(m, a, i){
24799             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24800         });
24801     },
24802
24803     selectMPMonth: function(m){
24804         
24805     },
24806
24807     onMonthClick : function(e, t){
24808         e.stopEvent();
24809         var el = new Roo.Element(t), pn;
24810         if(el.is('button.x-date-mp-cancel')){
24811             this.hideMonthPicker();
24812         }
24813         else if(el.is('button.x-date-mp-ok')){
24814             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24815             this.hideMonthPicker();
24816         }
24817         else if(pn = el.up('td.x-date-mp-month', 2)){
24818             this.mpMonths.removeClass('x-date-mp-sel');
24819             pn.addClass('x-date-mp-sel');
24820             this.mpSelMonth = pn.dom.xmonth;
24821         }
24822         else if(pn = el.up('td.x-date-mp-year', 2)){
24823             this.mpYears.removeClass('x-date-mp-sel');
24824             pn.addClass('x-date-mp-sel');
24825             this.mpSelYear = pn.dom.xyear;
24826         }
24827         else if(el.is('a.x-date-mp-prev')){
24828             this.updateMPYear(this.mpyear-10);
24829         }
24830         else if(el.is('a.x-date-mp-next')){
24831             this.updateMPYear(this.mpyear+10);
24832         }
24833     },
24834
24835     onMonthDblClick : function(e, t){
24836         e.stopEvent();
24837         var el = new Roo.Element(t), pn;
24838         if(pn = el.up('td.x-date-mp-month', 2)){
24839             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24840             this.hideMonthPicker();
24841         }
24842         else if(pn = el.up('td.x-date-mp-year', 2)){
24843             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24844             this.hideMonthPicker();
24845         }
24846     },
24847
24848     hideMonthPicker : function(disableAnim){
24849         if(this.monthPicker){
24850             if(disableAnim === true){
24851                 this.monthPicker.hide();
24852             }else{
24853                 this.monthPicker.slideOut('t', {duration:.2});
24854             }
24855         }
24856     },
24857
24858     // private
24859     showPrevMonth : function(e){
24860         this.update(this.activeDate.add("mo", -1));
24861     },
24862
24863     // private
24864     showNextMonth : function(e){
24865         this.update(this.activeDate.add("mo", 1));
24866     },
24867
24868     // private
24869     showPrevYear : function(){
24870         this.update(this.activeDate.add("y", -1));
24871     },
24872
24873     // private
24874     showNextYear : function(){
24875         this.update(this.activeDate.add("y", 1));
24876     },
24877
24878     // private
24879     handleMouseWheel : function(e){
24880         var delta = e.getWheelDelta();
24881         if(delta > 0){
24882             this.showPrevMonth();
24883             e.stopEvent();
24884         } else if(delta < 0){
24885             this.showNextMonth();
24886             e.stopEvent();
24887         }
24888     },
24889
24890     // private
24891     handleDateClick : function(e, t){
24892         e.stopEvent();
24893         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24894             this.setValue(new Date(t.dateValue));
24895             this.fireEvent("select", this, this.value);
24896         }
24897     },
24898
24899     // private
24900     selectToday : function(){
24901         this.setValue(new Date().clearTime());
24902         this.fireEvent("select", this, this.value);
24903     },
24904
24905     // private
24906     update : function(date){
24907         var vd = this.activeDate;
24908         this.activeDate = date;
24909         if(vd && this.el){
24910             var t = date.getTime();
24911             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24912                 this.cells.removeClass("x-date-selected");
24913                 this.cells.each(function(c){
24914                    if(c.dom.firstChild.dateValue == t){
24915                        c.addClass("x-date-selected");
24916                        setTimeout(function(){
24917                             try{c.dom.firstChild.focus();}catch(e){}
24918                        }, 50);
24919                        return false;
24920                    }
24921                 });
24922                 return;
24923             }
24924         }
24925         var days = date.getDaysInMonth();
24926         var firstOfMonth = date.getFirstDateOfMonth();
24927         var startingPos = firstOfMonth.getDay()-this.startDay;
24928
24929         if(startingPos <= this.startDay){
24930             startingPos += 7;
24931         }
24932
24933         var pm = date.add("mo", -1);
24934         var prevStart = pm.getDaysInMonth()-startingPos;
24935
24936         var cells = this.cells.elements;
24937         var textEls = this.textNodes;
24938         days += startingPos;
24939
24940         // convert everything to numbers so it's fast
24941         var day = 86400000;
24942         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24943         var today = new Date().clearTime().getTime();
24944         var sel = date.clearTime().getTime();
24945         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24946         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24947         var ddMatch = this.disabledDatesRE;
24948         var ddText = this.disabledDatesText;
24949         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24950         var ddaysText = this.disabledDaysText;
24951         var format = this.format;
24952
24953         var setCellClass = function(cal, cell){
24954             cell.title = "";
24955             var t = d.getTime();
24956             cell.firstChild.dateValue = t;
24957             if(t == today){
24958                 cell.className += " x-date-today";
24959                 cell.title = cal.todayText;
24960             }
24961             if(t == sel){
24962                 cell.className += " x-date-selected";
24963                 setTimeout(function(){
24964                     try{cell.firstChild.focus();}catch(e){}
24965                 }, 50);
24966             }
24967             // disabling
24968             if(t < min) {
24969                 cell.className = " x-date-disabled";
24970                 cell.title = cal.minText;
24971                 return;
24972             }
24973             if(t > max) {
24974                 cell.className = " x-date-disabled";
24975                 cell.title = cal.maxText;
24976                 return;
24977             }
24978             if(ddays){
24979                 if(ddays.indexOf(d.getDay()) != -1){
24980                     cell.title = ddaysText;
24981                     cell.className = " x-date-disabled";
24982                 }
24983             }
24984             if(ddMatch && format){
24985                 var fvalue = d.dateFormat(format);
24986                 if(ddMatch.test(fvalue)){
24987                     cell.title = ddText.replace("%0", fvalue);
24988                     cell.className = " x-date-disabled";
24989                 }
24990             }
24991         };
24992
24993         var i = 0;
24994         for(; i < startingPos; i++) {
24995             textEls[i].innerHTML = (++prevStart);
24996             d.setDate(d.getDate()+1);
24997             cells[i].className = "x-date-prevday";
24998             setCellClass(this, cells[i]);
24999         }
25000         for(; i < days; i++){
25001             intDay = i - startingPos + 1;
25002             textEls[i].innerHTML = (intDay);
25003             d.setDate(d.getDate()+1);
25004             cells[i].className = "x-date-active";
25005             setCellClass(this, cells[i]);
25006         }
25007         var extraDays = 0;
25008         for(; i < 42; i++) {
25009              textEls[i].innerHTML = (++extraDays);
25010              d.setDate(d.getDate()+1);
25011              cells[i].className = "x-date-nextday";
25012              setCellClass(this, cells[i]);
25013         }
25014
25015         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25016
25017         if(!this.internalRender){
25018             var main = this.el.dom.firstChild;
25019             var w = main.offsetWidth;
25020             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25021             Roo.fly(main).setWidth(w);
25022             this.internalRender = true;
25023             // opera does not respect the auto grow header center column
25024             // then, after it gets a width opera refuses to recalculate
25025             // without a second pass
25026             if(Roo.isOpera && !this.secondPass){
25027                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25028                 this.secondPass = true;
25029                 this.update.defer(10, this, [date]);
25030             }
25031         }
25032     }
25033 });/*
25034  * Based on:
25035  * Ext JS Library 1.1.1
25036  * Copyright(c) 2006-2007, Ext JS, LLC.
25037  *
25038  * Originally Released Under LGPL - original licence link has changed is not relivant.
25039  *
25040  * Fork - LGPL
25041  * <script type="text/javascript">
25042  */
25043 /**
25044  * @class Roo.TabPanel
25045  * @extends Roo.util.Observable
25046  * A lightweight tab container.
25047  * <br><br>
25048  * Usage:
25049  * <pre><code>
25050 // basic tabs 1, built from existing content
25051 var tabs = new Roo.TabPanel("tabs1");
25052 tabs.addTab("script", "View Script");
25053 tabs.addTab("markup", "View Markup");
25054 tabs.activate("script");
25055
25056 // more advanced tabs, built from javascript
25057 var jtabs = new Roo.TabPanel("jtabs");
25058 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25059
25060 // set up the UpdateManager
25061 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25062 var updater = tab2.getUpdateManager();
25063 updater.setDefaultUrl("ajax1.htm");
25064 tab2.on('activate', updater.refresh, updater, true);
25065
25066 // Use setUrl for Ajax loading
25067 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25068 tab3.setUrl("ajax2.htm", null, true);
25069
25070 // Disabled tab
25071 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25072 tab4.disable();
25073
25074 jtabs.activate("jtabs-1");
25075  * </code></pre>
25076  * @constructor
25077  * Create a new TabPanel.
25078  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25079  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25080  */
25081 Roo.TabPanel = function(container, config){
25082     /**
25083     * The container element for this TabPanel.
25084     * @type Roo.Element
25085     */
25086     this.el = Roo.get(container, true);
25087     if(config){
25088         if(typeof config == "boolean"){
25089             this.tabPosition = config ? "bottom" : "top";
25090         }else{
25091             Roo.apply(this, config);
25092         }
25093     }
25094     if(this.tabPosition == "bottom"){
25095         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25096         this.el.addClass("x-tabs-bottom");
25097     }
25098     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25099     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25100     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25101     if(Roo.isIE){
25102         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25103     }
25104     if(this.tabPosition != "bottom"){
25105     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25106      * @type Roo.Element
25107      */
25108       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25109       this.el.addClass("x-tabs-top");
25110     }
25111     this.items = [];
25112
25113     this.bodyEl.setStyle("position", "relative");
25114
25115     this.active = null;
25116     this.activateDelegate = this.activate.createDelegate(this);
25117
25118     this.addEvents({
25119         /**
25120          * @event tabchange
25121          * Fires when the active tab changes
25122          * @param {Roo.TabPanel} this
25123          * @param {Roo.TabPanelItem} activePanel The new active tab
25124          */
25125         "tabchange": true,
25126         /**
25127          * @event beforetabchange
25128          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25129          * @param {Roo.TabPanel} this
25130          * @param {Object} e Set cancel to true on this object to cancel the tab change
25131          * @param {Roo.TabPanelItem} tab The tab being changed to
25132          */
25133         "beforetabchange" : true
25134     });
25135
25136     Roo.EventManager.onWindowResize(this.onResize, this);
25137     this.cpad = this.el.getPadding("lr");
25138     this.hiddenCount = 0;
25139
25140     Roo.TabPanel.superclass.constructor.call(this);
25141 };
25142
25143 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25144         /*
25145          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25146          */
25147     tabPosition : "top",
25148         /*
25149          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25150          */
25151     currentTabWidth : 0,
25152         /*
25153          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25154          */
25155     minTabWidth : 40,
25156         /*
25157          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25158          */
25159     maxTabWidth : 250,
25160         /*
25161          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25162          */
25163     preferredTabWidth : 175,
25164         /*
25165          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25166          */
25167     resizeTabs : false,
25168         /*
25169          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25170          */
25171     monitorResize : true,
25172
25173     /**
25174      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25175      * @param {String} id The id of the div to use <b>or create</b>
25176      * @param {String} text The text for the tab
25177      * @param {String} content (optional) Content to put in the TabPanelItem body
25178      * @param {Boolean} closable (optional) True to create a close icon on the tab
25179      * @return {Roo.TabPanelItem} The created TabPanelItem
25180      */
25181     addTab : function(id, text, content, closable){
25182         var item = new Roo.TabPanelItem(this, id, text, closable);
25183         this.addTabItem(item);
25184         if(content){
25185             item.setContent(content);
25186         }
25187         return item;
25188     },
25189
25190     /**
25191      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25192      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25193      * @return {Roo.TabPanelItem}
25194      */
25195     getTab : function(id){
25196         return this.items[id];
25197     },
25198
25199     /**
25200      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25201      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25202      */
25203     hideTab : function(id){
25204         var t = this.items[id];
25205         if(!t.isHidden()){
25206            t.setHidden(true);
25207            this.hiddenCount++;
25208            this.autoSizeTabs();
25209         }
25210     },
25211
25212     /**
25213      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25214      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25215      */
25216     unhideTab : function(id){
25217         var t = this.items[id];
25218         if(t.isHidden()){
25219            t.setHidden(false);
25220            this.hiddenCount--;
25221            this.autoSizeTabs();
25222         }
25223     },
25224
25225     /**
25226      * Adds an existing {@link Roo.TabPanelItem}.
25227      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25228      */
25229     addTabItem : function(item){
25230         this.items[item.id] = item;
25231         this.items.push(item);
25232         if(this.resizeTabs){
25233            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25234            this.autoSizeTabs();
25235         }else{
25236             item.autoSize();
25237         }
25238     },
25239
25240     /**
25241      * Removes a {@link Roo.TabPanelItem}.
25242      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25243      */
25244     removeTab : function(id){
25245         var items = this.items;
25246         var tab = items[id];
25247         if(!tab) { return; }
25248         var index = items.indexOf(tab);
25249         if(this.active == tab && items.length > 1){
25250             var newTab = this.getNextAvailable(index);
25251             if(newTab) {
25252                 newTab.activate();
25253             }
25254         }
25255         this.stripEl.dom.removeChild(tab.pnode.dom);
25256         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25257             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25258         }
25259         items.splice(index, 1);
25260         delete this.items[tab.id];
25261         tab.fireEvent("close", tab);
25262         tab.purgeListeners();
25263         this.autoSizeTabs();
25264     },
25265
25266     getNextAvailable : function(start){
25267         var items = this.items;
25268         var index = start;
25269         // look for a next tab that will slide over to
25270         // replace the one being removed
25271         while(index < items.length){
25272             var item = items[++index];
25273             if(item && !item.isHidden()){
25274                 return item;
25275             }
25276         }
25277         // if one isn't found select the previous tab (on the left)
25278         index = start;
25279         while(index >= 0){
25280             var item = items[--index];
25281             if(item && !item.isHidden()){
25282                 return item;
25283             }
25284         }
25285         return null;
25286     },
25287
25288     /**
25289      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25290      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25291      */
25292     disableTab : function(id){
25293         var tab = this.items[id];
25294         if(tab && this.active != tab){
25295             tab.disable();
25296         }
25297     },
25298
25299     /**
25300      * Enables a {@link Roo.TabPanelItem} that is disabled.
25301      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25302      */
25303     enableTab : function(id){
25304         var tab = this.items[id];
25305         tab.enable();
25306     },
25307
25308     /**
25309      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25310      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25311      * @return {Roo.TabPanelItem} The TabPanelItem.
25312      */
25313     activate : function(id){
25314         var tab = this.items[id];
25315         if(!tab){
25316             return null;
25317         }
25318         if(tab == this.active || tab.disabled){
25319             return tab;
25320         }
25321         var e = {};
25322         this.fireEvent("beforetabchange", this, e, tab);
25323         if(e.cancel !== true && !tab.disabled){
25324             if(this.active){
25325                 this.active.hide();
25326             }
25327             this.active = this.items[id];
25328             this.active.show();
25329             this.fireEvent("tabchange", this, this.active);
25330         }
25331         return tab;
25332     },
25333
25334     /**
25335      * Gets the active {@link Roo.TabPanelItem}.
25336      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25337      */
25338     getActiveTab : function(){
25339         return this.active;
25340     },
25341
25342     /**
25343      * Updates the tab body element to fit the height of the container element
25344      * for overflow scrolling
25345      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25346      */
25347     syncHeight : function(targetHeight){
25348         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25349         var bm = this.bodyEl.getMargins();
25350         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25351         this.bodyEl.setHeight(newHeight);
25352         return newHeight;
25353     },
25354
25355     onResize : function(){
25356         if(this.monitorResize){
25357             this.autoSizeTabs();
25358         }
25359     },
25360
25361     /**
25362      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25363      */
25364     beginUpdate : function(){
25365         this.updating = true;
25366     },
25367
25368     /**
25369      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25370      */
25371     endUpdate : function(){
25372         this.updating = false;
25373         this.autoSizeTabs();
25374     },
25375
25376     /**
25377      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25378      */
25379     autoSizeTabs : function(){
25380         var count = this.items.length;
25381         var vcount = count - this.hiddenCount;
25382         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25383         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25384         var availWidth = Math.floor(w / vcount);
25385         var b = this.stripBody;
25386         if(b.getWidth() > w){
25387             var tabs = this.items;
25388             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25389             if(availWidth < this.minTabWidth){
25390                 /*if(!this.sleft){    // incomplete scrolling code
25391                     this.createScrollButtons();
25392                 }
25393                 this.showScroll();
25394                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25395             }
25396         }else{
25397             if(this.currentTabWidth < this.preferredTabWidth){
25398                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25399             }
25400         }
25401     },
25402
25403     /**
25404      * Returns the number of tabs in this TabPanel.
25405      * @return {Number}
25406      */
25407      getCount : function(){
25408          return this.items.length;
25409      },
25410
25411     /**
25412      * Resizes all the tabs to the passed width
25413      * @param {Number} The new width
25414      */
25415     setTabWidth : function(width){
25416         this.currentTabWidth = width;
25417         for(var i = 0, len = this.items.length; i < len; i++) {
25418                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25419         }
25420     },
25421
25422     /**
25423      * Destroys this TabPanel
25424      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25425      */
25426     destroy : function(removeEl){
25427         Roo.EventManager.removeResizeListener(this.onResize, this);
25428         for(var i = 0, len = this.items.length; i < len; i++){
25429             this.items[i].purgeListeners();
25430         }
25431         if(removeEl === true){
25432             this.el.update("");
25433             this.el.remove();
25434         }
25435     }
25436 });
25437
25438 /**
25439  * @class Roo.TabPanelItem
25440  * @extends Roo.util.Observable
25441  * Represents an individual item (tab plus body) in a TabPanel.
25442  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25443  * @param {String} id The id of this TabPanelItem
25444  * @param {String} text The text for the tab of this TabPanelItem
25445  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25446  */
25447 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25448     /**
25449      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25450      * @type Roo.TabPanel
25451      */
25452     this.tabPanel = tabPanel;
25453     /**
25454      * The id for this TabPanelItem
25455      * @type String
25456      */
25457     this.id = id;
25458     /** @private */
25459     this.disabled = false;
25460     /** @private */
25461     this.text = text;
25462     /** @private */
25463     this.loaded = false;
25464     this.closable = closable;
25465
25466     /**
25467      * The body element for this TabPanelItem.
25468      * @type Roo.Element
25469      */
25470     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25471     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25472     this.bodyEl.setStyle("display", "block");
25473     this.bodyEl.setStyle("zoom", "1");
25474     this.hideAction();
25475
25476     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25477     /** @private */
25478     this.el = Roo.get(els.el, true);
25479     this.inner = Roo.get(els.inner, true);
25480     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25481     this.pnode = Roo.get(els.el.parentNode, true);
25482     this.el.on("mousedown", this.onTabMouseDown, this);
25483     this.el.on("click", this.onTabClick, this);
25484     /** @private */
25485     if(closable){
25486         var c = Roo.get(els.close, true);
25487         c.dom.title = this.closeText;
25488         c.addClassOnOver("close-over");
25489         c.on("click", this.closeClick, this);
25490      }
25491
25492     this.addEvents({
25493          /**
25494          * @event activate
25495          * Fires when this tab becomes the active tab.
25496          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25497          * @param {Roo.TabPanelItem} this
25498          */
25499         "activate": true,
25500         /**
25501          * @event beforeclose
25502          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25503          * @param {Roo.TabPanelItem} this
25504          * @param {Object} e Set cancel to true on this object to cancel the close.
25505          */
25506         "beforeclose": true,
25507         /**
25508          * @event close
25509          * Fires when this tab is closed.
25510          * @param {Roo.TabPanelItem} this
25511          */
25512          "close": true,
25513         /**
25514          * @event deactivate
25515          * Fires when this tab is no longer the active tab.
25516          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25517          * @param {Roo.TabPanelItem} this
25518          */
25519          "deactivate" : true
25520     });
25521     this.hidden = false;
25522
25523     Roo.TabPanelItem.superclass.constructor.call(this);
25524 };
25525
25526 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25527     purgeListeners : function(){
25528        Roo.util.Observable.prototype.purgeListeners.call(this);
25529        this.el.removeAllListeners();
25530     },
25531     /**
25532      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25533      */
25534     show : function(){
25535         this.pnode.addClass("on");
25536         this.showAction();
25537         if(Roo.isOpera){
25538             this.tabPanel.stripWrap.repaint();
25539         }
25540         this.fireEvent("activate", this.tabPanel, this);
25541     },
25542
25543     /**
25544      * Returns true if this tab is the active tab.
25545      * @return {Boolean}
25546      */
25547     isActive : function(){
25548         return this.tabPanel.getActiveTab() == this;
25549     },
25550
25551     /**
25552      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25553      */
25554     hide : function(){
25555         this.pnode.removeClass("on");
25556         this.hideAction();
25557         this.fireEvent("deactivate", this.tabPanel, this);
25558     },
25559
25560     hideAction : function(){
25561         this.bodyEl.hide();
25562         this.bodyEl.setStyle("position", "absolute");
25563         this.bodyEl.setLeft("-20000px");
25564         this.bodyEl.setTop("-20000px");
25565     },
25566
25567     showAction : function(){
25568         this.bodyEl.setStyle("position", "relative");
25569         this.bodyEl.setTop("");
25570         this.bodyEl.setLeft("");
25571         this.bodyEl.show();
25572     },
25573
25574     /**
25575      * Set the tooltip for the tab.
25576      * @param {String} tooltip The tab's tooltip
25577      */
25578     setTooltip : function(text){
25579         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25580             this.textEl.dom.qtip = text;
25581             this.textEl.dom.removeAttribute('title');
25582         }else{
25583             this.textEl.dom.title = text;
25584         }
25585     },
25586
25587     onTabClick : function(e){
25588         e.preventDefault();
25589         this.tabPanel.activate(this.id);
25590     },
25591
25592     onTabMouseDown : function(e){
25593         e.preventDefault();
25594         this.tabPanel.activate(this.id);
25595     },
25596
25597     getWidth : function(){
25598         return this.inner.getWidth();
25599     },
25600
25601     setWidth : function(width){
25602         var iwidth = width - this.pnode.getPadding("lr");
25603         this.inner.setWidth(iwidth);
25604         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25605         this.pnode.setWidth(width);
25606     },
25607
25608     /**
25609      * Show or hide the tab
25610      * @param {Boolean} hidden True to hide or false to show.
25611      */
25612     setHidden : function(hidden){
25613         this.hidden = hidden;
25614         this.pnode.setStyle("display", hidden ? "none" : "");
25615     },
25616
25617     /**
25618      * Returns true if this tab is "hidden"
25619      * @return {Boolean}
25620      */
25621     isHidden : function(){
25622         return this.hidden;
25623     },
25624
25625     /**
25626      * Returns the text for this tab
25627      * @return {String}
25628      */
25629     getText : function(){
25630         return this.text;
25631     },
25632
25633     autoSize : function(){
25634         //this.el.beginMeasure();
25635         this.textEl.setWidth(1);
25636         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25637         //this.el.endMeasure();
25638     },
25639
25640     /**
25641      * Sets the text for the tab (Note: this also sets the tooltip text)
25642      * @param {String} text The tab's text and tooltip
25643      */
25644     setText : function(text){
25645         this.text = text;
25646         this.textEl.update(text);
25647         this.setTooltip(text);
25648         if(!this.tabPanel.resizeTabs){
25649             this.autoSize();
25650         }
25651     },
25652     /**
25653      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25654      */
25655     activate : function(){
25656         this.tabPanel.activate(this.id);
25657     },
25658
25659     /**
25660      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25661      */
25662     disable : function(){
25663         if(this.tabPanel.active != this){
25664             this.disabled = true;
25665             this.pnode.addClass("disabled");
25666         }
25667     },
25668
25669     /**
25670      * Enables this TabPanelItem if it was previously disabled.
25671      */
25672     enable : function(){
25673         this.disabled = false;
25674         this.pnode.removeClass("disabled");
25675     },
25676
25677     /**
25678      * Sets the content for this TabPanelItem.
25679      * @param {String} content The content
25680      * @param {Boolean} loadScripts true to look for and load scripts
25681      */
25682     setContent : function(content, loadScripts){
25683         this.bodyEl.update(content, loadScripts);
25684     },
25685
25686     /**
25687      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25688      * @return {Roo.UpdateManager} The UpdateManager
25689      */
25690     getUpdateManager : function(){
25691         return this.bodyEl.getUpdateManager();
25692     },
25693
25694     /**
25695      * Set a URL to be used to load the content for this TabPanelItem.
25696      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25697      * @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)
25698      * @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)
25699      * @return {Roo.UpdateManager} The UpdateManager
25700      */
25701     setUrl : function(url, params, loadOnce){
25702         if(this.refreshDelegate){
25703             this.un('activate', this.refreshDelegate);
25704         }
25705         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25706         this.on("activate", this.refreshDelegate);
25707         return this.bodyEl.getUpdateManager();
25708     },
25709
25710     /** @private */
25711     _handleRefresh : function(url, params, loadOnce){
25712         if(!loadOnce || !this.loaded){
25713             var updater = this.bodyEl.getUpdateManager();
25714             updater.update(url, params, this._setLoaded.createDelegate(this));
25715         }
25716     },
25717
25718     /**
25719      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25720      *   Will fail silently if the setUrl method has not been called.
25721      *   This does not activate the panel, just updates its content.
25722      */
25723     refresh : function(){
25724         if(this.refreshDelegate){
25725            this.loaded = false;
25726            this.refreshDelegate();
25727         }
25728     },
25729
25730     /** @private */
25731     _setLoaded : function(){
25732         this.loaded = true;
25733     },
25734
25735     /** @private */
25736     closeClick : function(e){
25737         var o = {};
25738         e.stopEvent();
25739         this.fireEvent("beforeclose", this, o);
25740         if(o.cancel !== true){
25741             this.tabPanel.removeTab(this.id);
25742         }
25743     },
25744     /**
25745      * The text displayed in the tooltip for the close icon.
25746      * @type String
25747      */
25748     closeText : "Close this tab"
25749 });
25750
25751 /** @private */
25752 Roo.TabPanel.prototype.createStrip = function(container){
25753     var strip = document.createElement("div");
25754     strip.className = "x-tabs-wrap";
25755     container.appendChild(strip);
25756     return strip;
25757 };
25758 /** @private */
25759 Roo.TabPanel.prototype.createStripList = function(strip){
25760     // div wrapper for retard IE
25761     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>';
25762     return strip.firstChild.firstChild.firstChild.firstChild;
25763 };
25764 /** @private */
25765 Roo.TabPanel.prototype.createBody = function(container){
25766     var body = document.createElement("div");
25767     Roo.id(body, "tab-body");
25768     Roo.fly(body).addClass("x-tabs-body");
25769     container.appendChild(body);
25770     return body;
25771 };
25772 /** @private */
25773 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25774     var body = Roo.getDom(id);
25775     if(!body){
25776         body = document.createElement("div");
25777         body.id = id;
25778     }
25779     Roo.fly(body).addClass("x-tabs-item-body");
25780     bodyEl.insertBefore(body, bodyEl.firstChild);
25781     return body;
25782 };
25783 /** @private */
25784 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25785     var td = document.createElement("td");
25786     stripEl.appendChild(td);
25787     if(closable){
25788         td.className = "x-tabs-closable";
25789         if(!this.closeTpl){
25790             this.closeTpl = new Roo.Template(
25791                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25792                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25793                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25794             );
25795         }
25796         var el = this.closeTpl.overwrite(td, {"text": text});
25797         var close = el.getElementsByTagName("div")[0];
25798         var inner = el.getElementsByTagName("em")[0];
25799         return {"el": el, "close": close, "inner": inner};
25800     } else {
25801         if(!this.tabTpl){
25802             this.tabTpl = new Roo.Template(
25803                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25804                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25805             );
25806         }
25807         var el = this.tabTpl.overwrite(td, {"text": text});
25808         var inner = el.getElementsByTagName("em")[0];
25809         return {"el": el, "inner": inner};
25810     }
25811 };/*
25812  * Based on:
25813  * Ext JS Library 1.1.1
25814  * Copyright(c) 2006-2007, Ext JS, LLC.
25815  *
25816  * Originally Released Under LGPL - original licence link has changed is not relivant.
25817  *
25818  * Fork - LGPL
25819  * <script type="text/javascript">
25820  */
25821
25822 /**
25823  * @class Roo.Button
25824  * @extends Roo.util.Observable
25825  * Simple Button class
25826  * @cfg {String} text The button text
25827  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25828  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25829  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25830  * @cfg {Object} scope The scope of the handler
25831  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25832  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25833  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25834  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25835  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25836  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25837    applies if enableToggle = true)
25838  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25839  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25840   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25841  * @constructor
25842  * Create a new button
25843  * @param {Object} config The config object
25844  */
25845 Roo.Button = function(renderTo, config)
25846 {
25847     if (!config) {
25848         config = renderTo;
25849         renderTo = config.renderTo || false;
25850     }
25851     
25852     Roo.apply(this, config);
25853     this.addEvents({
25854         /**
25855              * @event click
25856              * Fires when this button is clicked
25857              * @param {Button} this
25858              * @param {EventObject} e The click event
25859              */
25860             "click" : true,
25861         /**
25862              * @event toggle
25863              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25864              * @param {Button} this
25865              * @param {Boolean} pressed
25866              */
25867             "toggle" : true,
25868         /**
25869              * @event mouseover
25870              * Fires when the mouse hovers over the button
25871              * @param {Button} this
25872              * @param {Event} e The event object
25873              */
25874         'mouseover' : true,
25875         /**
25876              * @event mouseout
25877              * Fires when the mouse exits the button
25878              * @param {Button} this
25879              * @param {Event} e The event object
25880              */
25881         'mouseout': true,
25882          /**
25883              * @event render
25884              * Fires when the button is rendered
25885              * @param {Button} this
25886              */
25887         'render': true
25888     });
25889     if(this.menu){
25890         this.menu = Roo.menu.MenuMgr.get(this.menu);
25891     }
25892     // register listeners first!!  - so render can be captured..
25893     Roo.util.Observable.call(this);
25894     if(renderTo){
25895         this.render(renderTo);
25896     }
25897     
25898   
25899 };
25900
25901 Roo.extend(Roo.Button, Roo.util.Observable, {
25902     /**
25903      * 
25904      */
25905     
25906     /**
25907      * Read-only. True if this button is hidden
25908      * @type Boolean
25909      */
25910     hidden : false,
25911     /**
25912      * Read-only. True if this button is disabled
25913      * @type Boolean
25914      */
25915     disabled : false,
25916     /**
25917      * Read-only. True if this button is pressed (only if enableToggle = true)
25918      * @type Boolean
25919      */
25920     pressed : false,
25921
25922     /**
25923      * @cfg {Number} tabIndex 
25924      * The DOM tabIndex for this button (defaults to undefined)
25925      */
25926     tabIndex : undefined,
25927
25928     /**
25929      * @cfg {Boolean} enableToggle
25930      * True to enable pressed/not pressed toggling (defaults to false)
25931      */
25932     enableToggle: false,
25933     /**
25934      * @cfg {Mixed} menu
25935      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25936      */
25937     menu : undefined,
25938     /**
25939      * @cfg {String} menuAlign
25940      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25941      */
25942     menuAlign : "tl-bl?",
25943
25944     /**
25945      * @cfg {String} iconCls
25946      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25947      */
25948     iconCls : undefined,
25949     /**
25950      * @cfg {String} type
25951      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25952      */
25953     type : 'button',
25954
25955     // private
25956     menuClassTarget: 'tr',
25957
25958     /**
25959      * @cfg {String} clickEvent
25960      * The type of event to map to the button's event handler (defaults to 'click')
25961      */
25962     clickEvent : 'click',
25963
25964     /**
25965      * @cfg {Boolean} handleMouseEvents
25966      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25967      */
25968     handleMouseEvents : true,
25969
25970     /**
25971      * @cfg {String} tooltipType
25972      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25973      */
25974     tooltipType : 'qtip',
25975
25976     /**
25977      * @cfg {String} cls
25978      * A CSS class to apply to the button's main element.
25979      */
25980     
25981     /**
25982      * @cfg {Roo.Template} template (Optional)
25983      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25984      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25985      * require code modifications if required elements (e.g. a button) aren't present.
25986      */
25987
25988     // private
25989     render : function(renderTo){
25990         var btn;
25991         if(this.hideParent){
25992             this.parentEl = Roo.get(renderTo);
25993         }
25994         if(!this.dhconfig){
25995             if(!this.template){
25996                 if(!Roo.Button.buttonTemplate){
25997                     // hideous table template
25998                     Roo.Button.buttonTemplate = new Roo.Template(
25999                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26000                         '<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>',
26001                         "</tr></tbody></table>");
26002                 }
26003                 this.template = Roo.Button.buttonTemplate;
26004             }
26005             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26006             var btnEl = btn.child("button:first");
26007             btnEl.on('focus', this.onFocus, this);
26008             btnEl.on('blur', this.onBlur, this);
26009             if(this.cls){
26010                 btn.addClass(this.cls);
26011             }
26012             if(this.icon){
26013                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26014             }
26015             if(this.iconCls){
26016                 btnEl.addClass(this.iconCls);
26017                 if(!this.cls){
26018                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26019                 }
26020             }
26021             if(this.tabIndex !== undefined){
26022                 btnEl.dom.tabIndex = this.tabIndex;
26023             }
26024             if(this.tooltip){
26025                 if(typeof this.tooltip == 'object'){
26026                     Roo.QuickTips.tips(Roo.apply({
26027                           target: btnEl.id
26028                     }, this.tooltip));
26029                 } else {
26030                     btnEl.dom[this.tooltipType] = this.tooltip;
26031                 }
26032             }
26033         }else{
26034             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26035         }
26036         this.el = btn;
26037         if(this.id){
26038             this.el.dom.id = this.el.id = this.id;
26039         }
26040         if(this.menu){
26041             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26042             this.menu.on("show", this.onMenuShow, this);
26043             this.menu.on("hide", this.onMenuHide, this);
26044         }
26045         btn.addClass("x-btn");
26046         if(Roo.isIE && !Roo.isIE7){
26047             this.autoWidth.defer(1, this);
26048         }else{
26049             this.autoWidth();
26050         }
26051         if(this.handleMouseEvents){
26052             btn.on("mouseover", this.onMouseOver, this);
26053             btn.on("mouseout", this.onMouseOut, this);
26054             btn.on("mousedown", this.onMouseDown, this);
26055         }
26056         btn.on(this.clickEvent, this.onClick, this);
26057         //btn.on("mouseup", this.onMouseUp, this);
26058         if(this.hidden){
26059             this.hide();
26060         }
26061         if(this.disabled){
26062             this.disable();
26063         }
26064         Roo.ButtonToggleMgr.register(this);
26065         if(this.pressed){
26066             this.el.addClass("x-btn-pressed");
26067         }
26068         if(this.repeat){
26069             var repeater = new Roo.util.ClickRepeater(btn,
26070                 typeof this.repeat == "object" ? this.repeat : {}
26071             );
26072             repeater.on("click", this.onClick,  this);
26073         }
26074         
26075         this.fireEvent('render', this);
26076         
26077     },
26078     /**
26079      * Returns the button's underlying element
26080      * @return {Roo.Element} The element
26081      */
26082     getEl : function(){
26083         return this.el;  
26084     },
26085     
26086     /**
26087      * Destroys this Button and removes any listeners.
26088      */
26089     destroy : function(){
26090         Roo.ButtonToggleMgr.unregister(this);
26091         this.el.removeAllListeners();
26092         this.purgeListeners();
26093         this.el.remove();
26094     },
26095
26096     // private
26097     autoWidth : function(){
26098         if(this.el){
26099             this.el.setWidth("auto");
26100             if(Roo.isIE7 && Roo.isStrict){
26101                 var ib = this.el.child('button');
26102                 if(ib && ib.getWidth() > 20){
26103                     ib.clip();
26104                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26105                 }
26106             }
26107             if(this.minWidth){
26108                 if(this.hidden){
26109                     this.el.beginMeasure();
26110                 }
26111                 if(this.el.getWidth() < this.minWidth){
26112                     this.el.setWidth(this.minWidth);
26113                 }
26114                 if(this.hidden){
26115                     this.el.endMeasure();
26116                 }
26117             }
26118         }
26119     },
26120
26121     /**
26122      * Assigns this button's click handler
26123      * @param {Function} handler The function to call when the button is clicked
26124      * @param {Object} scope (optional) Scope for the function passed in
26125      */
26126     setHandler : function(handler, scope){
26127         this.handler = handler;
26128         this.scope = scope;  
26129     },
26130     
26131     /**
26132      * Sets this button's text
26133      * @param {String} text The button text
26134      */
26135     setText : function(text){
26136         this.text = text;
26137         if(this.el){
26138             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26139         }
26140         this.autoWidth();
26141     },
26142     
26143     /**
26144      * Gets the text for this button
26145      * @return {String} The button text
26146      */
26147     getText : function(){
26148         return this.text;  
26149     },
26150     
26151     /**
26152      * Show this button
26153      */
26154     show: function(){
26155         this.hidden = false;
26156         if(this.el){
26157             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26158         }
26159     },
26160     
26161     /**
26162      * Hide this button
26163      */
26164     hide: function(){
26165         this.hidden = true;
26166         if(this.el){
26167             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26168         }
26169     },
26170     
26171     /**
26172      * Convenience function for boolean show/hide
26173      * @param {Boolean} visible True to show, false to hide
26174      */
26175     setVisible: function(visible){
26176         if(visible) {
26177             this.show();
26178         }else{
26179             this.hide();
26180         }
26181     },
26182     
26183     /**
26184      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26185      * @param {Boolean} state (optional) Force a particular state
26186      */
26187     toggle : function(state){
26188         state = state === undefined ? !this.pressed : state;
26189         if(state != this.pressed){
26190             if(state){
26191                 this.el.addClass("x-btn-pressed");
26192                 this.pressed = true;
26193                 this.fireEvent("toggle", this, true);
26194             }else{
26195                 this.el.removeClass("x-btn-pressed");
26196                 this.pressed = false;
26197                 this.fireEvent("toggle", this, false);
26198             }
26199             if(this.toggleHandler){
26200                 this.toggleHandler.call(this.scope || this, this, state);
26201             }
26202         }
26203     },
26204     
26205     /**
26206      * Focus the button
26207      */
26208     focus : function(){
26209         this.el.child('button:first').focus();
26210     },
26211     
26212     /**
26213      * Disable this button
26214      */
26215     disable : function(){
26216         if(this.el){
26217             this.el.addClass("x-btn-disabled");
26218         }
26219         this.disabled = true;
26220     },
26221     
26222     /**
26223      * Enable this button
26224      */
26225     enable : function(){
26226         if(this.el){
26227             this.el.removeClass("x-btn-disabled");
26228         }
26229         this.disabled = false;
26230     },
26231
26232     /**
26233      * Convenience function for boolean enable/disable
26234      * @param {Boolean} enabled True to enable, false to disable
26235      */
26236     setDisabled : function(v){
26237         this[v !== true ? "enable" : "disable"]();
26238     },
26239
26240     // private
26241     onClick : function(e){
26242         if(e){
26243             e.preventDefault();
26244         }
26245         if(e.button != 0){
26246             return;
26247         }
26248         if(!this.disabled){
26249             if(this.enableToggle){
26250                 this.toggle();
26251             }
26252             if(this.menu && !this.menu.isVisible()){
26253                 this.menu.show(this.el, this.menuAlign);
26254             }
26255             this.fireEvent("click", this, e);
26256             if(this.handler){
26257                 this.el.removeClass("x-btn-over");
26258                 this.handler.call(this.scope || this, this, e);
26259             }
26260         }
26261     },
26262     // private
26263     onMouseOver : function(e){
26264         if(!this.disabled){
26265             this.el.addClass("x-btn-over");
26266             this.fireEvent('mouseover', this, e);
26267         }
26268     },
26269     // private
26270     onMouseOut : function(e){
26271         if(!e.within(this.el,  true)){
26272             this.el.removeClass("x-btn-over");
26273             this.fireEvent('mouseout', this, e);
26274         }
26275     },
26276     // private
26277     onFocus : function(e){
26278         if(!this.disabled){
26279             this.el.addClass("x-btn-focus");
26280         }
26281     },
26282     // private
26283     onBlur : function(e){
26284         this.el.removeClass("x-btn-focus");
26285     },
26286     // private
26287     onMouseDown : function(e){
26288         if(!this.disabled && e.button == 0){
26289             this.el.addClass("x-btn-click");
26290             Roo.get(document).on('mouseup', this.onMouseUp, this);
26291         }
26292     },
26293     // private
26294     onMouseUp : function(e){
26295         if(e.button == 0){
26296             this.el.removeClass("x-btn-click");
26297             Roo.get(document).un('mouseup', this.onMouseUp, this);
26298         }
26299     },
26300     // private
26301     onMenuShow : function(e){
26302         this.el.addClass("x-btn-menu-active");
26303     },
26304     // private
26305     onMenuHide : function(e){
26306         this.el.removeClass("x-btn-menu-active");
26307     }   
26308 });
26309
26310 // Private utility class used by Button
26311 Roo.ButtonToggleMgr = function(){
26312    var groups = {};
26313    
26314    function toggleGroup(btn, state){
26315        if(state){
26316            var g = groups[btn.toggleGroup];
26317            for(var i = 0, l = g.length; i < l; i++){
26318                if(g[i] != btn){
26319                    g[i].toggle(false);
26320                }
26321            }
26322        }
26323    }
26324    
26325    return {
26326        register : function(btn){
26327            if(!btn.toggleGroup){
26328                return;
26329            }
26330            var g = groups[btn.toggleGroup];
26331            if(!g){
26332                g = groups[btn.toggleGroup] = [];
26333            }
26334            g.push(btn);
26335            btn.on("toggle", toggleGroup);
26336        },
26337        
26338        unregister : function(btn){
26339            if(!btn.toggleGroup){
26340                return;
26341            }
26342            var g = groups[btn.toggleGroup];
26343            if(g){
26344                g.remove(btn);
26345                btn.un("toggle", toggleGroup);
26346            }
26347        }
26348    };
26349 }();/*
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.SplitButton
26362  * @extends Roo.Button
26363  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26364  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26365  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26366  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26367  * @cfg {String} arrowTooltip The title attribute of the arrow
26368  * @constructor
26369  * Create a new menu button
26370  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26371  * @param {Object} config The config object
26372  */
26373 Roo.SplitButton = function(renderTo, config){
26374     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26375     /**
26376      * @event arrowclick
26377      * Fires when this button's arrow is clicked
26378      * @param {SplitButton} this
26379      * @param {EventObject} e The click event
26380      */
26381     this.addEvents({"arrowclick":true});
26382 };
26383
26384 Roo.extend(Roo.SplitButton, Roo.Button, {
26385     render : function(renderTo){
26386         // this is one sweet looking template!
26387         var tpl = new Roo.Template(
26388             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26389             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26390             '<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>',
26391             "</tbody></table></td><td>",
26392             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26393             '<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>',
26394             "</tbody></table></td></tr></table>"
26395         );
26396         var btn = tpl.append(renderTo, [this.text, this.type], true);
26397         var btnEl = btn.child("button");
26398         if(this.cls){
26399             btn.addClass(this.cls);
26400         }
26401         if(this.icon){
26402             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26403         }
26404         if(this.iconCls){
26405             btnEl.addClass(this.iconCls);
26406             if(!this.cls){
26407                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26408             }
26409         }
26410         this.el = btn;
26411         if(this.handleMouseEvents){
26412             btn.on("mouseover", this.onMouseOver, this);
26413             btn.on("mouseout", this.onMouseOut, this);
26414             btn.on("mousedown", this.onMouseDown, this);
26415             btn.on("mouseup", this.onMouseUp, this);
26416         }
26417         btn.on(this.clickEvent, this.onClick, this);
26418         if(this.tooltip){
26419             if(typeof this.tooltip == 'object'){
26420                 Roo.QuickTips.tips(Roo.apply({
26421                       target: btnEl.id
26422                 }, this.tooltip));
26423             } else {
26424                 btnEl.dom[this.tooltipType] = this.tooltip;
26425             }
26426         }
26427         if(this.arrowTooltip){
26428             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26429         }
26430         if(this.hidden){
26431             this.hide();
26432         }
26433         if(this.disabled){
26434             this.disable();
26435         }
26436         if(this.pressed){
26437             this.el.addClass("x-btn-pressed");
26438         }
26439         if(Roo.isIE && !Roo.isIE7){
26440             this.autoWidth.defer(1, this);
26441         }else{
26442             this.autoWidth();
26443         }
26444         if(this.menu){
26445             this.menu.on("show", this.onMenuShow, this);
26446             this.menu.on("hide", this.onMenuHide, this);
26447         }
26448         this.fireEvent('render', this);
26449     },
26450
26451     // private
26452     autoWidth : function(){
26453         if(this.el){
26454             var tbl = this.el.child("table:first");
26455             var tbl2 = this.el.child("table:last");
26456             this.el.setWidth("auto");
26457             tbl.setWidth("auto");
26458             if(Roo.isIE7 && Roo.isStrict){
26459                 var ib = this.el.child('button:first');
26460                 if(ib && ib.getWidth() > 20){
26461                     ib.clip();
26462                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26463                 }
26464             }
26465             if(this.minWidth){
26466                 if(this.hidden){
26467                     this.el.beginMeasure();
26468                 }
26469                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26470                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26471                 }
26472                 if(this.hidden){
26473                     this.el.endMeasure();
26474                 }
26475             }
26476             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26477         } 
26478     },
26479     /**
26480      * Sets this button's click handler
26481      * @param {Function} handler The function to call when the button is clicked
26482      * @param {Object} scope (optional) Scope for the function passed above
26483      */
26484     setHandler : function(handler, scope){
26485         this.handler = handler;
26486         this.scope = scope;  
26487     },
26488     
26489     /**
26490      * Sets this button's arrow click handler
26491      * @param {Function} handler The function to call when the arrow is clicked
26492      * @param {Object} scope (optional) Scope for the function passed above
26493      */
26494     setArrowHandler : function(handler, scope){
26495         this.arrowHandler = handler;
26496         this.scope = scope;  
26497     },
26498     
26499     /**
26500      * Focus the button
26501      */
26502     focus : function(){
26503         if(this.el){
26504             this.el.child("button:first").focus();
26505         }
26506     },
26507
26508     // private
26509     onClick : function(e){
26510         e.preventDefault();
26511         if(!this.disabled){
26512             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26513                 if(this.menu && !this.menu.isVisible()){
26514                     this.menu.show(this.el, this.menuAlign);
26515                 }
26516                 this.fireEvent("arrowclick", this, e);
26517                 if(this.arrowHandler){
26518                     this.arrowHandler.call(this.scope || this, this, e);
26519                 }
26520             }else{
26521                 this.fireEvent("click", this, e);
26522                 if(this.handler){
26523                     this.handler.call(this.scope || this, this, e);
26524                 }
26525             }
26526         }
26527     },
26528     // private
26529     onMouseDown : function(e){
26530         if(!this.disabled){
26531             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26532         }
26533     },
26534     // private
26535     onMouseUp : function(e){
26536         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26537     }   
26538 });
26539
26540
26541 // backwards compat
26542 Roo.MenuButton = Roo.SplitButton;/*
26543  * Based on:
26544  * Ext JS Library 1.1.1
26545  * Copyright(c) 2006-2007, Ext JS, LLC.
26546  *
26547  * Originally Released Under LGPL - original licence link has changed is not relivant.
26548  *
26549  * Fork - LGPL
26550  * <script type="text/javascript">
26551  */
26552
26553 /**
26554  * @class Roo.Toolbar
26555  * Basic Toolbar class.
26556  * @constructor
26557  * Creates a new Toolbar
26558  * @param {Object} config The config object
26559  */ 
26560 Roo.Toolbar = function(container, buttons, config)
26561 {
26562     /// old consturctor format still supported..
26563     if(container instanceof Array){ // omit the container for later rendering
26564         buttons = container;
26565         config = buttons;
26566         container = null;
26567     }
26568     if (typeof(container) == 'object' && container.xtype) {
26569         config = container;
26570         container = config.container;
26571         buttons = config.buttons; // not really - use items!!
26572     }
26573     var xitems = [];
26574     if (config && config.items) {
26575         xitems = config.items;
26576         delete config.items;
26577     }
26578     Roo.apply(this, config);
26579     this.buttons = buttons;
26580     
26581     if(container){
26582         this.render(container);
26583     }
26584     Roo.each(xitems, function(b) {
26585         this.add(b);
26586     }, this);
26587     
26588 };
26589
26590 Roo.Toolbar.prototype = {
26591     /**
26592      * @cfg {Roo.data.Store} items
26593      * array of button configs or elements to add
26594      */
26595     
26596     /**
26597      * @cfg {String/HTMLElement/Element} container
26598      * The id or element that will contain the toolbar
26599      */
26600     // private
26601     render : function(ct){
26602         this.el = Roo.get(ct);
26603         if(this.cls){
26604             this.el.addClass(this.cls);
26605         }
26606         // using a table allows for vertical alignment
26607         // 100% width is needed by Safari...
26608         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26609         this.tr = this.el.child("tr", true);
26610         var autoId = 0;
26611         this.items = new Roo.util.MixedCollection(false, function(o){
26612             return o.id || ("item" + (++autoId));
26613         });
26614         if(this.buttons){
26615             this.add.apply(this, this.buttons);
26616             delete this.buttons;
26617         }
26618     },
26619
26620     /**
26621      * Adds element(s) to the toolbar -- this function takes a variable number of 
26622      * arguments of mixed type and adds them to the toolbar.
26623      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26624      * <ul>
26625      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26626      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26627      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26628      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26629      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26630      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26631      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26632      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26633      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26634      * </ul>
26635      * @param {Mixed} arg2
26636      * @param {Mixed} etc.
26637      */
26638     add : function(){
26639         var a = arguments, l = a.length;
26640         for(var i = 0; i < l; i++){
26641             this._add(a[i]);
26642         }
26643     },
26644     // private..
26645     _add : function(el) {
26646         
26647         if (el.xtype) {
26648             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26649         }
26650         
26651         if (el.applyTo){ // some kind of form field
26652             return this.addField(el);
26653         } 
26654         if (el.render){ // some kind of Toolbar.Item
26655             return this.addItem(el);
26656         }
26657         if (typeof el == "string"){ // string
26658             if(el == "separator" || el == "-"){
26659                 return this.addSeparator();
26660             }
26661             if (el == " "){
26662                 return this.addSpacer();
26663             }
26664             if(el == "->"){
26665                 return this.addFill();
26666             }
26667             return this.addText(el);
26668             
26669         }
26670         if(el.tagName){ // element
26671             return this.addElement(el);
26672         }
26673         if(typeof el == "object"){ // must be button config?
26674             return this.addButton(el);
26675         }
26676         // and now what?!?!
26677         return false;
26678         
26679     },
26680     
26681     /**
26682      * Add an Xtype element
26683      * @param {Object} xtype Xtype Object
26684      * @return {Object} created Object
26685      */
26686     addxtype : function(e){
26687         return this.add(e);  
26688     },
26689     
26690     /**
26691      * Returns the Element for this toolbar.
26692      * @return {Roo.Element}
26693      */
26694     getEl : function(){
26695         return this.el;  
26696     },
26697     
26698     /**
26699      * Adds a separator
26700      * @return {Roo.Toolbar.Item} The separator item
26701      */
26702     addSeparator : function(){
26703         return this.addItem(new Roo.Toolbar.Separator());
26704     },
26705
26706     /**
26707      * Adds a spacer element
26708      * @return {Roo.Toolbar.Spacer} The spacer item
26709      */
26710     addSpacer : function(){
26711         return this.addItem(new Roo.Toolbar.Spacer());
26712     },
26713
26714     /**
26715      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26716      * @return {Roo.Toolbar.Fill} The fill item
26717      */
26718     addFill : function(){
26719         return this.addItem(new Roo.Toolbar.Fill());
26720     },
26721
26722     /**
26723      * Adds any standard HTML element to the toolbar
26724      * @param {String/HTMLElement/Element} el The element or id of the element to add
26725      * @return {Roo.Toolbar.Item} The element's item
26726      */
26727     addElement : function(el){
26728         return this.addItem(new Roo.Toolbar.Item(el));
26729     },
26730     /**
26731      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26732      * @type Roo.util.MixedCollection  
26733      */
26734     items : false,
26735      
26736     /**
26737      * Adds any Toolbar.Item or subclass
26738      * @param {Roo.Toolbar.Item} item
26739      * @return {Roo.Toolbar.Item} The item
26740      */
26741     addItem : function(item){
26742         var td = this.nextBlock();
26743         item.render(td);
26744         this.items.add(item);
26745         return item;
26746     },
26747     
26748     /**
26749      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26750      * @param {Object/Array} config A button config or array of configs
26751      * @return {Roo.Toolbar.Button/Array}
26752      */
26753     addButton : function(config){
26754         if(config instanceof Array){
26755             var buttons = [];
26756             for(var i = 0, len = config.length; i < len; i++) {
26757                 buttons.push(this.addButton(config[i]));
26758             }
26759             return buttons;
26760         }
26761         var b = config;
26762         if(!(config instanceof Roo.Toolbar.Button)){
26763             b = config.split ?
26764                 new Roo.Toolbar.SplitButton(config) :
26765                 new Roo.Toolbar.Button(config);
26766         }
26767         var td = this.nextBlock();
26768         b.render(td);
26769         this.items.add(b);
26770         return b;
26771     },
26772     
26773     /**
26774      * Adds text to the toolbar
26775      * @param {String} text The text to add
26776      * @return {Roo.Toolbar.Item} The element's item
26777      */
26778     addText : function(text){
26779         return this.addItem(new Roo.Toolbar.TextItem(text));
26780     },
26781     
26782     /**
26783      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26784      * @param {Number} index The index where the item is to be inserted
26785      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26786      * @return {Roo.Toolbar.Button/Item}
26787      */
26788     insertButton : function(index, item){
26789         if(item instanceof Array){
26790             var buttons = [];
26791             for(var i = 0, len = item.length; i < len; i++) {
26792                buttons.push(this.insertButton(index + i, item[i]));
26793             }
26794             return buttons;
26795         }
26796         if (!(item instanceof Roo.Toolbar.Button)){
26797            item = new Roo.Toolbar.Button(item);
26798         }
26799         var td = document.createElement("td");
26800         this.tr.insertBefore(td, this.tr.childNodes[index]);
26801         item.render(td);
26802         this.items.insert(index, item);
26803         return item;
26804     },
26805     
26806     /**
26807      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26808      * @param {Object} config
26809      * @return {Roo.Toolbar.Item} The element's item
26810      */
26811     addDom : function(config, returnEl){
26812         var td = this.nextBlock();
26813         Roo.DomHelper.overwrite(td, config);
26814         var ti = new Roo.Toolbar.Item(td.firstChild);
26815         ti.render(td);
26816         this.items.add(ti);
26817         return ti;
26818     },
26819
26820     /**
26821      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26822      * @type Roo.util.MixedCollection  
26823      */
26824     fields : false,
26825     
26826     /**
26827      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26828      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26829      * @param {Roo.form.Field} field
26830      * @return {Roo.ToolbarItem}
26831      */
26832      
26833       
26834     addField : function(field) {
26835         if (!this.fields) {
26836             var autoId = 0;
26837             this.fields = new Roo.util.MixedCollection(false, function(o){
26838                 return o.id || ("item" + (++autoId));
26839             });
26840
26841         }
26842         
26843         var td = this.nextBlock();
26844         field.render(td);
26845         var ti = new Roo.Toolbar.Item(td.firstChild);
26846         ti.render(td);
26847         this.items.add(ti);
26848         this.fields.add(field);
26849         return ti;
26850     },
26851     /**
26852      * Hide the toolbar
26853      * @method hide
26854      */
26855      
26856       
26857     hide : function()
26858     {
26859         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26860         this.el.child('div').hide();
26861     },
26862     /**
26863      * Show the toolbar
26864      * @method show
26865      */
26866     show : function()
26867     {
26868         this.el.child('div').show();
26869     },
26870       
26871     // private
26872     nextBlock : function(){
26873         var td = document.createElement("td");
26874         this.tr.appendChild(td);
26875         return td;
26876     },
26877
26878     // private
26879     destroy : function(){
26880         if(this.items){ // rendered?
26881             Roo.destroy.apply(Roo, this.items.items);
26882         }
26883         if(this.fields){ // rendered?
26884             Roo.destroy.apply(Roo, this.fields.items);
26885         }
26886         Roo.Element.uncache(this.el, this.tr);
26887     }
26888 };
26889
26890 /**
26891  * @class Roo.Toolbar.Item
26892  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26893  * @constructor
26894  * Creates a new Item
26895  * @param {HTMLElement} el 
26896  */
26897 Roo.Toolbar.Item = function(el){
26898     this.el = Roo.getDom(el);
26899     this.id = Roo.id(this.el);
26900     this.hidden = false;
26901 };
26902
26903 Roo.Toolbar.Item.prototype = {
26904     
26905     /**
26906      * Get this item's HTML Element
26907      * @return {HTMLElement}
26908      */
26909     getEl : function(){
26910        return this.el;  
26911     },
26912
26913     // private
26914     render : function(td){
26915         this.td = td;
26916         td.appendChild(this.el);
26917     },
26918     
26919     /**
26920      * Removes and destroys this item.
26921      */
26922     destroy : function(){
26923         this.td.parentNode.removeChild(this.td);
26924     },
26925     
26926     /**
26927      * Shows this item.
26928      */
26929     show: function(){
26930         this.hidden = false;
26931         this.td.style.display = "";
26932     },
26933     
26934     /**
26935      * Hides this item.
26936      */
26937     hide: function(){
26938         this.hidden = true;
26939         this.td.style.display = "none";
26940     },
26941     
26942     /**
26943      * Convenience function for boolean show/hide.
26944      * @param {Boolean} visible true to show/false to hide
26945      */
26946     setVisible: function(visible){
26947         if(visible) {
26948             this.show();
26949         }else{
26950             this.hide();
26951         }
26952     },
26953     
26954     /**
26955      * Try to focus this item.
26956      */
26957     focus : function(){
26958         Roo.fly(this.el).focus();
26959     },
26960     
26961     /**
26962      * Disables this item.
26963      */
26964     disable : function(){
26965         Roo.fly(this.td).addClass("x-item-disabled");
26966         this.disabled = true;
26967         this.el.disabled = true;
26968     },
26969     
26970     /**
26971      * Enables this item.
26972      */
26973     enable : function(){
26974         Roo.fly(this.td).removeClass("x-item-disabled");
26975         this.disabled = false;
26976         this.el.disabled = false;
26977     }
26978 };
26979
26980
26981 /**
26982  * @class Roo.Toolbar.Separator
26983  * @extends Roo.Toolbar.Item
26984  * A simple toolbar separator class
26985  * @constructor
26986  * Creates a new Separator
26987  */
26988 Roo.Toolbar.Separator = function(){
26989     var s = document.createElement("span");
26990     s.className = "ytb-sep";
26991     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26992 };
26993 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26994     enable:Roo.emptyFn,
26995     disable:Roo.emptyFn,
26996     focus:Roo.emptyFn
26997 });
26998
26999 /**
27000  * @class Roo.Toolbar.Spacer
27001  * @extends Roo.Toolbar.Item
27002  * A simple element that adds extra horizontal space to a toolbar.
27003  * @constructor
27004  * Creates a new Spacer
27005  */
27006 Roo.Toolbar.Spacer = function(){
27007     var s = document.createElement("div");
27008     s.className = "ytb-spacer";
27009     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27010 };
27011 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27012     enable:Roo.emptyFn,
27013     disable:Roo.emptyFn,
27014     focus:Roo.emptyFn
27015 });
27016
27017 /**
27018  * @class Roo.Toolbar.Fill
27019  * @extends Roo.Toolbar.Spacer
27020  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27021  * @constructor
27022  * Creates a new Spacer
27023  */
27024 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27025     // private
27026     render : function(td){
27027         td.style.width = '100%';
27028         Roo.Toolbar.Fill.superclass.render.call(this, td);
27029     }
27030 });
27031
27032 /**
27033  * @class Roo.Toolbar.TextItem
27034  * @extends Roo.Toolbar.Item
27035  * A simple class that renders text directly into a toolbar.
27036  * @constructor
27037  * Creates a new TextItem
27038  * @param {String} text
27039  */
27040 Roo.Toolbar.TextItem = function(text){
27041     if (typeof(text) == 'object') {
27042         text = text.text;
27043     }
27044     var s = document.createElement("span");
27045     s.className = "ytb-text";
27046     s.innerHTML = text;
27047     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27048 };
27049 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27050     enable:Roo.emptyFn,
27051     disable:Roo.emptyFn,
27052     focus:Roo.emptyFn
27053 });
27054
27055 /**
27056  * @class Roo.Toolbar.Button
27057  * @extends Roo.Button
27058  * A button that renders into a toolbar.
27059  * @constructor
27060  * Creates a new Button
27061  * @param {Object} config A standard {@link Roo.Button} config object
27062  */
27063 Roo.Toolbar.Button = function(config){
27064     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27065 };
27066 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27067     render : function(td){
27068         this.td = td;
27069         Roo.Toolbar.Button.superclass.render.call(this, td);
27070     },
27071     
27072     /**
27073      * Removes and destroys this button
27074      */
27075     destroy : function(){
27076         Roo.Toolbar.Button.superclass.destroy.call(this);
27077         this.td.parentNode.removeChild(this.td);
27078     },
27079     
27080     /**
27081      * Shows this button
27082      */
27083     show: function(){
27084         this.hidden = false;
27085         this.td.style.display = "";
27086     },
27087     
27088     /**
27089      * Hides this button
27090      */
27091     hide: function(){
27092         this.hidden = true;
27093         this.td.style.display = "none";
27094     },
27095
27096     /**
27097      * Disables this item
27098      */
27099     disable : function(){
27100         Roo.fly(this.td).addClass("x-item-disabled");
27101         this.disabled = true;
27102     },
27103
27104     /**
27105      * Enables this item
27106      */
27107     enable : function(){
27108         Roo.fly(this.td).removeClass("x-item-disabled");
27109         this.disabled = false;
27110     }
27111 });
27112 // backwards compat
27113 Roo.ToolbarButton = Roo.Toolbar.Button;
27114
27115 /**
27116  * @class Roo.Toolbar.SplitButton
27117  * @extends Roo.SplitButton
27118  * A menu button that renders into a toolbar.
27119  * @constructor
27120  * Creates a new SplitButton
27121  * @param {Object} config A standard {@link Roo.SplitButton} config object
27122  */
27123 Roo.Toolbar.SplitButton = function(config){
27124     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27125 };
27126 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27127     render : function(td){
27128         this.td = td;
27129         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27130     },
27131     
27132     /**
27133      * Removes and destroys this button
27134      */
27135     destroy : function(){
27136         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27137         this.td.parentNode.removeChild(this.td);
27138     },
27139     
27140     /**
27141      * Shows this button
27142      */
27143     show: function(){
27144         this.hidden = false;
27145         this.td.style.display = "";
27146     },
27147     
27148     /**
27149      * Hides this button
27150      */
27151     hide: function(){
27152         this.hidden = true;
27153         this.td.style.display = "none";
27154     }
27155 });
27156
27157 // backwards compat
27158 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27159  * Based on:
27160  * Ext JS Library 1.1.1
27161  * Copyright(c) 2006-2007, Ext JS, LLC.
27162  *
27163  * Originally Released Under LGPL - original licence link has changed is not relivant.
27164  *
27165  * Fork - LGPL
27166  * <script type="text/javascript">
27167  */
27168  
27169 /**
27170  * @class Roo.PagingToolbar
27171  * @extends Roo.Toolbar
27172  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27173  * @constructor
27174  * Create a new PagingToolbar
27175  * @param {Object} config The config object
27176  */
27177 Roo.PagingToolbar = function(el, ds, config)
27178 {
27179     // old args format still supported... - xtype is prefered..
27180     if (typeof(el) == 'object' && el.xtype) {
27181         // created from xtype...
27182         config = el;
27183         ds = el.dataSource;
27184         el = config.container;
27185     }
27186     var items = [];
27187     if (config.items) {
27188         items = config.items;
27189         config.items = [];
27190     }
27191     
27192     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27193     this.ds = ds;
27194     this.cursor = 0;
27195     this.renderButtons(this.el);
27196     this.bind(ds);
27197     
27198     // supprot items array.
27199    
27200     Roo.each(items, function(e) {
27201         this.add(Roo.factory(e));
27202     },this);
27203     
27204 };
27205
27206 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27207     /**
27208      * @cfg {Roo.data.Store} dataSource
27209      * The underlying data store providing the paged data
27210      */
27211     /**
27212      * @cfg {String/HTMLElement/Element} container
27213      * container The id or element that will contain the toolbar
27214      */
27215     /**
27216      * @cfg {Boolean} displayInfo
27217      * True to display the displayMsg (defaults to false)
27218      */
27219     /**
27220      * @cfg {Number} pageSize
27221      * The number of records to display per page (defaults to 20)
27222      */
27223     pageSize: 20,
27224     /**
27225      * @cfg {String} displayMsg
27226      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27227      */
27228     displayMsg : 'Displaying {0} - {1} of {2}',
27229     /**
27230      * @cfg {String} emptyMsg
27231      * The message to display when no records are found (defaults to "No data to display")
27232      */
27233     emptyMsg : 'No data to display',
27234     /**
27235      * Customizable piece of the default paging text (defaults to "Page")
27236      * @type String
27237      */
27238     beforePageText : "Page",
27239     /**
27240      * Customizable piece of the default paging text (defaults to "of %0")
27241      * @type String
27242      */
27243     afterPageText : "of {0}",
27244     /**
27245      * Customizable piece of the default paging text (defaults to "First Page")
27246      * @type String
27247      */
27248     firstText : "First Page",
27249     /**
27250      * Customizable piece of the default paging text (defaults to "Previous Page")
27251      * @type String
27252      */
27253     prevText : "Previous Page",
27254     /**
27255      * Customizable piece of the default paging text (defaults to "Next Page")
27256      * @type String
27257      */
27258     nextText : "Next Page",
27259     /**
27260      * Customizable piece of the default paging text (defaults to "Last Page")
27261      * @type String
27262      */
27263     lastText : "Last Page",
27264     /**
27265      * Customizable piece of the default paging text (defaults to "Refresh")
27266      * @type String
27267      */
27268     refreshText : "Refresh",
27269
27270     // private
27271     renderButtons : function(el){
27272         Roo.PagingToolbar.superclass.render.call(this, el);
27273         this.first = this.addButton({
27274             tooltip: this.firstText,
27275             cls: "x-btn-icon x-grid-page-first",
27276             disabled: true,
27277             handler: this.onClick.createDelegate(this, ["first"])
27278         });
27279         this.prev = this.addButton({
27280             tooltip: this.prevText,
27281             cls: "x-btn-icon x-grid-page-prev",
27282             disabled: true,
27283             handler: this.onClick.createDelegate(this, ["prev"])
27284         });
27285         //this.addSeparator();
27286         this.add(this.beforePageText);
27287         this.field = Roo.get(this.addDom({
27288            tag: "input",
27289            type: "text",
27290            size: "3",
27291            value: "1",
27292            cls: "x-grid-page-number"
27293         }).el);
27294         this.field.on("keydown", this.onPagingKeydown, this);
27295         this.field.on("focus", function(){this.dom.select();});
27296         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27297         this.field.setHeight(18);
27298         //this.addSeparator();
27299         this.next = this.addButton({
27300             tooltip: this.nextText,
27301             cls: "x-btn-icon x-grid-page-next",
27302             disabled: true,
27303             handler: this.onClick.createDelegate(this, ["next"])
27304         });
27305         this.last = this.addButton({
27306             tooltip: this.lastText,
27307             cls: "x-btn-icon x-grid-page-last",
27308             disabled: true,
27309             handler: this.onClick.createDelegate(this, ["last"])
27310         });
27311         //this.addSeparator();
27312         this.loading = this.addButton({
27313             tooltip: this.refreshText,
27314             cls: "x-btn-icon x-grid-loading",
27315             handler: this.onClick.createDelegate(this, ["refresh"])
27316         });
27317
27318         if(this.displayInfo){
27319             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27320         }
27321     },
27322
27323     // private
27324     updateInfo : function(){
27325         if(this.displayEl){
27326             var count = this.ds.getCount();
27327             var msg = count == 0 ?
27328                 this.emptyMsg :
27329                 String.format(
27330                     this.displayMsg,
27331                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27332                 );
27333             this.displayEl.update(msg);
27334         }
27335     },
27336
27337     // private
27338     onLoad : function(ds, r, o){
27339        this.cursor = o.params ? o.params.start : 0;
27340        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27341
27342        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27343        this.field.dom.value = ap;
27344        this.first.setDisabled(ap == 1);
27345        this.prev.setDisabled(ap == 1);
27346        this.next.setDisabled(ap == ps);
27347        this.last.setDisabled(ap == ps);
27348        this.loading.enable();
27349        this.updateInfo();
27350     },
27351
27352     // private
27353     getPageData : function(){
27354         var total = this.ds.getTotalCount();
27355         return {
27356             total : total,
27357             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27358             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27359         };
27360     },
27361
27362     // private
27363     onLoadError : function(){
27364         this.loading.enable();
27365     },
27366
27367     // private
27368     onPagingKeydown : function(e){
27369         var k = e.getKey();
27370         var d = this.getPageData();
27371         if(k == e.RETURN){
27372             var v = this.field.dom.value, pageNum;
27373             if(!v || isNaN(pageNum = parseInt(v, 10))){
27374                 this.field.dom.value = d.activePage;
27375                 return;
27376             }
27377             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27378             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27379             e.stopEvent();
27380         }
27381         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))
27382         {
27383           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27384           this.field.dom.value = pageNum;
27385           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27386           e.stopEvent();
27387         }
27388         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27389         {
27390           var v = this.field.dom.value, pageNum; 
27391           var increment = (e.shiftKey) ? 10 : 1;
27392           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27393             increment *= -1;
27394           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27395             this.field.dom.value = d.activePage;
27396             return;
27397           }
27398           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27399           {
27400             this.field.dom.value = parseInt(v, 10) + increment;
27401             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27402             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27403           }
27404           e.stopEvent();
27405         }
27406     },
27407
27408     // private
27409     beforeLoad : function(){
27410         if(this.loading){
27411             this.loading.disable();
27412         }
27413     },
27414
27415     // private
27416     onClick : function(which){
27417         var ds = this.ds;
27418         switch(which){
27419             case "first":
27420                 ds.load({params:{start: 0, limit: this.pageSize}});
27421             break;
27422             case "prev":
27423                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27424             break;
27425             case "next":
27426                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27427             break;
27428             case "last":
27429                 var total = ds.getTotalCount();
27430                 var extra = total % this.pageSize;
27431                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27432                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27433             break;
27434             case "refresh":
27435                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27436             break;
27437         }
27438     },
27439
27440     /**
27441      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27442      * @param {Roo.data.Store} store The data store to unbind
27443      */
27444     unbind : function(ds){
27445         ds.un("beforeload", this.beforeLoad, this);
27446         ds.un("load", this.onLoad, this);
27447         ds.un("loadexception", this.onLoadError, this);
27448         ds.un("remove", this.updateInfo, this);
27449         ds.un("add", this.updateInfo, this);
27450         this.ds = undefined;
27451     },
27452
27453     /**
27454      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27455      * @param {Roo.data.Store} store The data store to bind
27456      */
27457     bind : function(ds){
27458         ds.on("beforeload", this.beforeLoad, this);
27459         ds.on("load", this.onLoad, this);
27460         ds.on("loadexception", this.onLoadError, this);
27461         ds.on("remove", this.updateInfo, this);
27462         ds.on("add", this.updateInfo, this);
27463         this.ds = ds;
27464     }
27465 });/*
27466  * Based on:
27467  * Ext JS Library 1.1.1
27468  * Copyright(c) 2006-2007, Ext JS, LLC.
27469  *
27470  * Originally Released Under LGPL - original licence link has changed is not relivant.
27471  *
27472  * Fork - LGPL
27473  * <script type="text/javascript">
27474  */
27475
27476 /**
27477  * @class Roo.Resizable
27478  * @extends Roo.util.Observable
27479  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27480  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27481  * 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
27482  * the element will be wrapped for you automatically.</p>
27483  * <p>Here is the list of valid resize handles:</p>
27484  * <pre>
27485 Value   Description
27486 ------  -------------------
27487  'n'     north
27488  's'     south
27489  'e'     east
27490  'w'     west
27491  'nw'    northwest
27492  'sw'    southwest
27493  'se'    southeast
27494  'ne'    northeast
27495  'hd'    horizontal drag
27496  'all'   all
27497 </pre>
27498  * <p>Here's an example showing the creation of a typical Resizable:</p>
27499  * <pre><code>
27500 var resizer = new Roo.Resizable("element-id", {
27501     handles: 'all',
27502     minWidth: 200,
27503     minHeight: 100,
27504     maxWidth: 500,
27505     maxHeight: 400,
27506     pinned: true
27507 });
27508 resizer.on("resize", myHandler);
27509 </code></pre>
27510  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27511  * resizer.east.setDisplayed(false);</p>
27512  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27513  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27514  * resize operation's new size (defaults to [0, 0])
27515  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27516  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27517  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27518  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27519  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27520  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27521  * @cfg {Number} width The width of the element in pixels (defaults to null)
27522  * @cfg {Number} height The height of the element in pixels (defaults to null)
27523  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27524  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27525  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27526  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27527  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27528  * in favor of the handles config option (defaults to false)
27529  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27530  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27531  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27532  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27533  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27534  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27535  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27536  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27537  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27538  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27539  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27540  * @constructor
27541  * Create a new resizable component
27542  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27543  * @param {Object} config configuration options
27544   */
27545 Roo.Resizable = function(el, config)
27546 {
27547     this.el = Roo.get(el);
27548
27549     if(config && config.wrap){
27550         config.resizeChild = this.el;
27551         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27552         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27553         this.el.setStyle("overflow", "hidden");
27554         this.el.setPositioning(config.resizeChild.getPositioning());
27555         config.resizeChild.clearPositioning();
27556         if(!config.width || !config.height){
27557             var csize = config.resizeChild.getSize();
27558             this.el.setSize(csize.width, csize.height);
27559         }
27560         if(config.pinned && !config.adjustments){
27561             config.adjustments = "auto";
27562         }
27563     }
27564
27565     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27566     this.proxy.unselectable();
27567     this.proxy.enableDisplayMode('block');
27568
27569     Roo.apply(this, config);
27570
27571     if(this.pinned){
27572         this.disableTrackOver = true;
27573         this.el.addClass("x-resizable-pinned");
27574     }
27575     // if the element isn't positioned, make it relative
27576     var position = this.el.getStyle("position");
27577     if(position != "absolute" && position != "fixed"){
27578         this.el.setStyle("position", "relative");
27579     }
27580     if(!this.handles){ // no handles passed, must be legacy style
27581         this.handles = 's,e,se';
27582         if(this.multiDirectional){
27583             this.handles += ',n,w';
27584         }
27585     }
27586     if(this.handles == "all"){
27587         this.handles = "n s e w ne nw se sw";
27588     }
27589     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27590     var ps = Roo.Resizable.positions;
27591     for(var i = 0, len = hs.length; i < len; i++){
27592         if(hs[i] && ps[hs[i]]){
27593             var pos = ps[hs[i]];
27594             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27595         }
27596     }
27597     // legacy
27598     this.corner = this.southeast;
27599     
27600     // updateBox = the box can move..
27601     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27602         this.updateBox = true;
27603     }
27604
27605     this.activeHandle = null;
27606
27607     if(this.resizeChild){
27608         if(typeof this.resizeChild == "boolean"){
27609             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27610         }else{
27611             this.resizeChild = Roo.get(this.resizeChild, true);
27612         }
27613     }
27614     
27615     if(this.adjustments == "auto"){
27616         var rc = this.resizeChild;
27617         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27618         if(rc && (hw || hn)){
27619             rc.position("relative");
27620             rc.setLeft(hw ? hw.el.getWidth() : 0);
27621             rc.setTop(hn ? hn.el.getHeight() : 0);
27622         }
27623         this.adjustments = [
27624             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27625             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27626         ];
27627     }
27628
27629     if(this.draggable){
27630         this.dd = this.dynamic ?
27631             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27632         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27633     }
27634
27635     // public events
27636     this.addEvents({
27637         /**
27638          * @event beforeresize
27639          * Fired before resize is allowed. Set enabled to false to cancel resize.
27640          * @param {Roo.Resizable} this
27641          * @param {Roo.EventObject} e The mousedown event
27642          */
27643         "beforeresize" : true,
27644         /**
27645          * @event resize
27646          * Fired after a resize.
27647          * @param {Roo.Resizable} this
27648          * @param {Number} width The new width
27649          * @param {Number} height The new height
27650          * @param {Roo.EventObject} e The mouseup event
27651          */
27652         "resize" : true
27653     });
27654
27655     if(this.width !== null && this.height !== null){
27656         this.resizeTo(this.width, this.height);
27657     }else{
27658         this.updateChildSize();
27659     }
27660     if(Roo.isIE){
27661         this.el.dom.style.zoom = 1;
27662     }
27663     Roo.Resizable.superclass.constructor.call(this);
27664 };
27665
27666 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27667         resizeChild : false,
27668         adjustments : [0, 0],
27669         minWidth : 5,
27670         minHeight : 5,
27671         maxWidth : 10000,
27672         maxHeight : 10000,
27673         enabled : true,
27674         animate : false,
27675         duration : .35,
27676         dynamic : false,
27677         handles : false,
27678         multiDirectional : false,
27679         disableTrackOver : false,
27680         easing : 'easeOutStrong',
27681         widthIncrement : 0,
27682         heightIncrement : 0,
27683         pinned : false,
27684         width : null,
27685         height : null,
27686         preserveRatio : false,
27687         transparent: false,
27688         minX: 0,
27689         minY: 0,
27690         draggable: false,
27691
27692         /**
27693          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27694          */
27695         constrainTo: undefined,
27696         /**
27697          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27698          */
27699         resizeRegion: undefined,
27700
27701
27702     /**
27703      * Perform a manual resize
27704      * @param {Number} width
27705      * @param {Number} height
27706      */
27707     resizeTo : function(width, height){
27708         this.el.setSize(width, height);
27709         this.updateChildSize();
27710         this.fireEvent("resize", this, width, height, null);
27711     },
27712
27713     // private
27714     startSizing : function(e, handle){
27715         this.fireEvent("beforeresize", this, e);
27716         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27717
27718             if(!this.overlay){
27719                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27720                 this.overlay.unselectable();
27721                 this.overlay.enableDisplayMode("block");
27722                 this.overlay.on("mousemove", this.onMouseMove, this);
27723                 this.overlay.on("mouseup", this.onMouseUp, this);
27724             }
27725             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27726
27727             this.resizing = true;
27728             this.startBox = this.el.getBox();
27729             this.startPoint = e.getXY();
27730             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27731                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27732
27733             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27734             this.overlay.show();
27735
27736             if(this.constrainTo) {
27737                 var ct = Roo.get(this.constrainTo);
27738                 this.resizeRegion = ct.getRegion().adjust(
27739                     ct.getFrameWidth('t'),
27740                     ct.getFrameWidth('l'),
27741                     -ct.getFrameWidth('b'),
27742                     -ct.getFrameWidth('r')
27743                 );
27744             }
27745
27746             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27747             this.proxy.show();
27748             this.proxy.setBox(this.startBox);
27749             if(!this.dynamic){
27750                 this.proxy.setStyle('visibility', 'visible');
27751             }
27752         }
27753     },
27754
27755     // private
27756     onMouseDown : function(handle, e){
27757         if(this.enabled){
27758             e.stopEvent();
27759             this.activeHandle = handle;
27760             this.startSizing(e, handle);
27761         }
27762     },
27763
27764     // private
27765     onMouseUp : function(e){
27766         var size = this.resizeElement();
27767         this.resizing = false;
27768         this.handleOut();
27769         this.overlay.hide();
27770         this.proxy.hide();
27771         this.fireEvent("resize", this, size.width, size.height, e);
27772     },
27773
27774     // private
27775     updateChildSize : function(){
27776         if(this.resizeChild){
27777             var el = this.el;
27778             var child = this.resizeChild;
27779             var adj = this.adjustments;
27780             if(el.dom.offsetWidth){
27781                 var b = el.getSize(true);
27782                 child.setSize(b.width+adj[0], b.height+adj[1]);
27783             }
27784             // Second call here for IE
27785             // The first call enables instant resizing and
27786             // the second call corrects scroll bars if they
27787             // exist
27788             if(Roo.isIE){
27789                 setTimeout(function(){
27790                     if(el.dom.offsetWidth){
27791                         var b = el.getSize(true);
27792                         child.setSize(b.width+adj[0], b.height+adj[1]);
27793                     }
27794                 }, 10);
27795             }
27796         }
27797     },
27798
27799     // private
27800     snap : function(value, inc, min){
27801         if(!inc || !value) return value;
27802         var newValue = value;
27803         var m = value % inc;
27804         if(m > 0){
27805             if(m > (inc/2)){
27806                 newValue = value + (inc-m);
27807             }else{
27808                 newValue = value - m;
27809             }
27810         }
27811         return Math.max(min, newValue);
27812     },
27813
27814     // private
27815     resizeElement : function(){
27816         var box = this.proxy.getBox();
27817         if(this.updateBox){
27818             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27819         }else{
27820             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27821         }
27822         this.updateChildSize();
27823         if(!this.dynamic){
27824             this.proxy.hide();
27825         }
27826         return box;
27827     },
27828
27829     // private
27830     constrain : function(v, diff, m, mx){
27831         if(v - diff < m){
27832             diff = v - m;
27833         }else if(v - diff > mx){
27834             diff = mx - v;
27835         }
27836         return diff;
27837     },
27838
27839     // private
27840     onMouseMove : function(e){
27841         if(this.enabled){
27842             try{// try catch so if something goes wrong the user doesn't get hung
27843
27844             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27845                 return;
27846             }
27847
27848             //var curXY = this.startPoint;
27849             var curSize = this.curSize || this.startBox;
27850             var x = this.startBox.x, y = this.startBox.y;
27851             var ox = x, oy = y;
27852             var w = curSize.width, h = curSize.height;
27853             var ow = w, oh = h;
27854             var mw = this.minWidth, mh = this.minHeight;
27855             var mxw = this.maxWidth, mxh = this.maxHeight;
27856             var wi = this.widthIncrement;
27857             var hi = this.heightIncrement;
27858
27859             var eventXY = e.getXY();
27860             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27861             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27862
27863             var pos = this.activeHandle.position;
27864
27865             switch(pos){
27866                 case "east":
27867                     w += diffX;
27868                     w = Math.min(Math.max(mw, w), mxw);
27869                     break;
27870              
27871                 case "south":
27872                     h += diffY;
27873                     h = Math.min(Math.max(mh, h), mxh);
27874                     break;
27875                 case "southeast":
27876                     w += diffX;
27877                     h += diffY;
27878                     w = Math.min(Math.max(mw, w), mxw);
27879                     h = Math.min(Math.max(mh, h), mxh);
27880                     break;
27881                 case "north":
27882                     diffY = this.constrain(h, diffY, mh, mxh);
27883                     y += diffY;
27884                     h -= diffY;
27885                     break;
27886                 case "hdrag":
27887                     
27888                     if (wi) {
27889                         var adiffX = Math.abs(diffX);
27890                         var sub = (adiffX % wi); // how much 
27891                         if (sub > (wi/2)) { // far enough to snap
27892                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27893                         } else {
27894                             // remove difference.. 
27895                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27896                         }
27897                     }
27898                     x += diffX;
27899                     x = Math.max(this.minX, x);
27900                     break;
27901                 case "west":
27902                     diffX = this.constrain(w, diffX, mw, mxw);
27903                     x += diffX;
27904                     w -= diffX;
27905                     break;
27906                 case "northeast":
27907                     w += diffX;
27908                     w = Math.min(Math.max(mw, w), mxw);
27909                     diffY = this.constrain(h, diffY, mh, mxh);
27910                     y += diffY;
27911                     h -= diffY;
27912                     break;
27913                 case "northwest":
27914                     diffX = this.constrain(w, diffX, mw, mxw);
27915                     diffY = this.constrain(h, diffY, mh, mxh);
27916                     y += diffY;
27917                     h -= diffY;
27918                     x += diffX;
27919                     w -= diffX;
27920                     break;
27921                case "southwest":
27922                     diffX = this.constrain(w, diffX, mw, mxw);
27923                     h += diffY;
27924                     h = Math.min(Math.max(mh, h), mxh);
27925                     x += diffX;
27926                     w -= diffX;
27927                     break;
27928             }
27929
27930             var sw = this.snap(w, wi, mw);
27931             var sh = this.snap(h, hi, mh);
27932             if(sw != w || sh != h){
27933                 switch(pos){
27934                     case "northeast":
27935                         y -= sh - h;
27936                     break;
27937                     case "north":
27938                         y -= sh - h;
27939                         break;
27940                     case "southwest":
27941                         x -= sw - w;
27942                     break;
27943                     case "west":
27944                         x -= sw - w;
27945                         break;
27946                     case "northwest":
27947                         x -= sw - w;
27948                         y -= sh - h;
27949                     break;
27950                 }
27951                 w = sw;
27952                 h = sh;
27953             }
27954
27955             if(this.preserveRatio){
27956                 switch(pos){
27957                     case "southeast":
27958                     case "east":
27959                         h = oh * (w/ow);
27960                         h = Math.min(Math.max(mh, h), mxh);
27961                         w = ow * (h/oh);
27962                        break;
27963                     case "south":
27964                         w = ow * (h/oh);
27965                         w = Math.min(Math.max(mw, w), mxw);
27966                         h = oh * (w/ow);
27967                         break;
27968                     case "northeast":
27969                         w = ow * (h/oh);
27970                         w = Math.min(Math.max(mw, w), mxw);
27971                         h = oh * (w/ow);
27972                     break;
27973                     case "north":
27974                         var tw = w;
27975                         w = ow * (h/oh);
27976                         w = Math.min(Math.max(mw, w), mxw);
27977                         h = oh * (w/ow);
27978                         x += (tw - w) / 2;
27979                         break;
27980                     case "southwest":
27981                         h = oh * (w/ow);
27982                         h = Math.min(Math.max(mh, h), mxh);
27983                         var tw = w;
27984                         w = ow * (h/oh);
27985                         x += tw - w;
27986                         break;
27987                     case "west":
27988                         var th = h;
27989                         h = oh * (w/ow);
27990                         h = Math.min(Math.max(mh, h), mxh);
27991                         y += (th - h) / 2;
27992                         var tw = w;
27993                         w = ow * (h/oh);
27994                         x += tw - w;
27995                        break;
27996                     case "northwest":
27997                         var tw = w;
27998                         var th = h;
27999                         h = oh * (w/ow);
28000                         h = Math.min(Math.max(mh, h), mxh);
28001                         w = ow * (h/oh);
28002                         y += th - h;
28003                         x += tw - w;
28004                        break;
28005
28006                 }
28007             }
28008             if (pos == 'hdrag') {
28009                 w = ow;
28010             }
28011             this.proxy.setBounds(x, y, w, h);
28012             if(this.dynamic){
28013                 this.resizeElement();
28014             }
28015             }catch(e){}
28016         }
28017     },
28018
28019     // private
28020     handleOver : function(){
28021         if(this.enabled){
28022             this.el.addClass("x-resizable-over");
28023         }
28024     },
28025
28026     // private
28027     handleOut : function(){
28028         if(!this.resizing){
28029             this.el.removeClass("x-resizable-over");
28030         }
28031     },
28032
28033     /**
28034      * Returns the element this component is bound to.
28035      * @return {Roo.Element}
28036      */
28037     getEl : function(){
28038         return this.el;
28039     },
28040
28041     /**
28042      * Returns the resizeChild element (or null).
28043      * @return {Roo.Element}
28044      */
28045     getResizeChild : function(){
28046         return this.resizeChild;
28047     },
28048
28049     /**
28050      * Destroys this resizable. If the element was wrapped and
28051      * removeEl is not true then the element remains.
28052      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28053      */
28054     destroy : function(removeEl){
28055         this.proxy.remove();
28056         if(this.overlay){
28057             this.overlay.removeAllListeners();
28058             this.overlay.remove();
28059         }
28060         var ps = Roo.Resizable.positions;
28061         for(var k in ps){
28062             if(typeof ps[k] != "function" && this[ps[k]]){
28063                 var h = this[ps[k]];
28064                 h.el.removeAllListeners();
28065                 h.el.remove();
28066             }
28067         }
28068         if(removeEl){
28069             this.el.update("");
28070             this.el.remove();
28071         }
28072     }
28073 });
28074
28075 // private
28076 // hash to map config positions to true positions
28077 Roo.Resizable.positions = {
28078     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28079     hd: "hdrag"
28080 };
28081
28082 // private
28083 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28084     if(!this.tpl){
28085         // only initialize the template if resizable is used
28086         var tpl = Roo.DomHelper.createTemplate(
28087             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28088         );
28089         tpl.compile();
28090         Roo.Resizable.Handle.prototype.tpl = tpl;
28091     }
28092     this.position = pos;
28093     this.rz = rz;
28094     // show north drag fro topdra
28095     var handlepos = pos == 'hdrag' ? 'north' : pos;
28096     
28097     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28098     if (pos == 'hdrag') {
28099         this.el.setStyle('cursor', 'pointer');
28100     }
28101     this.el.unselectable();
28102     if(transparent){
28103         this.el.setOpacity(0);
28104     }
28105     this.el.on("mousedown", this.onMouseDown, this);
28106     if(!disableTrackOver){
28107         this.el.on("mouseover", this.onMouseOver, this);
28108         this.el.on("mouseout", this.onMouseOut, this);
28109     }
28110 };
28111
28112 // private
28113 Roo.Resizable.Handle.prototype = {
28114     afterResize : function(rz){
28115         // do nothing
28116     },
28117     // private
28118     onMouseDown : function(e){
28119         this.rz.onMouseDown(this, e);
28120     },
28121     // private
28122     onMouseOver : function(e){
28123         this.rz.handleOver(this, e);
28124     },
28125     // private
28126     onMouseOut : function(e){
28127         this.rz.handleOut(this, e);
28128     }
28129 };/*
28130  * Based on:
28131  * Ext JS Library 1.1.1
28132  * Copyright(c) 2006-2007, Ext JS, LLC.
28133  *
28134  * Originally Released Under LGPL - original licence link has changed is not relivant.
28135  *
28136  * Fork - LGPL
28137  * <script type="text/javascript">
28138  */
28139
28140 /**
28141  * @class Roo.Editor
28142  * @extends Roo.Component
28143  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28144  * @constructor
28145  * Create a new Editor
28146  * @param {Roo.form.Field} field The Field object (or descendant)
28147  * @param {Object} config The config object
28148  */
28149 Roo.Editor = function(field, config){
28150     Roo.Editor.superclass.constructor.call(this, config);
28151     this.field = field;
28152     this.addEvents({
28153         /**
28154              * @event beforestartedit
28155              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28156              * false from the handler of this event.
28157              * @param {Editor} this
28158              * @param {Roo.Element} boundEl The underlying element bound to this editor
28159              * @param {Mixed} value The field value being set
28160              */
28161         "beforestartedit" : true,
28162         /**
28163              * @event startedit
28164              * Fires when this editor is displayed
28165              * @param {Roo.Element} boundEl The underlying element bound to this editor
28166              * @param {Mixed} value The starting field value
28167              */
28168         "startedit" : true,
28169         /**
28170              * @event beforecomplete
28171              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28172              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28173              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28174              * event will not fire since no edit actually occurred.
28175              * @param {Editor} this
28176              * @param {Mixed} value The current field value
28177              * @param {Mixed} startValue The original field value
28178              */
28179         "beforecomplete" : true,
28180         /**
28181              * @event complete
28182              * Fires after editing is complete and any changed value has been written to the underlying field.
28183              * @param {Editor} this
28184              * @param {Mixed} value The current field value
28185              * @param {Mixed} startValue The original field value
28186              */
28187         "complete" : true,
28188         /**
28189          * @event specialkey
28190          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28191          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28192          * @param {Roo.form.Field} this
28193          * @param {Roo.EventObject} e The event object
28194          */
28195         "specialkey" : true
28196     });
28197 };
28198
28199 Roo.extend(Roo.Editor, Roo.Component, {
28200     /**
28201      * @cfg {Boolean/String} autosize
28202      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28203      * or "height" to adopt the height only (defaults to false)
28204      */
28205     /**
28206      * @cfg {Boolean} revertInvalid
28207      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28208      * validation fails (defaults to true)
28209      */
28210     /**
28211      * @cfg {Boolean} ignoreNoChange
28212      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28213      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28214      * will never be ignored.
28215      */
28216     /**
28217      * @cfg {Boolean} hideEl
28218      * False to keep the bound element visible while the editor is displayed (defaults to true)
28219      */
28220     /**
28221      * @cfg {Mixed} value
28222      * The data value of the underlying field (defaults to "")
28223      */
28224     value : "",
28225     /**
28226      * @cfg {String} alignment
28227      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28228      */
28229     alignment: "c-c?",
28230     /**
28231      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28232      * for bottom-right shadow (defaults to "frame")
28233      */
28234     shadow : "frame",
28235     /**
28236      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28237      */
28238     constrain : false,
28239     /**
28240      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28241      */
28242     completeOnEnter : false,
28243     /**
28244      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28245      */
28246     cancelOnEsc : false,
28247     /**
28248      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28249      */
28250     updateEl : false,
28251
28252     // private
28253     onRender : function(ct, position){
28254         this.el = new Roo.Layer({
28255             shadow: this.shadow,
28256             cls: "x-editor",
28257             parentEl : ct,
28258             shim : this.shim,
28259             shadowOffset:4,
28260             id: this.id,
28261             constrain: this.constrain
28262         });
28263         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28264         if(this.field.msgTarget != 'title'){
28265             this.field.msgTarget = 'qtip';
28266         }
28267         this.field.render(this.el);
28268         if(Roo.isGecko){
28269             this.field.el.dom.setAttribute('autocomplete', 'off');
28270         }
28271         this.field.on("specialkey", this.onSpecialKey, this);
28272         if(this.swallowKeys){
28273             this.field.el.swallowEvent(['keydown','keypress']);
28274         }
28275         this.field.show();
28276         this.field.on("blur", this.onBlur, this);
28277         if(this.field.grow){
28278             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28279         }
28280     },
28281
28282     onSpecialKey : function(field, e){
28283         //Roo.log('editor onSpecialKey');
28284         if(this.completeOnEnter && e.getKey() == e.ENTER){
28285             e.stopEvent();
28286             this.completeEdit();
28287         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28288             this.cancelEdit();
28289         }else{
28290             this.fireEvent('specialkey', field, e);
28291         }
28292     },
28293
28294     /**
28295      * Starts the editing process and shows the editor.
28296      * @param {String/HTMLElement/Element} el The element to edit
28297      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28298       * to the innerHTML of el.
28299      */
28300     startEdit : function(el, value){
28301         if(this.editing){
28302             this.completeEdit();
28303         }
28304         this.boundEl = Roo.get(el);
28305         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28306         if(!this.rendered){
28307             this.render(this.parentEl || document.body);
28308         }
28309         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28310             return;
28311         }
28312         this.startValue = v;
28313         this.field.setValue(v);
28314         if(this.autoSize){
28315             var sz = this.boundEl.getSize();
28316             switch(this.autoSize){
28317                 case "width":
28318                 this.setSize(sz.width,  "");
28319                 break;
28320                 case "height":
28321                 this.setSize("",  sz.height);
28322                 break;
28323                 default:
28324                 this.setSize(sz.width,  sz.height);
28325             }
28326         }
28327         this.el.alignTo(this.boundEl, this.alignment);
28328         this.editing = true;
28329         if(Roo.QuickTips){
28330             Roo.QuickTips.disable();
28331         }
28332         this.show();
28333     },
28334
28335     /**
28336      * Sets the height and width of this editor.
28337      * @param {Number} width The new width
28338      * @param {Number} height The new height
28339      */
28340     setSize : function(w, h){
28341         this.field.setSize(w, h);
28342         if(this.el){
28343             this.el.sync();
28344         }
28345     },
28346
28347     /**
28348      * Realigns the editor to the bound field based on the current alignment config value.
28349      */
28350     realign : function(){
28351         this.el.alignTo(this.boundEl, this.alignment);
28352     },
28353
28354     /**
28355      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28356      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28357      */
28358     completeEdit : function(remainVisible){
28359         if(!this.editing){
28360             return;
28361         }
28362         var v = this.getValue();
28363         if(this.revertInvalid !== false && !this.field.isValid()){
28364             v = this.startValue;
28365             this.cancelEdit(true);
28366         }
28367         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28368             this.editing = false;
28369             this.hide();
28370             return;
28371         }
28372         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28373             this.editing = false;
28374             if(this.updateEl && this.boundEl){
28375                 this.boundEl.update(v);
28376             }
28377             if(remainVisible !== true){
28378                 this.hide();
28379             }
28380             this.fireEvent("complete", this, v, this.startValue);
28381         }
28382     },
28383
28384     // private
28385     onShow : function(){
28386         this.el.show();
28387         if(this.hideEl !== false){
28388             this.boundEl.hide();
28389         }
28390         this.field.show();
28391         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28392             this.fixIEFocus = true;
28393             this.deferredFocus.defer(50, this);
28394         }else{
28395             this.field.focus();
28396         }
28397         this.fireEvent("startedit", this.boundEl, this.startValue);
28398     },
28399
28400     deferredFocus : function(){
28401         if(this.editing){
28402             this.field.focus();
28403         }
28404     },
28405
28406     /**
28407      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28408      * reverted to the original starting value.
28409      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28410      * cancel (defaults to false)
28411      */
28412     cancelEdit : function(remainVisible){
28413         if(this.editing){
28414             this.setValue(this.startValue);
28415             if(remainVisible !== true){
28416                 this.hide();
28417             }
28418         }
28419     },
28420
28421     // private
28422     onBlur : function(){
28423         if(this.allowBlur !== true && this.editing){
28424             this.completeEdit();
28425         }
28426     },
28427
28428     // private
28429     onHide : function(){
28430         if(this.editing){
28431             this.completeEdit();
28432             return;
28433         }
28434         this.field.blur();
28435         if(this.field.collapse){
28436             this.field.collapse();
28437         }
28438         this.el.hide();
28439         if(this.hideEl !== false){
28440             this.boundEl.show();
28441         }
28442         if(Roo.QuickTips){
28443             Roo.QuickTips.enable();
28444         }
28445     },
28446
28447     /**
28448      * Sets the data value of the editor
28449      * @param {Mixed} value Any valid value supported by the underlying field
28450      */
28451     setValue : function(v){
28452         this.field.setValue(v);
28453     },
28454
28455     /**
28456      * Gets the data value of the editor
28457      * @return {Mixed} The data value
28458      */
28459     getValue : function(){
28460         return this.field.getValue();
28461     }
28462 });/*
28463  * Based on:
28464  * Ext JS Library 1.1.1
28465  * Copyright(c) 2006-2007, Ext JS, LLC.
28466  *
28467  * Originally Released Under LGPL - original licence link has changed is not relivant.
28468  *
28469  * Fork - LGPL
28470  * <script type="text/javascript">
28471  */
28472  
28473 /**
28474  * @class Roo.BasicDialog
28475  * @extends Roo.util.Observable
28476  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28477  * <pre><code>
28478 var dlg = new Roo.BasicDialog("my-dlg", {
28479     height: 200,
28480     width: 300,
28481     minHeight: 100,
28482     minWidth: 150,
28483     modal: true,
28484     proxyDrag: true,
28485     shadow: true
28486 });
28487 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28488 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28489 dlg.addButton('Cancel', dlg.hide, dlg);
28490 dlg.show();
28491 </code></pre>
28492   <b>A Dialog should always be a direct child of the body element.</b>
28493  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28494  * @cfg {String} title Default text to display in the title bar (defaults to null)
28495  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28496  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28497  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28498  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28499  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28500  * (defaults to null with no animation)
28501  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28502  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28503  * property for valid values (defaults to 'all')
28504  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28505  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28506  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28507  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28508  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28509  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28510  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28511  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28512  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28513  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28514  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28515  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28516  * draggable = true (defaults to false)
28517  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28518  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28519  * shadow (defaults to false)
28520  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28521  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28522  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28523  * @cfg {Array} buttons Array of buttons
28524  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28525  * @constructor
28526  * Create a new BasicDialog.
28527  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28528  * @param {Object} config Configuration options
28529  */
28530 Roo.BasicDialog = function(el, config){
28531     this.el = Roo.get(el);
28532     var dh = Roo.DomHelper;
28533     if(!this.el && config && config.autoCreate){
28534         if(typeof config.autoCreate == "object"){
28535             if(!config.autoCreate.id){
28536                 config.autoCreate.id = el;
28537             }
28538             this.el = dh.append(document.body,
28539                         config.autoCreate, true);
28540         }else{
28541             this.el = dh.append(document.body,
28542                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28543         }
28544     }
28545     el = this.el;
28546     el.setDisplayed(true);
28547     el.hide = this.hideAction;
28548     this.id = el.id;
28549     el.addClass("x-dlg");
28550
28551     Roo.apply(this, config);
28552
28553     this.proxy = el.createProxy("x-dlg-proxy");
28554     this.proxy.hide = this.hideAction;
28555     this.proxy.setOpacity(.5);
28556     this.proxy.hide();
28557
28558     if(config.width){
28559         el.setWidth(config.width);
28560     }
28561     if(config.height){
28562         el.setHeight(config.height);
28563     }
28564     this.size = el.getSize();
28565     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28566         this.xy = [config.x,config.y];
28567     }else{
28568         this.xy = el.getCenterXY(true);
28569     }
28570     /** The header element @type Roo.Element */
28571     this.header = el.child("> .x-dlg-hd");
28572     /** The body element @type Roo.Element */
28573     this.body = el.child("> .x-dlg-bd");
28574     /** The footer element @type Roo.Element */
28575     this.footer = el.child("> .x-dlg-ft");
28576
28577     if(!this.header){
28578         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28579     }
28580     if(!this.body){
28581         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28582     }
28583
28584     this.header.unselectable();
28585     if(this.title){
28586         this.header.update(this.title);
28587     }
28588     // this element allows the dialog to be focused for keyboard event
28589     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28590     this.focusEl.swallowEvent("click", true);
28591
28592     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28593
28594     // wrap the body and footer for special rendering
28595     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28596     if(this.footer){
28597         this.bwrap.dom.appendChild(this.footer.dom);
28598     }
28599
28600     this.bg = this.el.createChild({
28601         tag: "div", cls:"x-dlg-bg",
28602         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28603     });
28604     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28605
28606
28607     if(this.autoScroll !== false && !this.autoTabs){
28608         this.body.setStyle("overflow", "auto");
28609     }
28610
28611     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28612
28613     if(this.closable !== false){
28614         this.el.addClass("x-dlg-closable");
28615         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28616         this.close.on("click", this.closeClick, this);
28617         this.close.addClassOnOver("x-dlg-close-over");
28618     }
28619     if(this.collapsible !== false){
28620         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28621         this.collapseBtn.on("click", this.collapseClick, this);
28622         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28623         this.header.on("dblclick", this.collapseClick, this);
28624     }
28625     if(this.resizable !== false){
28626         this.el.addClass("x-dlg-resizable");
28627         this.resizer = new Roo.Resizable(el, {
28628             minWidth: this.minWidth || 80,
28629             minHeight:this.minHeight || 80,
28630             handles: this.resizeHandles || "all",
28631             pinned: true
28632         });
28633         this.resizer.on("beforeresize", this.beforeResize, this);
28634         this.resizer.on("resize", this.onResize, this);
28635     }
28636     if(this.draggable !== false){
28637         el.addClass("x-dlg-draggable");
28638         if (!this.proxyDrag) {
28639             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28640         }
28641         else {
28642             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28643         }
28644         dd.setHandleElId(this.header.id);
28645         dd.endDrag = this.endMove.createDelegate(this);
28646         dd.startDrag = this.startMove.createDelegate(this);
28647         dd.onDrag = this.onDrag.createDelegate(this);
28648         dd.scroll = false;
28649         this.dd = dd;
28650     }
28651     if(this.modal){
28652         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28653         this.mask.enableDisplayMode("block");
28654         this.mask.hide();
28655         this.el.addClass("x-dlg-modal");
28656     }
28657     if(this.shadow){
28658         this.shadow = new Roo.Shadow({
28659             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28660             offset : this.shadowOffset
28661         });
28662     }else{
28663         this.shadowOffset = 0;
28664     }
28665     if(Roo.useShims && this.shim !== false){
28666         this.shim = this.el.createShim();
28667         this.shim.hide = this.hideAction;
28668         this.shim.hide();
28669     }else{
28670         this.shim = false;
28671     }
28672     if(this.autoTabs){
28673         this.initTabs();
28674     }
28675     if (this.buttons) { 
28676         var bts= this.buttons;
28677         this.buttons = [];
28678         Roo.each(bts, function(b) {
28679             this.addButton(b);
28680         }, this);
28681     }
28682     
28683     
28684     this.addEvents({
28685         /**
28686          * @event keydown
28687          * Fires when a key is pressed
28688          * @param {Roo.BasicDialog} this
28689          * @param {Roo.EventObject} e
28690          */
28691         "keydown" : true,
28692         /**
28693          * @event move
28694          * Fires when this dialog is moved by the user.
28695          * @param {Roo.BasicDialog} this
28696          * @param {Number} x The new page X
28697          * @param {Number} y The new page Y
28698          */
28699         "move" : true,
28700         /**
28701          * @event resize
28702          * Fires when this dialog is resized by the user.
28703          * @param {Roo.BasicDialog} this
28704          * @param {Number} width The new width
28705          * @param {Number} height The new height
28706          */
28707         "resize" : true,
28708         /**
28709          * @event beforehide
28710          * Fires before this dialog is hidden.
28711          * @param {Roo.BasicDialog} this
28712          */
28713         "beforehide" : true,
28714         /**
28715          * @event hide
28716          * Fires when this dialog is hidden.
28717          * @param {Roo.BasicDialog} this
28718          */
28719         "hide" : true,
28720         /**
28721          * @event beforeshow
28722          * Fires before this dialog is shown.
28723          * @param {Roo.BasicDialog} this
28724          */
28725         "beforeshow" : true,
28726         /**
28727          * @event show
28728          * Fires when this dialog is shown.
28729          * @param {Roo.BasicDialog} this
28730          */
28731         "show" : true
28732     });
28733     el.on("keydown", this.onKeyDown, this);
28734     el.on("mousedown", this.toFront, this);
28735     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28736     this.el.hide();
28737     Roo.DialogManager.register(this);
28738     Roo.BasicDialog.superclass.constructor.call(this);
28739 };
28740
28741 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28742     shadowOffset: Roo.isIE ? 6 : 5,
28743     minHeight: 80,
28744     minWidth: 200,
28745     minButtonWidth: 75,
28746     defaultButton: null,
28747     buttonAlign: "right",
28748     tabTag: 'div',
28749     firstShow: true,
28750
28751     /**
28752      * Sets the dialog title text
28753      * @param {String} text The title text to display
28754      * @return {Roo.BasicDialog} this
28755      */
28756     setTitle : function(text){
28757         this.header.update(text);
28758         return this;
28759     },
28760
28761     // private
28762     closeClick : function(){
28763         this.hide();
28764     },
28765
28766     // private
28767     collapseClick : function(){
28768         this[this.collapsed ? "expand" : "collapse"]();
28769     },
28770
28771     /**
28772      * Collapses the dialog to its minimized state (only the title bar is visible).
28773      * Equivalent to the user clicking the collapse dialog button.
28774      */
28775     collapse : function(){
28776         if(!this.collapsed){
28777             this.collapsed = true;
28778             this.el.addClass("x-dlg-collapsed");
28779             this.restoreHeight = this.el.getHeight();
28780             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28781         }
28782     },
28783
28784     /**
28785      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28786      * clicking the expand dialog button.
28787      */
28788     expand : function(){
28789         if(this.collapsed){
28790             this.collapsed = false;
28791             this.el.removeClass("x-dlg-collapsed");
28792             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28793         }
28794     },
28795
28796     /**
28797      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28798      * @return {Roo.TabPanel} The tabs component
28799      */
28800     initTabs : function(){
28801         var tabs = this.getTabs();
28802         while(tabs.getTab(0)){
28803             tabs.removeTab(0);
28804         }
28805         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28806             var dom = el.dom;
28807             tabs.addTab(Roo.id(dom), dom.title);
28808             dom.title = "";
28809         });
28810         tabs.activate(0);
28811         return tabs;
28812     },
28813
28814     // private
28815     beforeResize : function(){
28816         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28817     },
28818
28819     // private
28820     onResize : function(){
28821         this.refreshSize();
28822         this.syncBodyHeight();
28823         this.adjustAssets();
28824         this.focus();
28825         this.fireEvent("resize", this, this.size.width, this.size.height);
28826     },
28827
28828     // private
28829     onKeyDown : function(e){
28830         if(this.isVisible()){
28831             this.fireEvent("keydown", this, e);
28832         }
28833     },
28834
28835     /**
28836      * Resizes the dialog.
28837      * @param {Number} width
28838      * @param {Number} height
28839      * @return {Roo.BasicDialog} this
28840      */
28841     resizeTo : function(width, height){
28842         this.el.setSize(width, height);
28843         this.size = {width: width, height: height};
28844         this.syncBodyHeight();
28845         if(this.fixedcenter){
28846             this.center();
28847         }
28848         if(this.isVisible()){
28849             this.constrainXY();
28850             this.adjustAssets();
28851         }
28852         this.fireEvent("resize", this, width, height);
28853         return this;
28854     },
28855
28856
28857     /**
28858      * Resizes the dialog to fit the specified content size.
28859      * @param {Number} width
28860      * @param {Number} height
28861      * @return {Roo.BasicDialog} this
28862      */
28863     setContentSize : function(w, h){
28864         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28865         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28866         //if(!this.el.isBorderBox()){
28867             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28868             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28869         //}
28870         if(this.tabs){
28871             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28872             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28873         }
28874         this.resizeTo(w, h);
28875         return this;
28876     },
28877
28878     /**
28879      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28880      * executed in response to a particular key being pressed while the dialog is active.
28881      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28882      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28883      * @param {Function} fn The function to call
28884      * @param {Object} scope (optional) The scope of the function
28885      * @return {Roo.BasicDialog} this
28886      */
28887     addKeyListener : function(key, fn, scope){
28888         var keyCode, shift, ctrl, alt;
28889         if(typeof key == "object" && !(key instanceof Array)){
28890             keyCode = key["key"];
28891             shift = key["shift"];
28892             ctrl = key["ctrl"];
28893             alt = key["alt"];
28894         }else{
28895             keyCode = key;
28896         }
28897         var handler = function(dlg, e){
28898             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28899                 var k = e.getKey();
28900                 if(keyCode instanceof Array){
28901                     for(var i = 0, len = keyCode.length; i < len; i++){
28902                         if(keyCode[i] == k){
28903                           fn.call(scope || window, dlg, k, e);
28904                           return;
28905                         }
28906                     }
28907                 }else{
28908                     if(k == keyCode){
28909                         fn.call(scope || window, dlg, k, e);
28910                     }
28911                 }
28912             }
28913         };
28914         this.on("keydown", handler);
28915         return this;
28916     },
28917
28918     /**
28919      * Returns the TabPanel component (creates it if it doesn't exist).
28920      * Note: If you wish to simply check for the existence of tabs without creating them,
28921      * check for a null 'tabs' property.
28922      * @return {Roo.TabPanel} The tabs component
28923      */
28924     getTabs : function(){
28925         if(!this.tabs){
28926             this.el.addClass("x-dlg-auto-tabs");
28927             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28928             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28929         }
28930         return this.tabs;
28931     },
28932
28933     /**
28934      * Adds a button to the footer section of the dialog.
28935      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28936      * object or a valid Roo.DomHelper element config
28937      * @param {Function} handler The function called when the button is clicked
28938      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28939      * @return {Roo.Button} The new button
28940      */
28941     addButton : function(config, handler, scope){
28942         var dh = Roo.DomHelper;
28943         if(!this.footer){
28944             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28945         }
28946         if(!this.btnContainer){
28947             var tb = this.footer.createChild({
28948
28949                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28950                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28951             }, null, true);
28952             this.btnContainer = tb.firstChild.firstChild.firstChild;
28953         }
28954         var bconfig = {
28955             handler: handler,
28956             scope: scope,
28957             minWidth: this.minButtonWidth,
28958             hideParent:true
28959         };
28960         if(typeof config == "string"){
28961             bconfig.text = config;
28962         }else{
28963             if(config.tag){
28964                 bconfig.dhconfig = config;
28965             }else{
28966                 Roo.apply(bconfig, config);
28967             }
28968         }
28969         var fc = false;
28970         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28971             bconfig.position = Math.max(0, bconfig.position);
28972             fc = this.btnContainer.childNodes[bconfig.position];
28973         }
28974          
28975         var btn = new Roo.Button(
28976             fc ? 
28977                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28978                 : this.btnContainer.appendChild(document.createElement("td")),
28979             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28980             bconfig
28981         );
28982         this.syncBodyHeight();
28983         if(!this.buttons){
28984             /**
28985              * Array of all the buttons that have been added to this dialog via addButton
28986              * @type Array
28987              */
28988             this.buttons = [];
28989         }
28990         this.buttons.push(btn);
28991         return btn;
28992     },
28993
28994     /**
28995      * Sets the default button to be focused when the dialog is displayed.
28996      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28997      * @return {Roo.BasicDialog} this
28998      */
28999     setDefaultButton : function(btn){
29000         this.defaultButton = btn;
29001         return this;
29002     },
29003
29004     // private
29005     getHeaderFooterHeight : function(safe){
29006         var height = 0;
29007         if(this.header){
29008            height += this.header.getHeight();
29009         }
29010         if(this.footer){
29011            var fm = this.footer.getMargins();
29012             height += (this.footer.getHeight()+fm.top+fm.bottom);
29013         }
29014         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29015         height += this.centerBg.getPadding("tb");
29016         return height;
29017     },
29018
29019     // private
29020     syncBodyHeight : function(){
29021         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29022         var height = this.size.height - this.getHeaderFooterHeight(false);
29023         bd.setHeight(height-bd.getMargins("tb"));
29024         var hh = this.header.getHeight();
29025         var h = this.size.height-hh;
29026         cb.setHeight(h);
29027         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29028         bw.setHeight(h-cb.getPadding("tb"));
29029         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29030         bd.setWidth(bw.getWidth(true));
29031         if(this.tabs){
29032             this.tabs.syncHeight();
29033             if(Roo.isIE){
29034                 this.tabs.el.repaint();
29035             }
29036         }
29037     },
29038
29039     /**
29040      * Restores the previous state of the dialog if Roo.state is configured.
29041      * @return {Roo.BasicDialog} this
29042      */
29043     restoreState : function(){
29044         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29045         if(box && box.width){
29046             this.xy = [box.x, box.y];
29047             this.resizeTo(box.width, box.height);
29048         }
29049         return this;
29050     },
29051
29052     // private
29053     beforeShow : function(){
29054         this.expand();
29055         if(this.fixedcenter){
29056             this.xy = this.el.getCenterXY(true);
29057         }
29058         if(this.modal){
29059             Roo.get(document.body).addClass("x-body-masked");
29060             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29061             this.mask.show();
29062         }
29063         this.constrainXY();
29064     },
29065
29066     // private
29067     animShow : function(){
29068         var b = Roo.get(this.animateTarget).getBox();
29069         this.proxy.setSize(b.width, b.height);
29070         this.proxy.setLocation(b.x, b.y);
29071         this.proxy.show();
29072         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29073                     true, .35, this.showEl.createDelegate(this));
29074     },
29075
29076     /**
29077      * Shows the dialog.
29078      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29079      * @return {Roo.BasicDialog} this
29080      */
29081     show : function(animateTarget){
29082         if (this.fireEvent("beforeshow", this) === false){
29083             return;
29084         }
29085         if(this.syncHeightBeforeShow){
29086             this.syncBodyHeight();
29087         }else if(this.firstShow){
29088             this.firstShow = false;
29089             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29090         }
29091         this.animateTarget = animateTarget || this.animateTarget;
29092         if(!this.el.isVisible()){
29093             this.beforeShow();
29094             if(this.animateTarget && Roo.get(this.animateTarget)){
29095                 this.animShow();
29096             }else{
29097                 this.showEl();
29098             }
29099         }
29100         return this;
29101     },
29102
29103     // private
29104     showEl : function(){
29105         this.proxy.hide();
29106         this.el.setXY(this.xy);
29107         this.el.show();
29108         this.adjustAssets(true);
29109         this.toFront();
29110         this.focus();
29111         // IE peekaboo bug - fix found by Dave Fenwick
29112         if(Roo.isIE){
29113             this.el.repaint();
29114         }
29115         this.fireEvent("show", this);
29116     },
29117
29118     /**
29119      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29120      * dialog itself will receive focus.
29121      */
29122     focus : function(){
29123         if(this.defaultButton){
29124             this.defaultButton.focus();
29125         }else{
29126             this.focusEl.focus();
29127         }
29128     },
29129
29130     // private
29131     constrainXY : function(){
29132         if(this.constraintoviewport !== false){
29133             if(!this.viewSize){
29134                 if(this.container){
29135                     var s = this.container.getSize();
29136                     this.viewSize = [s.width, s.height];
29137                 }else{
29138                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29139                 }
29140             }
29141             var s = Roo.get(this.container||document).getScroll();
29142
29143             var x = this.xy[0], y = this.xy[1];
29144             var w = this.size.width, h = this.size.height;
29145             var vw = this.viewSize[0], vh = this.viewSize[1];
29146             // only move it if it needs it
29147             var moved = false;
29148             // first validate right/bottom
29149             if(x + w > vw+s.left){
29150                 x = vw - w;
29151                 moved = true;
29152             }
29153             if(y + h > vh+s.top){
29154                 y = vh - h;
29155                 moved = true;
29156             }
29157             // then make sure top/left isn't negative
29158             if(x < s.left){
29159                 x = s.left;
29160                 moved = true;
29161             }
29162             if(y < s.top){
29163                 y = s.top;
29164                 moved = true;
29165             }
29166             if(moved){
29167                 // cache xy
29168                 this.xy = [x, y];
29169                 if(this.isVisible()){
29170                     this.el.setLocation(x, y);
29171                     this.adjustAssets();
29172                 }
29173             }
29174         }
29175     },
29176
29177     // private
29178     onDrag : function(){
29179         if(!this.proxyDrag){
29180             this.xy = this.el.getXY();
29181             this.adjustAssets();
29182         }
29183     },
29184
29185     // private
29186     adjustAssets : function(doShow){
29187         var x = this.xy[0], y = this.xy[1];
29188         var w = this.size.width, h = this.size.height;
29189         if(doShow === true){
29190             if(this.shadow){
29191                 this.shadow.show(this.el);
29192             }
29193             if(this.shim){
29194                 this.shim.show();
29195             }
29196         }
29197         if(this.shadow && this.shadow.isVisible()){
29198             this.shadow.show(this.el);
29199         }
29200         if(this.shim && this.shim.isVisible()){
29201             this.shim.setBounds(x, y, w, h);
29202         }
29203     },
29204
29205     // private
29206     adjustViewport : function(w, h){
29207         if(!w || !h){
29208             w = Roo.lib.Dom.getViewWidth();
29209             h = Roo.lib.Dom.getViewHeight();
29210         }
29211         // cache the size
29212         this.viewSize = [w, h];
29213         if(this.modal && this.mask.isVisible()){
29214             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29215             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29216         }
29217         if(this.isVisible()){
29218             this.constrainXY();
29219         }
29220     },
29221
29222     /**
29223      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29224      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29225      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29226      */
29227     destroy : function(removeEl){
29228         if(this.isVisible()){
29229             this.animateTarget = null;
29230             this.hide();
29231         }
29232         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29233         if(this.tabs){
29234             this.tabs.destroy(removeEl);
29235         }
29236         Roo.destroy(
29237              this.shim,
29238              this.proxy,
29239              this.resizer,
29240              this.close,
29241              this.mask
29242         );
29243         if(this.dd){
29244             this.dd.unreg();
29245         }
29246         if(this.buttons){
29247            for(var i = 0, len = this.buttons.length; i < len; i++){
29248                this.buttons[i].destroy();
29249            }
29250         }
29251         this.el.removeAllListeners();
29252         if(removeEl === true){
29253             this.el.update("");
29254             this.el.remove();
29255         }
29256         Roo.DialogManager.unregister(this);
29257     },
29258
29259     // private
29260     startMove : function(){
29261         if(this.proxyDrag){
29262             this.proxy.show();
29263         }
29264         if(this.constraintoviewport !== false){
29265             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29266         }
29267     },
29268
29269     // private
29270     endMove : function(){
29271         if(!this.proxyDrag){
29272             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29273         }else{
29274             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29275             this.proxy.hide();
29276         }
29277         this.refreshSize();
29278         this.adjustAssets();
29279         this.focus();
29280         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29281     },
29282
29283     /**
29284      * Brings this dialog to the front of any other visible dialogs
29285      * @return {Roo.BasicDialog} this
29286      */
29287     toFront : function(){
29288         Roo.DialogManager.bringToFront(this);
29289         return this;
29290     },
29291
29292     /**
29293      * Sends this dialog to the back (under) of any other visible dialogs
29294      * @return {Roo.BasicDialog} this
29295      */
29296     toBack : function(){
29297         Roo.DialogManager.sendToBack(this);
29298         return this;
29299     },
29300
29301     /**
29302      * Centers this dialog in the viewport
29303      * @return {Roo.BasicDialog} this
29304      */
29305     center : function(){
29306         var xy = this.el.getCenterXY(true);
29307         this.moveTo(xy[0], xy[1]);
29308         return this;
29309     },
29310
29311     /**
29312      * Moves the dialog's top-left corner to the specified point
29313      * @param {Number} x
29314      * @param {Number} y
29315      * @return {Roo.BasicDialog} this
29316      */
29317     moveTo : function(x, y){
29318         this.xy = [x,y];
29319         if(this.isVisible()){
29320             this.el.setXY(this.xy);
29321             this.adjustAssets();
29322         }
29323         return this;
29324     },
29325
29326     /**
29327      * Aligns the dialog to the specified element
29328      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29329      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29330      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29331      * @return {Roo.BasicDialog} this
29332      */
29333     alignTo : function(element, position, offsets){
29334         this.xy = this.el.getAlignToXY(element, position, offsets);
29335         if(this.isVisible()){
29336             this.el.setXY(this.xy);
29337             this.adjustAssets();
29338         }
29339         return this;
29340     },
29341
29342     /**
29343      * Anchors an element to another element and realigns it when the window is resized.
29344      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29345      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29346      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29347      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29348      * is a number, it is used as the buffer delay (defaults to 50ms).
29349      * @return {Roo.BasicDialog} this
29350      */
29351     anchorTo : function(el, alignment, offsets, monitorScroll){
29352         var action = function(){
29353             this.alignTo(el, alignment, offsets);
29354         };
29355         Roo.EventManager.onWindowResize(action, this);
29356         var tm = typeof monitorScroll;
29357         if(tm != 'undefined'){
29358             Roo.EventManager.on(window, 'scroll', action, this,
29359                 {buffer: tm == 'number' ? monitorScroll : 50});
29360         }
29361         action.call(this);
29362         return this;
29363     },
29364
29365     /**
29366      * Returns true if the dialog is visible
29367      * @return {Boolean}
29368      */
29369     isVisible : function(){
29370         return this.el.isVisible();
29371     },
29372
29373     // private
29374     animHide : function(callback){
29375         var b = Roo.get(this.animateTarget).getBox();
29376         this.proxy.show();
29377         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29378         this.el.hide();
29379         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29380                     this.hideEl.createDelegate(this, [callback]));
29381     },
29382
29383     /**
29384      * Hides the dialog.
29385      * @param {Function} callback (optional) Function to call when the dialog is hidden
29386      * @return {Roo.BasicDialog} this
29387      */
29388     hide : function(callback){
29389         if (this.fireEvent("beforehide", this) === false){
29390             return;
29391         }
29392         if(this.shadow){
29393             this.shadow.hide();
29394         }
29395         if(this.shim) {
29396           this.shim.hide();
29397         }
29398         // sometimes animateTarget seems to get set.. causing problems...
29399         // this just double checks..
29400         if(this.animateTarget && Roo.get(this.animateTarget)) {
29401            this.animHide(callback);
29402         }else{
29403             this.el.hide();
29404             this.hideEl(callback);
29405         }
29406         return this;
29407     },
29408
29409     // private
29410     hideEl : function(callback){
29411         this.proxy.hide();
29412         if(this.modal){
29413             this.mask.hide();
29414             Roo.get(document.body).removeClass("x-body-masked");
29415         }
29416         this.fireEvent("hide", this);
29417         if(typeof callback == "function"){
29418             callback();
29419         }
29420     },
29421
29422     // private
29423     hideAction : function(){
29424         this.setLeft("-10000px");
29425         this.setTop("-10000px");
29426         this.setStyle("visibility", "hidden");
29427     },
29428
29429     // private
29430     refreshSize : function(){
29431         this.size = this.el.getSize();
29432         this.xy = this.el.getXY();
29433         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29434     },
29435
29436     // private
29437     // z-index is managed by the DialogManager and may be overwritten at any time
29438     setZIndex : function(index){
29439         if(this.modal){
29440             this.mask.setStyle("z-index", index);
29441         }
29442         if(this.shim){
29443             this.shim.setStyle("z-index", ++index);
29444         }
29445         if(this.shadow){
29446             this.shadow.setZIndex(++index);
29447         }
29448         this.el.setStyle("z-index", ++index);
29449         if(this.proxy){
29450             this.proxy.setStyle("z-index", ++index);
29451         }
29452         if(this.resizer){
29453             this.resizer.proxy.setStyle("z-index", ++index);
29454         }
29455
29456         this.lastZIndex = index;
29457     },
29458
29459     /**
29460      * Returns the element for this dialog
29461      * @return {Roo.Element} The underlying dialog Element
29462      */
29463     getEl : function(){
29464         return this.el;
29465     }
29466 });
29467
29468 /**
29469  * @class Roo.DialogManager
29470  * Provides global access to BasicDialogs that have been created and
29471  * support for z-indexing (layering) multiple open dialogs.
29472  */
29473 Roo.DialogManager = function(){
29474     var list = {};
29475     var accessList = [];
29476     var front = null;
29477
29478     // private
29479     var sortDialogs = function(d1, d2){
29480         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29481     };
29482
29483     // private
29484     var orderDialogs = function(){
29485         accessList.sort(sortDialogs);
29486         var seed = Roo.DialogManager.zseed;
29487         for(var i = 0, len = accessList.length; i < len; i++){
29488             var dlg = accessList[i];
29489             if(dlg){
29490                 dlg.setZIndex(seed + (i*10));
29491             }
29492         }
29493     };
29494
29495     return {
29496         /**
29497          * The starting z-index for BasicDialogs (defaults to 9000)
29498          * @type Number The z-index value
29499          */
29500         zseed : 9000,
29501
29502         // private
29503         register : function(dlg){
29504             list[dlg.id] = dlg;
29505             accessList.push(dlg);
29506         },
29507
29508         // private
29509         unregister : function(dlg){
29510             delete list[dlg.id];
29511             var i=0;
29512             var len=0;
29513             if(!accessList.indexOf){
29514                 for(  i = 0, len = accessList.length; i < len; i++){
29515                     if(accessList[i] == dlg){
29516                         accessList.splice(i, 1);
29517                         return;
29518                     }
29519                 }
29520             }else{
29521                  i = accessList.indexOf(dlg);
29522                 if(i != -1){
29523                     accessList.splice(i, 1);
29524                 }
29525             }
29526         },
29527
29528         /**
29529          * Gets a registered dialog by id
29530          * @param {String/Object} id The id of the dialog or a dialog
29531          * @return {Roo.BasicDialog} this
29532          */
29533         get : function(id){
29534             return typeof id == "object" ? id : list[id];
29535         },
29536
29537         /**
29538          * Brings the specified dialog to the front
29539          * @param {String/Object} dlg The id of the dialog or a dialog
29540          * @return {Roo.BasicDialog} this
29541          */
29542         bringToFront : function(dlg){
29543             dlg = this.get(dlg);
29544             if(dlg != front){
29545                 front = dlg;
29546                 dlg._lastAccess = new Date().getTime();
29547                 orderDialogs();
29548             }
29549             return dlg;
29550         },
29551
29552         /**
29553          * Sends the specified dialog to the back
29554          * @param {String/Object} dlg The id of the dialog or a dialog
29555          * @return {Roo.BasicDialog} this
29556          */
29557         sendToBack : function(dlg){
29558             dlg = this.get(dlg);
29559             dlg._lastAccess = -(new Date().getTime());
29560             orderDialogs();
29561             return dlg;
29562         },
29563
29564         /**
29565          * Hides all dialogs
29566          */
29567         hideAll : function(){
29568             for(var id in list){
29569                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29570                     list[id].hide();
29571                 }
29572             }
29573         }
29574     };
29575 }();
29576
29577 /**
29578  * @class Roo.LayoutDialog
29579  * @extends Roo.BasicDialog
29580  * Dialog which provides adjustments for working with a layout in a Dialog.
29581  * Add your necessary layout config options to the dialog's config.<br>
29582  * Example usage (including a nested layout):
29583  * <pre><code>
29584 if(!dialog){
29585     dialog = new Roo.LayoutDialog("download-dlg", {
29586         modal: true,
29587         width:600,
29588         height:450,
29589         shadow:true,
29590         minWidth:500,
29591         minHeight:350,
29592         autoTabs:true,
29593         proxyDrag:true,
29594         // layout config merges with the dialog config
29595         center:{
29596             tabPosition: "top",
29597             alwaysShowTabs: true
29598         }
29599     });
29600     dialog.addKeyListener(27, dialog.hide, dialog);
29601     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29602     dialog.addButton("Build It!", this.getDownload, this);
29603
29604     // we can even add nested layouts
29605     var innerLayout = new Roo.BorderLayout("dl-inner", {
29606         east: {
29607             initialSize: 200,
29608             autoScroll:true,
29609             split:true
29610         },
29611         center: {
29612             autoScroll:true
29613         }
29614     });
29615     innerLayout.beginUpdate();
29616     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29617     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29618     innerLayout.endUpdate(true);
29619
29620     var layout = dialog.getLayout();
29621     layout.beginUpdate();
29622     layout.add("center", new Roo.ContentPanel("standard-panel",
29623                         {title: "Download the Source", fitToFrame:true}));
29624     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29625                {title: "Build your own roo.js"}));
29626     layout.getRegion("center").showPanel(sp);
29627     layout.endUpdate();
29628 }
29629 </code></pre>
29630     * @constructor
29631     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29632     * @param {Object} config configuration options
29633   */
29634 Roo.LayoutDialog = function(el, cfg){
29635     
29636     var config=  cfg;
29637     if (typeof(cfg) == 'undefined') {
29638         config = Roo.apply({}, el);
29639         // not sure why we use documentElement here.. - it should always be body.
29640         // IE7 borks horribly if we use documentElement.
29641         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
29642         //config.autoCreate = true;
29643     }
29644     
29645     
29646     config.autoTabs = false;
29647     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29648     this.body.setStyle({overflow:"hidden", position:"relative"});
29649     this.layout = new Roo.BorderLayout(this.body.dom, config);
29650     this.layout.monitorWindowResize = false;
29651     this.el.addClass("x-dlg-auto-layout");
29652     // fix case when center region overwrites center function
29653     this.center = Roo.BasicDialog.prototype.center;
29654     this.on("show", this.layout.layout, this.layout, true);
29655     if (config.items) {
29656         var xitems = config.items;
29657         delete config.items;
29658         Roo.each(xitems, this.addxtype, this);
29659     }
29660     
29661     
29662 };
29663 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29664     /**
29665      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29666      * @deprecated
29667      */
29668     endUpdate : function(){
29669         this.layout.endUpdate();
29670     },
29671
29672     /**
29673      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29674      *  @deprecated
29675      */
29676     beginUpdate : function(){
29677         this.layout.beginUpdate();
29678     },
29679
29680     /**
29681      * Get the BorderLayout for this dialog
29682      * @return {Roo.BorderLayout}
29683      */
29684     getLayout : function(){
29685         return this.layout;
29686     },
29687
29688     showEl : function(){
29689         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29690         if(Roo.isIE7){
29691             this.layout.layout();
29692         }
29693     },
29694
29695     // private
29696     // Use the syncHeightBeforeShow config option to control this automatically
29697     syncBodyHeight : function(){
29698         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29699         if(this.layout){this.layout.layout();}
29700     },
29701     
29702       /**
29703      * Add an xtype element (actually adds to the layout.)
29704      * @return {Object} xdata xtype object data.
29705      */
29706     
29707     addxtype : function(c) {
29708         return this.layout.addxtype(c);
29709     }
29710 });/*
29711  * Based on:
29712  * Ext JS Library 1.1.1
29713  * Copyright(c) 2006-2007, Ext JS, LLC.
29714  *
29715  * Originally Released Under LGPL - original licence link has changed is not relivant.
29716  *
29717  * Fork - LGPL
29718  * <script type="text/javascript">
29719  */
29720  
29721 /**
29722  * @class Roo.MessageBox
29723  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29724  * Example usage:
29725  *<pre><code>
29726 // Basic alert:
29727 Roo.Msg.alert('Status', 'Changes saved successfully.');
29728
29729 // Prompt for user data:
29730 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29731     if (btn == 'ok'){
29732         // process text value...
29733     }
29734 });
29735
29736 // Show a dialog using config options:
29737 Roo.Msg.show({
29738    title:'Save Changes?',
29739    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29740    buttons: Roo.Msg.YESNOCANCEL,
29741    fn: processResult,
29742    animEl: 'elId'
29743 });
29744 </code></pre>
29745  * @singleton
29746  */
29747 Roo.MessageBox = function(){
29748     var dlg, opt, mask, waitTimer;
29749     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29750     var buttons, activeTextEl, bwidth;
29751
29752     // private
29753     var handleButton = function(button){
29754         dlg.hide();
29755         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29756     };
29757
29758     // private
29759     var handleHide = function(){
29760         if(opt && opt.cls){
29761             dlg.el.removeClass(opt.cls);
29762         }
29763         if(waitTimer){
29764             Roo.TaskMgr.stop(waitTimer);
29765             waitTimer = null;
29766         }
29767     };
29768
29769     // private
29770     var updateButtons = function(b){
29771         var width = 0;
29772         if(!b){
29773             buttons["ok"].hide();
29774             buttons["cancel"].hide();
29775             buttons["yes"].hide();
29776             buttons["no"].hide();
29777             dlg.footer.dom.style.display = 'none';
29778             return width;
29779         }
29780         dlg.footer.dom.style.display = '';
29781         for(var k in buttons){
29782             if(typeof buttons[k] != "function"){
29783                 if(b[k]){
29784                     buttons[k].show();
29785                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29786                     width += buttons[k].el.getWidth()+15;
29787                 }else{
29788                     buttons[k].hide();
29789                 }
29790             }
29791         }
29792         return width;
29793     };
29794
29795     // private
29796     var handleEsc = function(d, k, e){
29797         if(opt && opt.closable !== false){
29798             dlg.hide();
29799         }
29800         if(e){
29801             e.stopEvent();
29802         }
29803     };
29804
29805     return {
29806         /**
29807          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29808          * @return {Roo.BasicDialog} The BasicDialog element
29809          */
29810         getDialog : function(){
29811            if(!dlg){
29812                 dlg = new Roo.BasicDialog("x-msg-box", {
29813                     autoCreate : true,
29814                     shadow: true,
29815                     draggable: true,
29816                     resizable:false,
29817                     constraintoviewport:false,
29818                     fixedcenter:true,
29819                     collapsible : false,
29820                     shim:true,
29821                     modal: true,
29822                     width:400, height:100,
29823                     buttonAlign:"center",
29824                     closeClick : function(){
29825                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29826                             handleButton("no");
29827                         }else{
29828                             handleButton("cancel");
29829                         }
29830                     }
29831                 });
29832                 dlg.on("hide", handleHide);
29833                 mask = dlg.mask;
29834                 dlg.addKeyListener(27, handleEsc);
29835                 buttons = {};
29836                 var bt = this.buttonText;
29837                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29838                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29839                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29840                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29841                 bodyEl = dlg.body.createChild({
29842
29843                     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>'
29844                 });
29845                 msgEl = bodyEl.dom.firstChild;
29846                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29847                 textboxEl.enableDisplayMode();
29848                 textboxEl.addKeyListener([10,13], function(){
29849                     if(dlg.isVisible() && opt && opt.buttons){
29850                         if(opt.buttons.ok){
29851                             handleButton("ok");
29852                         }else if(opt.buttons.yes){
29853                             handleButton("yes");
29854                         }
29855                     }
29856                 });
29857                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29858                 textareaEl.enableDisplayMode();
29859                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29860                 progressEl.enableDisplayMode();
29861                 var pf = progressEl.dom.firstChild;
29862                 if (pf) {
29863                     pp = Roo.get(pf.firstChild);
29864                     pp.setHeight(pf.offsetHeight);
29865                 }
29866                 
29867             }
29868             return dlg;
29869         },
29870
29871         /**
29872          * Updates the message box body text
29873          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29874          * the XHTML-compliant non-breaking space character '&amp;#160;')
29875          * @return {Roo.MessageBox} This message box
29876          */
29877         updateText : function(text){
29878             if(!dlg.isVisible() && !opt.width){
29879                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29880             }
29881             msgEl.innerHTML = text || '&#160;';
29882             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29883                         Math.max(opt.minWidth || this.minWidth, bwidth));
29884             if(opt.prompt){
29885                 activeTextEl.setWidth(w);
29886             }
29887             if(dlg.isVisible()){
29888                 dlg.fixedcenter = false;
29889             }
29890             dlg.setContentSize(w, bodyEl.getHeight());
29891             if(dlg.isVisible()){
29892                 dlg.fixedcenter = true;
29893             }
29894             return this;
29895         },
29896
29897         /**
29898          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29899          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29900          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29901          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29902          * @return {Roo.MessageBox} This message box
29903          */
29904         updateProgress : function(value, text){
29905             if(text){
29906                 this.updateText(text);
29907             }
29908             if (pp) { // weird bug on my firefox - for some reason this is not defined
29909                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29910             }
29911             return this;
29912         },        
29913
29914         /**
29915          * Returns true if the message box is currently displayed
29916          * @return {Boolean} True if the message box is visible, else false
29917          */
29918         isVisible : function(){
29919             return dlg && dlg.isVisible();  
29920         },
29921
29922         /**
29923          * Hides the message box if it is displayed
29924          */
29925         hide : function(){
29926             if(this.isVisible()){
29927                 dlg.hide();
29928             }  
29929         },
29930
29931         /**
29932          * Displays a new message box, or reinitializes an existing message box, based on the config options
29933          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29934          * The following config object properties are supported:
29935          * <pre>
29936 Property    Type             Description
29937 ----------  ---------------  ------------------------------------------------------------------------------------
29938 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29939                                    closes (defaults to undefined)
29940 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29941                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29942 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29943                                    progress and wait dialogs will ignore this property and always hide the
29944                                    close button as they can only be closed programmatically.
29945 cls               String           A custom CSS class to apply to the message box element
29946 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29947                                    displayed (defaults to 75)
29948 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29949                                    function will be btn (the name of the button that was clicked, if applicable,
29950                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29951                                    Progress and wait dialogs will ignore this option since they do not respond to
29952                                    user actions and can only be closed programmatically, so any required function
29953                                    should be called by the same code after it closes the dialog.
29954 icon              String           A CSS class that provides a background image to be used as an icon for
29955                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29956 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29957 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29958 modal             Boolean          False to allow user interaction with the page while the message box is
29959                                    displayed (defaults to true)
29960 msg               String           A string that will replace the existing message box body text (defaults
29961                                    to the XHTML-compliant non-breaking space character '&#160;')
29962 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29963 progress          Boolean          True to display a progress bar (defaults to false)
29964 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29965 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29966 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29967 title             String           The title text
29968 value             String           The string value to set into the active textbox element if displayed
29969 wait              Boolean          True to display a progress bar (defaults to false)
29970 width             Number           The width of the dialog in pixels
29971 </pre>
29972          *
29973          * Example usage:
29974          * <pre><code>
29975 Roo.Msg.show({
29976    title: 'Address',
29977    msg: 'Please enter your address:',
29978    width: 300,
29979    buttons: Roo.MessageBox.OKCANCEL,
29980    multiline: true,
29981    fn: saveAddress,
29982    animEl: 'addAddressBtn'
29983 });
29984 </code></pre>
29985          * @param {Object} config Configuration options
29986          * @return {Roo.MessageBox} This message box
29987          */
29988         show : function(options){
29989             if(this.isVisible()){
29990                 this.hide();
29991             }
29992             var d = this.getDialog();
29993             opt = options;
29994             d.setTitle(opt.title || "&#160;");
29995             d.close.setDisplayed(opt.closable !== false);
29996             activeTextEl = textboxEl;
29997             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29998             if(opt.prompt){
29999                 if(opt.multiline){
30000                     textboxEl.hide();
30001                     textareaEl.show();
30002                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30003                         opt.multiline : this.defaultTextHeight);
30004                     activeTextEl = textareaEl;
30005                 }else{
30006                     textboxEl.show();
30007                     textareaEl.hide();
30008                 }
30009             }else{
30010                 textboxEl.hide();
30011                 textareaEl.hide();
30012             }
30013             progressEl.setDisplayed(opt.progress === true);
30014             this.updateProgress(0);
30015             activeTextEl.dom.value = opt.value || "";
30016             if(opt.prompt){
30017                 dlg.setDefaultButton(activeTextEl);
30018             }else{
30019                 var bs = opt.buttons;
30020                 var db = null;
30021                 if(bs && bs.ok){
30022                     db = buttons["ok"];
30023                 }else if(bs && bs.yes){
30024                     db = buttons["yes"];
30025                 }
30026                 dlg.setDefaultButton(db);
30027             }
30028             bwidth = updateButtons(opt.buttons);
30029             this.updateText(opt.msg);
30030             if(opt.cls){
30031                 d.el.addClass(opt.cls);
30032             }
30033             d.proxyDrag = opt.proxyDrag === true;
30034             d.modal = opt.modal !== false;
30035             d.mask = opt.modal !== false ? mask : false;
30036             if(!d.isVisible()){
30037                 // force it to the end of the z-index stack so it gets a cursor in FF
30038                 document.body.appendChild(dlg.el.dom);
30039                 d.animateTarget = null;
30040                 d.show(options.animEl);
30041             }
30042             return this;
30043         },
30044
30045         /**
30046          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30047          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30048          * and closing the message box when the process is complete.
30049          * @param {String} title The title bar text
30050          * @param {String} msg The message box body text
30051          * @return {Roo.MessageBox} This message box
30052          */
30053         progress : function(title, msg){
30054             this.show({
30055                 title : title,
30056                 msg : msg,
30057                 buttons: false,
30058                 progress:true,
30059                 closable:false,
30060                 minWidth: this.minProgressWidth,
30061                 modal : true
30062             });
30063             return this;
30064         },
30065
30066         /**
30067          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30068          * If a callback function is passed it will be called after the user clicks the button, and the
30069          * id of the button that was clicked will be passed as the only parameter to the callback
30070          * (could also be the top-right close button).
30071          * @param {String} title The title bar text
30072          * @param {String} msg The message box body text
30073          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30074          * @param {Object} scope (optional) The scope of the callback function
30075          * @return {Roo.MessageBox} This message box
30076          */
30077         alert : function(title, msg, fn, scope){
30078             this.show({
30079                 title : title,
30080                 msg : msg,
30081                 buttons: this.OK,
30082                 fn: fn,
30083                 scope : scope,
30084                 modal : true
30085             });
30086             return this;
30087         },
30088
30089         /**
30090          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30091          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30092          * You are responsible for closing the message box when the process is complete.
30093          * @param {String} msg The message box body text
30094          * @param {String} title (optional) The title bar text
30095          * @return {Roo.MessageBox} This message box
30096          */
30097         wait : function(msg, title){
30098             this.show({
30099                 title : title,
30100                 msg : msg,
30101                 buttons: false,
30102                 closable:false,
30103                 progress:true,
30104                 modal:true,
30105                 width:300,
30106                 wait:true
30107             });
30108             waitTimer = Roo.TaskMgr.start({
30109                 run: function(i){
30110                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30111                 },
30112                 interval: 1000
30113             });
30114             return this;
30115         },
30116
30117         /**
30118          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30119          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30120          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30121          * @param {String} title The title bar text
30122          * @param {String} msg The message box body text
30123          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30124          * @param {Object} scope (optional) The scope of the callback function
30125          * @return {Roo.MessageBox} This message box
30126          */
30127         confirm : function(title, msg, fn, scope){
30128             this.show({
30129                 title : title,
30130                 msg : msg,
30131                 buttons: this.YESNO,
30132                 fn: fn,
30133                 scope : scope,
30134                 modal : true
30135             });
30136             return this;
30137         },
30138
30139         /**
30140          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30141          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30142          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30143          * (could also be the top-right close button) and the text that was entered will be passed as the two
30144          * parameters to the callback.
30145          * @param {String} title The title bar text
30146          * @param {String} msg The message box body text
30147          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30148          * @param {Object} scope (optional) The scope of the callback function
30149          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30150          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30151          * @return {Roo.MessageBox} This message box
30152          */
30153         prompt : function(title, msg, fn, scope, multiline){
30154             this.show({
30155                 title : title,
30156                 msg : msg,
30157                 buttons: this.OKCANCEL,
30158                 fn: fn,
30159                 minWidth:250,
30160                 scope : scope,
30161                 prompt:true,
30162                 multiline: multiline,
30163                 modal : true
30164             });
30165             return this;
30166         },
30167
30168         /**
30169          * Button config that displays a single OK button
30170          * @type Object
30171          */
30172         OK : {ok:true},
30173         /**
30174          * Button config that displays Yes and No buttons
30175          * @type Object
30176          */
30177         YESNO : {yes:true, no:true},
30178         /**
30179          * Button config that displays OK and Cancel buttons
30180          * @type Object
30181          */
30182         OKCANCEL : {ok:true, cancel:true},
30183         /**
30184          * Button config that displays Yes, No and Cancel buttons
30185          * @type Object
30186          */
30187         YESNOCANCEL : {yes:true, no:true, cancel:true},
30188
30189         /**
30190          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30191          * @type Number
30192          */
30193         defaultTextHeight : 75,
30194         /**
30195          * The maximum width in pixels of the message box (defaults to 600)
30196          * @type Number
30197          */
30198         maxWidth : 600,
30199         /**
30200          * The minimum width in pixels of the message box (defaults to 100)
30201          * @type Number
30202          */
30203         minWidth : 100,
30204         /**
30205          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30206          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30207          * @type Number
30208          */
30209         minProgressWidth : 250,
30210         /**
30211          * An object containing the default button text strings that can be overriden for localized language support.
30212          * Supported properties are: ok, cancel, yes and no.
30213          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30214          * @type Object
30215          */
30216         buttonText : {
30217             ok : "OK",
30218             cancel : "Cancel",
30219             yes : "Yes",
30220             no : "No"
30221         }
30222     };
30223 }();
30224
30225 /**
30226  * Shorthand for {@link Roo.MessageBox}
30227  */
30228 Roo.Msg = Roo.MessageBox;/*
30229  * Based on:
30230  * Ext JS Library 1.1.1
30231  * Copyright(c) 2006-2007, Ext JS, LLC.
30232  *
30233  * Originally Released Under LGPL - original licence link has changed is not relivant.
30234  *
30235  * Fork - LGPL
30236  * <script type="text/javascript">
30237  */
30238 /**
30239  * @class Roo.QuickTips
30240  * Provides attractive and customizable tooltips for any element.
30241  * @singleton
30242  */
30243 Roo.QuickTips = function(){
30244     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30245     var ce, bd, xy, dd;
30246     var visible = false, disabled = true, inited = false;
30247     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30248     
30249     var onOver = function(e){
30250         if(disabled){
30251             return;
30252         }
30253         var t = e.getTarget();
30254         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30255             return;
30256         }
30257         if(ce && t == ce.el){
30258             clearTimeout(hideProc);
30259             return;
30260         }
30261         if(t && tagEls[t.id]){
30262             tagEls[t.id].el = t;
30263             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30264             return;
30265         }
30266         var ttp, et = Roo.fly(t);
30267         var ns = cfg.namespace;
30268         if(tm.interceptTitles && t.title){
30269             ttp = t.title;
30270             t.qtip = ttp;
30271             t.removeAttribute("title");
30272             e.preventDefault();
30273         }else{
30274             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30275         }
30276         if(ttp){
30277             showProc = show.defer(tm.showDelay, tm, [{
30278                 el: t, 
30279                 text: ttp, 
30280                 width: et.getAttributeNS(ns, cfg.width),
30281                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30282                 title: et.getAttributeNS(ns, cfg.title),
30283                     cls: et.getAttributeNS(ns, cfg.cls)
30284             }]);
30285         }
30286     };
30287     
30288     var onOut = function(e){
30289         clearTimeout(showProc);
30290         var t = e.getTarget();
30291         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30292             hideProc = setTimeout(hide, tm.hideDelay);
30293         }
30294     };
30295     
30296     var onMove = function(e){
30297         if(disabled){
30298             return;
30299         }
30300         xy = e.getXY();
30301         xy[1] += 18;
30302         if(tm.trackMouse && ce){
30303             el.setXY(xy);
30304         }
30305     };
30306     
30307     var onDown = function(e){
30308         clearTimeout(showProc);
30309         clearTimeout(hideProc);
30310         if(!e.within(el)){
30311             if(tm.hideOnClick){
30312                 hide();
30313                 tm.disable();
30314                 tm.enable.defer(100, tm);
30315             }
30316         }
30317     };
30318     
30319     var getPad = function(){
30320         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30321     };
30322
30323     var show = function(o){
30324         if(disabled){
30325             return;
30326         }
30327         clearTimeout(dismissProc);
30328         ce = o;
30329         if(removeCls){ // in case manually hidden
30330             el.removeClass(removeCls);
30331             removeCls = null;
30332         }
30333         if(ce.cls){
30334             el.addClass(ce.cls);
30335             removeCls = ce.cls;
30336         }
30337         if(ce.title){
30338             tipTitle.update(ce.title);
30339             tipTitle.show();
30340         }else{
30341             tipTitle.update('');
30342             tipTitle.hide();
30343         }
30344         el.dom.style.width  = tm.maxWidth+'px';
30345         //tipBody.dom.style.width = '';
30346         tipBodyText.update(o.text);
30347         var p = getPad(), w = ce.width;
30348         if(!w){
30349             var td = tipBodyText.dom;
30350             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30351             if(aw > tm.maxWidth){
30352                 w = tm.maxWidth;
30353             }else if(aw < tm.minWidth){
30354                 w = tm.minWidth;
30355             }else{
30356                 w = aw;
30357             }
30358         }
30359         //tipBody.setWidth(w);
30360         el.setWidth(parseInt(w, 10) + p);
30361         if(ce.autoHide === false){
30362             close.setDisplayed(true);
30363             if(dd){
30364                 dd.unlock();
30365             }
30366         }else{
30367             close.setDisplayed(false);
30368             if(dd){
30369                 dd.lock();
30370             }
30371         }
30372         if(xy){
30373             el.avoidY = xy[1]-18;
30374             el.setXY(xy);
30375         }
30376         if(tm.animate){
30377             el.setOpacity(.1);
30378             el.setStyle("visibility", "visible");
30379             el.fadeIn({callback: afterShow});
30380         }else{
30381             afterShow();
30382         }
30383     };
30384     
30385     var afterShow = function(){
30386         if(ce){
30387             el.show();
30388             esc.enable();
30389             if(tm.autoDismiss && ce.autoHide !== false){
30390                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30391             }
30392         }
30393     };
30394     
30395     var hide = function(noanim){
30396         clearTimeout(dismissProc);
30397         clearTimeout(hideProc);
30398         ce = null;
30399         if(el.isVisible()){
30400             esc.disable();
30401             if(noanim !== true && tm.animate){
30402                 el.fadeOut({callback: afterHide});
30403             }else{
30404                 afterHide();
30405             } 
30406         }
30407     };
30408     
30409     var afterHide = function(){
30410         el.hide();
30411         if(removeCls){
30412             el.removeClass(removeCls);
30413             removeCls = null;
30414         }
30415     };
30416     
30417     return {
30418         /**
30419         * @cfg {Number} minWidth
30420         * The minimum width of the quick tip (defaults to 40)
30421         */
30422        minWidth : 40,
30423         /**
30424         * @cfg {Number} maxWidth
30425         * The maximum width of the quick tip (defaults to 300)
30426         */
30427        maxWidth : 300,
30428         /**
30429         * @cfg {Boolean} interceptTitles
30430         * True to automatically use the element's DOM title value if available (defaults to false)
30431         */
30432        interceptTitles : false,
30433         /**
30434         * @cfg {Boolean} trackMouse
30435         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30436         */
30437        trackMouse : false,
30438         /**
30439         * @cfg {Boolean} hideOnClick
30440         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30441         */
30442        hideOnClick : true,
30443         /**
30444         * @cfg {Number} showDelay
30445         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30446         */
30447        showDelay : 500,
30448         /**
30449         * @cfg {Number} hideDelay
30450         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30451         */
30452        hideDelay : 200,
30453         /**
30454         * @cfg {Boolean} autoHide
30455         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30456         * Used in conjunction with hideDelay.
30457         */
30458        autoHide : true,
30459         /**
30460         * @cfg {Boolean}
30461         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30462         * (defaults to true).  Used in conjunction with autoDismissDelay.
30463         */
30464        autoDismiss : true,
30465         /**
30466         * @cfg {Number}
30467         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30468         */
30469        autoDismissDelay : 5000,
30470        /**
30471         * @cfg {Boolean} animate
30472         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30473         */
30474        animate : false,
30475
30476        /**
30477         * @cfg {String} title
30478         * Title text to display (defaults to '').  This can be any valid HTML markup.
30479         */
30480         title: '',
30481        /**
30482         * @cfg {String} text
30483         * Body text to display (defaults to '').  This can be any valid HTML markup.
30484         */
30485         text : '',
30486        /**
30487         * @cfg {String} cls
30488         * A CSS class to apply to the base quick tip element (defaults to '').
30489         */
30490         cls : '',
30491        /**
30492         * @cfg {Number} width
30493         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30494         * minWidth or maxWidth.
30495         */
30496         width : null,
30497
30498     /**
30499      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30500      * or display QuickTips in a page.
30501      */
30502        init : function(){
30503           tm = Roo.QuickTips;
30504           cfg = tm.tagConfig;
30505           if(!inited){
30506               if(!Roo.isReady){ // allow calling of init() before onReady
30507                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30508                   return;
30509               }
30510               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30511               el.fxDefaults = {stopFx: true};
30512               // maximum custom styling
30513               //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>');
30514               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>');              
30515               tipTitle = el.child('h3');
30516               tipTitle.enableDisplayMode("block");
30517               tipBody = el.child('div.x-tip-bd');
30518               tipBodyText = el.child('div.x-tip-bd-inner');
30519               //bdLeft = el.child('div.x-tip-bd-left');
30520               //bdRight = el.child('div.x-tip-bd-right');
30521               close = el.child('div.x-tip-close');
30522               close.enableDisplayMode("block");
30523               close.on("click", hide);
30524               var d = Roo.get(document);
30525               d.on("mousedown", onDown);
30526               d.on("mouseover", onOver);
30527               d.on("mouseout", onOut);
30528               d.on("mousemove", onMove);
30529               esc = d.addKeyListener(27, hide);
30530               esc.disable();
30531               if(Roo.dd.DD){
30532                   dd = el.initDD("default", null, {
30533                       onDrag : function(){
30534                           el.sync();  
30535                       }
30536                   });
30537                   dd.setHandleElId(tipTitle.id);
30538                   dd.lock();
30539               }
30540               inited = true;
30541           }
30542           this.enable(); 
30543        },
30544
30545     /**
30546      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30547      * are supported:
30548      * <pre>
30549 Property    Type                   Description
30550 ----------  ---------------------  ------------------------------------------------------------------------
30551 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30552      * </ul>
30553      * @param {Object} config The config object
30554      */
30555        register : function(config){
30556            var cs = config instanceof Array ? config : arguments;
30557            for(var i = 0, len = cs.length; i < len; i++) {
30558                var c = cs[i];
30559                var target = c.target;
30560                if(target){
30561                    if(target instanceof Array){
30562                        for(var j = 0, jlen = target.length; j < jlen; j++){
30563                            tagEls[target[j]] = c;
30564                        }
30565                    }else{
30566                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30567                    }
30568                }
30569            }
30570        },
30571
30572     /**
30573      * Removes this quick tip from its element and destroys it.
30574      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30575      */
30576        unregister : function(el){
30577            delete tagEls[Roo.id(el)];
30578        },
30579
30580     /**
30581      * Enable this quick tip.
30582      */
30583        enable : function(){
30584            if(inited && disabled){
30585                locks.pop();
30586                if(locks.length < 1){
30587                    disabled = false;
30588                }
30589            }
30590        },
30591
30592     /**
30593      * Disable this quick tip.
30594      */
30595        disable : function(){
30596           disabled = true;
30597           clearTimeout(showProc);
30598           clearTimeout(hideProc);
30599           clearTimeout(dismissProc);
30600           if(ce){
30601               hide(true);
30602           }
30603           locks.push(1);
30604        },
30605
30606     /**
30607      * Returns true if the quick tip is enabled, else false.
30608      */
30609        isEnabled : function(){
30610             return !disabled;
30611        },
30612
30613         // private
30614        tagConfig : {
30615            namespace : "ext",
30616            attribute : "qtip",
30617            width : "width",
30618            target : "target",
30619            title : "qtitle",
30620            hide : "hide",
30621            cls : "qclass"
30622        }
30623    };
30624 }();
30625
30626 // backwards compat
30627 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30628  * Based on:
30629  * Ext JS Library 1.1.1
30630  * Copyright(c) 2006-2007, Ext JS, LLC.
30631  *
30632  * Originally Released Under LGPL - original licence link has changed is not relivant.
30633  *
30634  * Fork - LGPL
30635  * <script type="text/javascript">
30636  */
30637  
30638
30639 /**
30640  * @class Roo.tree.TreePanel
30641  * @extends Roo.data.Tree
30642
30643  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30644  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30645  * @cfg {Boolean} enableDD true to enable drag and drop
30646  * @cfg {Boolean} enableDrag true to enable just drag
30647  * @cfg {Boolean} enableDrop true to enable just drop
30648  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30649  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30650  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30651  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30652  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30653  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30654  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30655  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30656  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30657  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30658  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30659  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30660  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30661  * @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>
30662  * @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>
30663  * 
30664  * @constructor
30665  * @param {String/HTMLElement/Element} el The container element
30666  * @param {Object} config
30667  */
30668 Roo.tree.TreePanel = function(el, config){
30669     var root = false;
30670     var loader = false;
30671     if (config.root) {
30672         root = config.root;
30673         delete config.root;
30674     }
30675     if (config.loader) {
30676         loader = config.loader;
30677         delete config.loader;
30678     }
30679     
30680     Roo.apply(this, config);
30681     Roo.tree.TreePanel.superclass.constructor.call(this);
30682     this.el = Roo.get(el);
30683     this.el.addClass('x-tree');
30684     //console.log(root);
30685     if (root) {
30686         this.setRootNode( Roo.factory(root, Roo.tree));
30687     }
30688     if (loader) {
30689         this.loader = Roo.factory(loader, Roo.tree);
30690     }
30691    /**
30692     * Read-only. The id of the container element becomes this TreePanel's id.
30693     */
30694    this.id = this.el.id;
30695    this.addEvents({
30696         /**
30697         * @event beforeload
30698         * Fires before a node is loaded, return false to cancel
30699         * @param {Node} node The node being loaded
30700         */
30701         "beforeload" : true,
30702         /**
30703         * @event load
30704         * Fires when a node is loaded
30705         * @param {Node} node The node that was loaded
30706         */
30707         "load" : true,
30708         /**
30709         * @event textchange
30710         * Fires when the text for a node is changed
30711         * @param {Node} node The node
30712         * @param {String} text The new text
30713         * @param {String} oldText The old text
30714         */
30715         "textchange" : true,
30716         /**
30717         * @event beforeexpand
30718         * Fires before a node is expanded, return false to cancel.
30719         * @param {Node} node The node
30720         * @param {Boolean} deep
30721         * @param {Boolean} anim
30722         */
30723         "beforeexpand" : true,
30724         /**
30725         * @event beforecollapse
30726         * Fires before a node is collapsed, return false to cancel.
30727         * @param {Node} node The node
30728         * @param {Boolean} deep
30729         * @param {Boolean} anim
30730         */
30731         "beforecollapse" : true,
30732         /**
30733         * @event expand
30734         * Fires when a node is expanded
30735         * @param {Node} node The node
30736         */
30737         "expand" : true,
30738         /**
30739         * @event disabledchange
30740         * Fires when the disabled status of a node changes
30741         * @param {Node} node The node
30742         * @param {Boolean} disabled
30743         */
30744         "disabledchange" : true,
30745         /**
30746         * @event collapse
30747         * Fires when a node is collapsed
30748         * @param {Node} node The node
30749         */
30750         "collapse" : true,
30751         /**
30752         * @event beforeclick
30753         * Fires before click processing on a node. Return false to cancel the default action.
30754         * @param {Node} node The node
30755         * @param {Roo.EventObject} e The event object
30756         */
30757         "beforeclick":true,
30758         /**
30759         * @event checkchange
30760         * Fires when a node with a checkbox's checked property changes
30761         * @param {Node} this This node
30762         * @param {Boolean} checked
30763         */
30764         "checkchange":true,
30765         /**
30766         * @event click
30767         * Fires when a node is clicked
30768         * @param {Node} node The node
30769         * @param {Roo.EventObject} e The event object
30770         */
30771         "click":true,
30772         /**
30773         * @event dblclick
30774         * Fires when a node is double clicked
30775         * @param {Node} node The node
30776         * @param {Roo.EventObject} e The event object
30777         */
30778         "dblclick":true,
30779         /**
30780         * @event contextmenu
30781         * Fires when a node is right clicked
30782         * @param {Node} node The node
30783         * @param {Roo.EventObject} e The event object
30784         */
30785         "contextmenu":true,
30786         /**
30787         * @event beforechildrenrendered
30788         * Fires right before the child nodes for a node are rendered
30789         * @param {Node} node The node
30790         */
30791         "beforechildrenrendered":true,
30792        /**
30793              * @event startdrag
30794              * Fires when a node starts being dragged
30795              * @param {Roo.tree.TreePanel} this
30796              * @param {Roo.tree.TreeNode} node
30797              * @param {event} e The raw browser event
30798              */ 
30799             "startdrag" : true,
30800             /**
30801              * @event enddrag
30802              * Fires when a drag operation is complete
30803              * @param {Roo.tree.TreePanel} this
30804              * @param {Roo.tree.TreeNode} node
30805              * @param {event} e The raw browser event
30806              */
30807             "enddrag" : true,
30808             /**
30809              * @event dragdrop
30810              * Fires when a dragged node is dropped on a valid DD target
30811              * @param {Roo.tree.TreePanel} this
30812              * @param {Roo.tree.TreeNode} node
30813              * @param {DD} dd The dd it was dropped on
30814              * @param {event} e The raw browser event
30815              */
30816             "dragdrop" : true,
30817             /**
30818              * @event beforenodedrop
30819              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30820              * passed to handlers has the following properties:<br />
30821              * <ul style="padding:5px;padding-left:16px;">
30822              * <li>tree - The TreePanel</li>
30823              * <li>target - The node being targeted for the drop</li>
30824              * <li>data - The drag data from the drag source</li>
30825              * <li>point - The point of the drop - append, above or below</li>
30826              * <li>source - The drag source</li>
30827              * <li>rawEvent - Raw mouse event</li>
30828              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30829              * to be inserted by setting them on this object.</li>
30830              * <li>cancel - Set this to true to cancel the drop.</li>
30831              * </ul>
30832              * @param {Object} dropEvent
30833              */
30834             "beforenodedrop" : true,
30835             /**
30836              * @event nodedrop
30837              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30838              * passed to handlers has the following properties:<br />
30839              * <ul style="padding:5px;padding-left:16px;">
30840              * <li>tree - The TreePanel</li>
30841              * <li>target - The node being targeted for the drop</li>
30842              * <li>data - The drag data from the drag source</li>
30843              * <li>point - The point of the drop - append, above or below</li>
30844              * <li>source - The drag source</li>
30845              * <li>rawEvent - Raw mouse event</li>
30846              * <li>dropNode - Dropped node(s).</li>
30847              * </ul>
30848              * @param {Object} dropEvent
30849              */
30850             "nodedrop" : true,
30851              /**
30852              * @event nodedragover
30853              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30854              * passed to handlers has the following properties:<br />
30855              * <ul style="padding:5px;padding-left:16px;">
30856              * <li>tree - The TreePanel</li>
30857              * <li>target - The node being targeted for the drop</li>
30858              * <li>data - The drag data from the drag source</li>
30859              * <li>point - The point of the drop - append, above or below</li>
30860              * <li>source - The drag source</li>
30861              * <li>rawEvent - Raw mouse event</li>
30862              * <li>dropNode - Drop node(s) provided by the source.</li>
30863              * <li>cancel - Set this to true to signal drop not allowed.</li>
30864              * </ul>
30865              * @param {Object} dragOverEvent
30866              */
30867             "nodedragover" : true
30868         
30869    });
30870    if(this.singleExpand){
30871        this.on("beforeexpand", this.restrictExpand, this);
30872    }
30873 };
30874 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30875     rootVisible : true,
30876     animate: Roo.enableFx,
30877     lines : true,
30878     enableDD : false,
30879     hlDrop : Roo.enableFx,
30880   
30881     renderer: false,
30882     
30883     rendererTip: false,
30884     // private
30885     restrictExpand : function(node){
30886         var p = node.parentNode;
30887         if(p){
30888             if(p.expandedChild && p.expandedChild.parentNode == p){
30889                 p.expandedChild.collapse();
30890             }
30891             p.expandedChild = node;
30892         }
30893     },
30894
30895     // private override
30896     setRootNode : function(node){
30897         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30898         if(!this.rootVisible){
30899             node.ui = new Roo.tree.RootTreeNodeUI(node);
30900         }
30901         return node;
30902     },
30903
30904     /**
30905      * Returns the container element for this TreePanel
30906      */
30907     getEl : function(){
30908         return this.el;
30909     },
30910
30911     /**
30912      * Returns the default TreeLoader for this TreePanel
30913      */
30914     getLoader : function(){
30915         return this.loader;
30916     },
30917
30918     /**
30919      * Expand all nodes
30920      */
30921     expandAll : function(){
30922         this.root.expand(true);
30923     },
30924
30925     /**
30926      * Collapse all nodes
30927      */
30928     collapseAll : function(){
30929         this.root.collapse(true);
30930     },
30931
30932     /**
30933      * Returns the selection model used by this TreePanel
30934      */
30935     getSelectionModel : function(){
30936         if(!this.selModel){
30937             this.selModel = new Roo.tree.DefaultSelectionModel();
30938         }
30939         return this.selModel;
30940     },
30941
30942     /**
30943      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30944      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30945      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30946      * @return {Array}
30947      */
30948     getChecked : function(a, startNode){
30949         startNode = startNode || this.root;
30950         var r = [];
30951         var f = function(){
30952             if(this.attributes.checked){
30953                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30954             }
30955         }
30956         startNode.cascade(f);
30957         return r;
30958     },
30959
30960     /**
30961      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30962      * @param {String} path
30963      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30964      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30965      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30966      */
30967     expandPath : function(path, attr, callback){
30968         attr = attr || "id";
30969         var keys = path.split(this.pathSeparator);
30970         var curNode = this.root;
30971         if(curNode.attributes[attr] != keys[1]){ // invalid root
30972             if(callback){
30973                 callback(false, null);
30974             }
30975             return;
30976         }
30977         var index = 1;
30978         var f = function(){
30979             if(++index == keys.length){
30980                 if(callback){
30981                     callback(true, curNode);
30982                 }
30983                 return;
30984             }
30985             var c = curNode.findChild(attr, keys[index]);
30986             if(!c){
30987                 if(callback){
30988                     callback(false, curNode);
30989                 }
30990                 return;
30991             }
30992             curNode = c;
30993             c.expand(false, false, f);
30994         };
30995         curNode.expand(false, false, f);
30996     },
30997
30998     /**
30999      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31000      * @param {String} path
31001      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31002      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31003      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31004      */
31005     selectPath : function(path, attr, callback){
31006         attr = attr || "id";
31007         var keys = path.split(this.pathSeparator);
31008         var v = keys.pop();
31009         if(keys.length > 0){
31010             var f = function(success, node){
31011                 if(success && node){
31012                     var n = node.findChild(attr, v);
31013                     if(n){
31014                         n.select();
31015                         if(callback){
31016                             callback(true, n);
31017                         }
31018                     }else if(callback){
31019                         callback(false, n);
31020                     }
31021                 }else{
31022                     if(callback){
31023                         callback(false, n);
31024                     }
31025                 }
31026             };
31027             this.expandPath(keys.join(this.pathSeparator), attr, f);
31028         }else{
31029             this.root.select();
31030             if(callback){
31031                 callback(true, this.root);
31032             }
31033         }
31034     },
31035
31036     getTreeEl : function(){
31037         return this.el;
31038     },
31039
31040     /**
31041      * Trigger rendering of this TreePanel
31042      */
31043     render : function(){
31044         if (this.innerCt) {
31045             return this; // stop it rendering more than once!!
31046         }
31047         
31048         this.innerCt = this.el.createChild({tag:"ul",
31049                cls:"x-tree-root-ct " +
31050                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31051
31052         if(this.containerScroll){
31053             Roo.dd.ScrollManager.register(this.el);
31054         }
31055         if((this.enableDD || this.enableDrop) && !this.dropZone){
31056            /**
31057             * The dropZone used by this tree if drop is enabled
31058             * @type Roo.tree.TreeDropZone
31059             */
31060              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31061                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31062            });
31063         }
31064         if((this.enableDD || this.enableDrag) && !this.dragZone){
31065            /**
31066             * The dragZone used by this tree if drag is enabled
31067             * @type Roo.tree.TreeDragZone
31068             */
31069             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31070                ddGroup: this.ddGroup || "TreeDD",
31071                scroll: this.ddScroll
31072            });
31073         }
31074         this.getSelectionModel().init(this);
31075         if (!this.root) {
31076             console.log("ROOT not set in tree");
31077             return;
31078         }
31079         this.root.render();
31080         if(!this.rootVisible){
31081             this.root.renderChildren();
31082         }
31083         return this;
31084     }
31085 });/*
31086  * Based on:
31087  * Ext JS Library 1.1.1
31088  * Copyright(c) 2006-2007, Ext JS, LLC.
31089  *
31090  * Originally Released Under LGPL - original licence link has changed is not relivant.
31091  *
31092  * Fork - LGPL
31093  * <script type="text/javascript">
31094  */
31095  
31096
31097 /**
31098  * @class Roo.tree.DefaultSelectionModel
31099  * @extends Roo.util.Observable
31100  * The default single selection for a TreePanel.
31101  */
31102 Roo.tree.DefaultSelectionModel = function(){
31103    this.selNode = null;
31104    
31105    this.addEvents({
31106        /**
31107         * @event selectionchange
31108         * Fires when the selected node changes
31109         * @param {DefaultSelectionModel} this
31110         * @param {TreeNode} node the new selection
31111         */
31112        "selectionchange" : true,
31113
31114        /**
31115         * @event beforeselect
31116         * Fires before the selected node changes, return false to cancel the change
31117         * @param {DefaultSelectionModel} this
31118         * @param {TreeNode} node the new selection
31119         * @param {TreeNode} node the old selection
31120         */
31121        "beforeselect" : true
31122    });
31123 };
31124
31125 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31126     init : function(tree){
31127         this.tree = tree;
31128         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31129         tree.on("click", this.onNodeClick, this);
31130     },
31131     
31132     onNodeClick : function(node, e){
31133         if (e.ctrlKey && this.selNode == node)  {
31134             this.unselect(node);
31135             return;
31136         }
31137         this.select(node);
31138     },
31139     
31140     /**
31141      * Select a node.
31142      * @param {TreeNode} node The node to select
31143      * @return {TreeNode} The selected node
31144      */
31145     select : function(node){
31146         var last = this.selNode;
31147         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31148             if(last){
31149                 last.ui.onSelectedChange(false);
31150             }
31151             this.selNode = node;
31152             node.ui.onSelectedChange(true);
31153             this.fireEvent("selectionchange", this, node, last);
31154         }
31155         return node;
31156     },
31157     
31158     /**
31159      * Deselect a node.
31160      * @param {TreeNode} node The node to unselect
31161      */
31162     unselect : function(node){
31163         if(this.selNode == node){
31164             this.clearSelections();
31165         }    
31166     },
31167     
31168     /**
31169      * Clear all selections
31170      */
31171     clearSelections : function(){
31172         var n = this.selNode;
31173         if(n){
31174             n.ui.onSelectedChange(false);
31175             this.selNode = null;
31176             this.fireEvent("selectionchange", this, null);
31177         }
31178         return n;
31179     },
31180     
31181     /**
31182      * Get the selected node
31183      * @return {TreeNode} The selected node
31184      */
31185     getSelectedNode : function(){
31186         return this.selNode;    
31187     },
31188     
31189     /**
31190      * Returns true if the node is selected
31191      * @param {TreeNode} node The node to check
31192      * @return {Boolean}
31193      */
31194     isSelected : function(node){
31195         return this.selNode == node;  
31196     },
31197
31198     /**
31199      * Selects the node above the selected node in the tree, intelligently walking the nodes
31200      * @return TreeNode The new selection
31201      */
31202     selectPrevious : function(){
31203         var s = this.selNode || this.lastSelNode;
31204         if(!s){
31205             return null;
31206         }
31207         var ps = s.previousSibling;
31208         if(ps){
31209             if(!ps.isExpanded() || ps.childNodes.length < 1){
31210                 return this.select(ps);
31211             } else{
31212                 var lc = ps.lastChild;
31213                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31214                     lc = lc.lastChild;
31215                 }
31216                 return this.select(lc);
31217             }
31218         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31219             return this.select(s.parentNode);
31220         }
31221         return null;
31222     },
31223
31224     /**
31225      * Selects the node above the selected node in the tree, intelligently walking the nodes
31226      * @return TreeNode The new selection
31227      */
31228     selectNext : function(){
31229         var s = this.selNode || this.lastSelNode;
31230         if(!s){
31231             return null;
31232         }
31233         if(s.firstChild && s.isExpanded()){
31234              return this.select(s.firstChild);
31235          }else if(s.nextSibling){
31236              return this.select(s.nextSibling);
31237          }else if(s.parentNode){
31238             var newS = null;
31239             s.parentNode.bubble(function(){
31240                 if(this.nextSibling){
31241                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31242                     return false;
31243                 }
31244             });
31245             return newS;
31246          }
31247         return null;
31248     },
31249
31250     onKeyDown : function(e){
31251         var s = this.selNode || this.lastSelNode;
31252         // undesirable, but required
31253         var sm = this;
31254         if(!s){
31255             return;
31256         }
31257         var k = e.getKey();
31258         switch(k){
31259              case e.DOWN:
31260                  e.stopEvent();
31261                  this.selectNext();
31262              break;
31263              case e.UP:
31264                  e.stopEvent();
31265                  this.selectPrevious();
31266              break;
31267              case e.RIGHT:
31268                  e.preventDefault();
31269                  if(s.hasChildNodes()){
31270                      if(!s.isExpanded()){
31271                          s.expand();
31272                      }else if(s.firstChild){
31273                          this.select(s.firstChild, e);
31274                      }
31275                  }
31276              break;
31277              case e.LEFT:
31278                  e.preventDefault();
31279                  if(s.hasChildNodes() && s.isExpanded()){
31280                      s.collapse();
31281                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31282                      this.select(s.parentNode, e);
31283                  }
31284              break;
31285         };
31286     }
31287 });
31288
31289 /**
31290  * @class Roo.tree.MultiSelectionModel
31291  * @extends Roo.util.Observable
31292  * Multi selection for a TreePanel.
31293  */
31294 Roo.tree.MultiSelectionModel = function(){
31295    this.selNodes = [];
31296    this.selMap = {};
31297    this.addEvents({
31298        /**
31299         * @event selectionchange
31300         * Fires when the selected nodes change
31301         * @param {MultiSelectionModel} this
31302         * @param {Array} nodes Array of the selected nodes
31303         */
31304        "selectionchange" : true
31305    });
31306 };
31307
31308 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31309     init : function(tree){
31310         this.tree = tree;
31311         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31312         tree.on("click", this.onNodeClick, this);
31313     },
31314     
31315     onNodeClick : function(node, e){
31316         this.select(node, e, e.ctrlKey);
31317     },
31318     
31319     /**
31320      * Select a node.
31321      * @param {TreeNode} node The node to select
31322      * @param {EventObject} e (optional) An event associated with the selection
31323      * @param {Boolean} keepExisting True to retain existing selections
31324      * @return {TreeNode} The selected node
31325      */
31326     select : function(node, e, keepExisting){
31327         if(keepExisting !== true){
31328             this.clearSelections(true);
31329         }
31330         if(this.isSelected(node)){
31331             this.lastSelNode = node;
31332             return node;
31333         }
31334         this.selNodes.push(node);
31335         this.selMap[node.id] = node;
31336         this.lastSelNode = node;
31337         node.ui.onSelectedChange(true);
31338         this.fireEvent("selectionchange", this, this.selNodes);
31339         return node;
31340     },
31341     
31342     /**
31343      * Deselect a node.
31344      * @param {TreeNode} node The node to unselect
31345      */
31346     unselect : function(node){
31347         if(this.selMap[node.id]){
31348             node.ui.onSelectedChange(false);
31349             var sn = this.selNodes;
31350             var index = -1;
31351             if(sn.indexOf){
31352                 index = sn.indexOf(node);
31353             }else{
31354                 for(var i = 0, len = sn.length; i < len; i++){
31355                     if(sn[i] == node){
31356                         index = i;
31357                         break;
31358                     }
31359                 }
31360             }
31361             if(index != -1){
31362                 this.selNodes.splice(index, 1);
31363             }
31364             delete this.selMap[node.id];
31365             this.fireEvent("selectionchange", this, this.selNodes);
31366         }
31367     },
31368     
31369     /**
31370      * Clear all selections
31371      */
31372     clearSelections : function(suppressEvent){
31373         var sn = this.selNodes;
31374         if(sn.length > 0){
31375             for(var i = 0, len = sn.length; i < len; i++){
31376                 sn[i].ui.onSelectedChange(false);
31377             }
31378             this.selNodes = [];
31379             this.selMap = {};
31380             if(suppressEvent !== true){
31381                 this.fireEvent("selectionchange", this, this.selNodes);
31382             }
31383         }
31384     },
31385     
31386     /**
31387      * Returns true if the node is selected
31388      * @param {TreeNode} node The node to check
31389      * @return {Boolean}
31390      */
31391     isSelected : function(node){
31392         return this.selMap[node.id] ? true : false;  
31393     },
31394     
31395     /**
31396      * Returns an array of the selected nodes
31397      * @return {Array}
31398      */
31399     getSelectedNodes : function(){
31400         return this.selNodes;    
31401     },
31402
31403     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31404
31405     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31406
31407     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31408 });/*
31409  * Based on:
31410  * Ext JS Library 1.1.1
31411  * Copyright(c) 2006-2007, Ext JS, LLC.
31412  *
31413  * Originally Released Under LGPL - original licence link has changed is not relivant.
31414  *
31415  * Fork - LGPL
31416  * <script type="text/javascript">
31417  */
31418  
31419 /**
31420  * @class Roo.tree.TreeNode
31421  * @extends Roo.data.Node
31422  * @cfg {String} text The text for this node
31423  * @cfg {Boolean} expanded true to start the node expanded
31424  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31425  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31426  * @cfg {Boolean} disabled true to start the node disabled
31427  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31428  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31429  * @cfg {String} cls A css class to be added to the node
31430  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31431  * @cfg {String} href URL of the link used for the node (defaults to #)
31432  * @cfg {String} hrefTarget target frame for the link
31433  * @cfg {String} qtip An Ext QuickTip for the node
31434  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31435  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31436  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31437  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31438  * (defaults to undefined with no checkbox rendered)
31439  * @constructor
31440  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31441  */
31442 Roo.tree.TreeNode = function(attributes){
31443     attributes = attributes || {};
31444     if(typeof attributes == "string"){
31445         attributes = {text: attributes};
31446     }
31447     this.childrenRendered = false;
31448     this.rendered = false;
31449     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31450     this.expanded = attributes.expanded === true;
31451     this.isTarget = attributes.isTarget !== false;
31452     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31453     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31454
31455     /**
31456      * Read-only. The text for this node. To change it use setText().
31457      * @type String
31458      */
31459     this.text = attributes.text;
31460     /**
31461      * True if this node is disabled.
31462      * @type Boolean
31463      */
31464     this.disabled = attributes.disabled === true;
31465
31466     this.addEvents({
31467         /**
31468         * @event textchange
31469         * Fires when the text for this node is changed
31470         * @param {Node} this This node
31471         * @param {String} text The new text
31472         * @param {String} oldText The old text
31473         */
31474         "textchange" : true,
31475         /**
31476         * @event beforeexpand
31477         * Fires before this node is expanded, return false to cancel.
31478         * @param {Node} this This node
31479         * @param {Boolean} deep
31480         * @param {Boolean} anim
31481         */
31482         "beforeexpand" : true,
31483         /**
31484         * @event beforecollapse
31485         * Fires before this node is collapsed, return false to cancel.
31486         * @param {Node} this This node
31487         * @param {Boolean} deep
31488         * @param {Boolean} anim
31489         */
31490         "beforecollapse" : true,
31491         /**
31492         * @event expand
31493         * Fires when this node is expanded
31494         * @param {Node} this This node
31495         */
31496         "expand" : true,
31497         /**
31498         * @event disabledchange
31499         * Fires when the disabled status of this node changes
31500         * @param {Node} this This node
31501         * @param {Boolean} disabled
31502         */
31503         "disabledchange" : true,
31504         /**
31505         * @event collapse
31506         * Fires when this node is collapsed
31507         * @param {Node} this This node
31508         */
31509         "collapse" : true,
31510         /**
31511         * @event beforeclick
31512         * Fires before click processing. Return false to cancel the default action.
31513         * @param {Node} this This node
31514         * @param {Roo.EventObject} e The event object
31515         */
31516         "beforeclick":true,
31517         /**
31518         * @event checkchange
31519         * Fires when a node with a checkbox's checked property changes
31520         * @param {Node} this This node
31521         * @param {Boolean} checked
31522         */
31523         "checkchange":true,
31524         /**
31525         * @event click
31526         * Fires when this node is clicked
31527         * @param {Node} this This node
31528         * @param {Roo.EventObject} e The event object
31529         */
31530         "click":true,
31531         /**
31532         * @event dblclick
31533         * Fires when this node is double clicked
31534         * @param {Node} this This node
31535         * @param {Roo.EventObject} e The event object
31536         */
31537         "dblclick":true,
31538         /**
31539         * @event contextmenu
31540         * Fires when this node is right clicked
31541         * @param {Node} this This node
31542         * @param {Roo.EventObject} e The event object
31543         */
31544         "contextmenu":true,
31545         /**
31546         * @event beforechildrenrendered
31547         * Fires right before the child nodes for this node are rendered
31548         * @param {Node} this This node
31549         */
31550         "beforechildrenrendered":true
31551     });
31552
31553     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31554
31555     /**
31556      * Read-only. The UI for this node
31557      * @type TreeNodeUI
31558      */
31559     this.ui = new uiClass(this);
31560 };
31561 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31562     preventHScroll: true,
31563     /**
31564      * Returns true if this node is expanded
31565      * @return {Boolean}
31566      */
31567     isExpanded : function(){
31568         return this.expanded;
31569     },
31570
31571     /**
31572      * Returns the UI object for this node
31573      * @return {TreeNodeUI}
31574      */
31575     getUI : function(){
31576         return this.ui;
31577     },
31578
31579     // private override
31580     setFirstChild : function(node){
31581         var of = this.firstChild;
31582         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31583         if(this.childrenRendered && of && node != of){
31584             of.renderIndent(true, true);
31585         }
31586         if(this.rendered){
31587             this.renderIndent(true, true);
31588         }
31589     },
31590
31591     // private override
31592     setLastChild : function(node){
31593         var ol = this.lastChild;
31594         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31595         if(this.childrenRendered && ol && node != ol){
31596             ol.renderIndent(true, true);
31597         }
31598         if(this.rendered){
31599             this.renderIndent(true, true);
31600         }
31601     },
31602
31603     // these methods are overridden to provide lazy rendering support
31604     // private override
31605     appendChild : function(){
31606         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31607         if(node && this.childrenRendered){
31608             node.render();
31609         }
31610         this.ui.updateExpandIcon();
31611         return node;
31612     },
31613
31614     // private override
31615     removeChild : function(node){
31616         this.ownerTree.getSelectionModel().unselect(node);
31617         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31618         // if it's been rendered remove dom node
31619         if(this.childrenRendered){
31620             node.ui.remove();
31621         }
31622         if(this.childNodes.length < 1){
31623             this.collapse(false, false);
31624         }else{
31625             this.ui.updateExpandIcon();
31626         }
31627         if(!this.firstChild) {
31628             this.childrenRendered = false;
31629         }
31630         return node;
31631     },
31632
31633     // private override
31634     insertBefore : function(node, refNode){
31635         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31636         if(newNode && refNode && this.childrenRendered){
31637             node.render();
31638         }
31639         this.ui.updateExpandIcon();
31640         return newNode;
31641     },
31642
31643     /**
31644      * Sets the text for this node
31645      * @param {String} text
31646      */
31647     setText : function(text){
31648         var oldText = this.text;
31649         this.text = text;
31650         this.attributes.text = text;
31651         if(this.rendered){ // event without subscribing
31652             this.ui.onTextChange(this, text, oldText);
31653         }
31654         this.fireEvent("textchange", this, text, oldText);
31655     },
31656
31657     /**
31658      * Triggers selection of this node
31659      */
31660     select : function(){
31661         this.getOwnerTree().getSelectionModel().select(this);
31662     },
31663
31664     /**
31665      * Triggers deselection of this node
31666      */
31667     unselect : function(){
31668         this.getOwnerTree().getSelectionModel().unselect(this);
31669     },
31670
31671     /**
31672      * Returns true if this node is selected
31673      * @return {Boolean}
31674      */
31675     isSelected : function(){
31676         return this.getOwnerTree().getSelectionModel().isSelected(this);
31677     },
31678
31679     /**
31680      * Expand this node.
31681      * @param {Boolean} deep (optional) True to expand all children as well
31682      * @param {Boolean} anim (optional) false to cancel the default animation
31683      * @param {Function} callback (optional) A callback to be called when
31684      * expanding this node completes (does not wait for deep expand to complete).
31685      * Called with 1 parameter, this node.
31686      */
31687     expand : function(deep, anim, callback){
31688         if(!this.expanded){
31689             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31690                 return;
31691             }
31692             if(!this.childrenRendered){
31693                 this.renderChildren();
31694             }
31695             this.expanded = true;
31696             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31697                 this.ui.animExpand(function(){
31698                     this.fireEvent("expand", this);
31699                     if(typeof callback == "function"){
31700                         callback(this);
31701                     }
31702                     if(deep === true){
31703                         this.expandChildNodes(true);
31704                     }
31705                 }.createDelegate(this));
31706                 return;
31707             }else{
31708                 this.ui.expand();
31709                 this.fireEvent("expand", this);
31710                 if(typeof callback == "function"){
31711                     callback(this);
31712                 }
31713             }
31714         }else{
31715            if(typeof callback == "function"){
31716                callback(this);
31717            }
31718         }
31719         if(deep === true){
31720             this.expandChildNodes(true);
31721         }
31722     },
31723
31724     isHiddenRoot : function(){
31725         return this.isRoot && !this.getOwnerTree().rootVisible;
31726     },
31727
31728     /**
31729      * Collapse this node.
31730      * @param {Boolean} deep (optional) True to collapse all children as well
31731      * @param {Boolean} anim (optional) false to cancel the default animation
31732      */
31733     collapse : function(deep, anim){
31734         if(this.expanded && !this.isHiddenRoot()){
31735             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31736                 return;
31737             }
31738             this.expanded = false;
31739             if((this.getOwnerTree().animate && anim !== false) || anim){
31740                 this.ui.animCollapse(function(){
31741                     this.fireEvent("collapse", this);
31742                     if(deep === true){
31743                         this.collapseChildNodes(true);
31744                     }
31745                 }.createDelegate(this));
31746                 return;
31747             }else{
31748                 this.ui.collapse();
31749                 this.fireEvent("collapse", this);
31750             }
31751         }
31752         if(deep === true){
31753             var cs = this.childNodes;
31754             for(var i = 0, len = cs.length; i < len; i++) {
31755                 cs[i].collapse(true, false);
31756             }
31757         }
31758     },
31759
31760     // private
31761     delayedExpand : function(delay){
31762         if(!this.expandProcId){
31763             this.expandProcId = this.expand.defer(delay, this);
31764         }
31765     },
31766
31767     // private
31768     cancelExpand : function(){
31769         if(this.expandProcId){
31770             clearTimeout(this.expandProcId);
31771         }
31772         this.expandProcId = false;
31773     },
31774
31775     /**
31776      * Toggles expanded/collapsed state of the node
31777      */
31778     toggle : function(){
31779         if(this.expanded){
31780             this.collapse();
31781         }else{
31782             this.expand();
31783         }
31784     },
31785
31786     /**
31787      * Ensures all parent nodes are expanded
31788      */
31789     ensureVisible : function(callback){
31790         var tree = this.getOwnerTree();
31791         tree.expandPath(this.parentNode.getPath(), false, function(){
31792             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31793             Roo.callback(callback);
31794         }.createDelegate(this));
31795     },
31796
31797     /**
31798      * Expand all child nodes
31799      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31800      */
31801     expandChildNodes : function(deep){
31802         var cs = this.childNodes;
31803         for(var i = 0, len = cs.length; i < len; i++) {
31804                 cs[i].expand(deep);
31805         }
31806     },
31807
31808     /**
31809      * Collapse all child nodes
31810      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31811      */
31812     collapseChildNodes : function(deep){
31813         var cs = this.childNodes;
31814         for(var i = 0, len = cs.length; i < len; i++) {
31815                 cs[i].collapse(deep);
31816         }
31817     },
31818
31819     /**
31820      * Disables this node
31821      */
31822     disable : function(){
31823         this.disabled = true;
31824         this.unselect();
31825         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31826             this.ui.onDisableChange(this, true);
31827         }
31828         this.fireEvent("disabledchange", this, true);
31829     },
31830
31831     /**
31832      * Enables this node
31833      */
31834     enable : function(){
31835         this.disabled = false;
31836         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31837             this.ui.onDisableChange(this, false);
31838         }
31839         this.fireEvent("disabledchange", this, false);
31840     },
31841
31842     // private
31843     renderChildren : function(suppressEvent){
31844         if(suppressEvent !== false){
31845             this.fireEvent("beforechildrenrendered", this);
31846         }
31847         var cs = this.childNodes;
31848         for(var i = 0, len = cs.length; i < len; i++){
31849             cs[i].render(true);
31850         }
31851         this.childrenRendered = true;
31852     },
31853
31854     // private
31855     sort : function(fn, scope){
31856         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31857         if(this.childrenRendered){
31858             var cs = this.childNodes;
31859             for(var i = 0, len = cs.length; i < len; i++){
31860                 cs[i].render(true);
31861             }
31862         }
31863     },
31864
31865     // private
31866     render : function(bulkRender){
31867         this.ui.render(bulkRender);
31868         if(!this.rendered){
31869             this.rendered = true;
31870             if(this.expanded){
31871                 this.expanded = false;
31872                 this.expand(false, false);
31873             }
31874         }
31875     },
31876
31877     // private
31878     renderIndent : function(deep, refresh){
31879         if(refresh){
31880             this.ui.childIndent = null;
31881         }
31882         this.ui.renderIndent();
31883         if(deep === true && this.childrenRendered){
31884             var cs = this.childNodes;
31885             for(var i = 0, len = cs.length; i < len; i++){
31886                 cs[i].renderIndent(true, refresh);
31887             }
31888         }
31889     }
31890 });/*
31891  * Based on:
31892  * Ext JS Library 1.1.1
31893  * Copyright(c) 2006-2007, Ext JS, LLC.
31894  *
31895  * Originally Released Under LGPL - original licence link has changed is not relivant.
31896  *
31897  * Fork - LGPL
31898  * <script type="text/javascript">
31899  */
31900  
31901 /**
31902  * @class Roo.tree.AsyncTreeNode
31903  * @extends Roo.tree.TreeNode
31904  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31905  * @constructor
31906  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31907  */
31908  Roo.tree.AsyncTreeNode = function(config){
31909     this.loaded = false;
31910     this.loading = false;
31911     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31912     /**
31913     * @event beforeload
31914     * Fires before this node is loaded, return false to cancel
31915     * @param {Node} this This node
31916     */
31917     this.addEvents({'beforeload':true, 'load': true});
31918     /**
31919     * @event load
31920     * Fires when this node is loaded
31921     * @param {Node} this This node
31922     */
31923     /**
31924      * The loader used by this node (defaults to using the tree's defined loader)
31925      * @type TreeLoader
31926      * @property loader
31927      */
31928 };
31929 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31930     expand : function(deep, anim, callback){
31931         if(this.loading){ // if an async load is already running, waiting til it's done
31932             var timer;
31933             var f = function(){
31934                 if(!this.loading){ // done loading
31935                     clearInterval(timer);
31936                     this.expand(deep, anim, callback);
31937                 }
31938             }.createDelegate(this);
31939             timer = setInterval(f, 200);
31940             return;
31941         }
31942         if(!this.loaded){
31943             if(this.fireEvent("beforeload", this) === false){
31944                 return;
31945             }
31946             this.loading = true;
31947             this.ui.beforeLoad(this);
31948             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31949             if(loader){
31950                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31951                 return;
31952             }
31953         }
31954         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31955     },
31956     
31957     /**
31958      * Returns true if this node is currently loading
31959      * @return {Boolean}
31960      */
31961     isLoading : function(){
31962         return this.loading;  
31963     },
31964     
31965     loadComplete : function(deep, anim, callback){
31966         this.loading = false;
31967         this.loaded = true;
31968         this.ui.afterLoad(this);
31969         this.fireEvent("load", this);
31970         this.expand(deep, anim, callback);
31971     },
31972     
31973     /**
31974      * Returns true if this node has been loaded
31975      * @return {Boolean}
31976      */
31977     isLoaded : function(){
31978         return this.loaded;
31979     },
31980     
31981     hasChildNodes : function(){
31982         if(!this.isLeaf() && !this.loaded){
31983             return true;
31984         }else{
31985             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31986         }
31987     },
31988
31989     /**
31990      * Trigger a reload for this node
31991      * @param {Function} callback
31992      */
31993     reload : function(callback){
31994         this.collapse(false, false);
31995         while(this.firstChild){
31996             this.removeChild(this.firstChild);
31997         }
31998         this.childrenRendered = false;
31999         this.loaded = false;
32000         if(this.isHiddenRoot()){
32001             this.expanded = false;
32002         }
32003         this.expand(false, false, callback);
32004     }
32005 });/*
32006  * Based on:
32007  * Ext JS Library 1.1.1
32008  * Copyright(c) 2006-2007, Ext JS, LLC.
32009  *
32010  * Originally Released Under LGPL - original licence link has changed is not relivant.
32011  *
32012  * Fork - LGPL
32013  * <script type="text/javascript">
32014  */
32015  
32016 /**
32017  * @class Roo.tree.TreeNodeUI
32018  * @constructor
32019  * @param {Object} node The node to render
32020  * The TreeNode UI implementation is separate from the
32021  * tree implementation. Unless you are customizing the tree UI,
32022  * you should never have to use this directly.
32023  */
32024 Roo.tree.TreeNodeUI = function(node){
32025     this.node = node;
32026     this.rendered = false;
32027     this.animating = false;
32028     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32029 };
32030
32031 Roo.tree.TreeNodeUI.prototype = {
32032     removeChild : function(node){
32033         if(this.rendered){
32034             this.ctNode.removeChild(node.ui.getEl());
32035         }
32036     },
32037
32038     beforeLoad : function(){
32039          this.addClass("x-tree-node-loading");
32040     },
32041
32042     afterLoad : function(){
32043          this.removeClass("x-tree-node-loading");
32044     },
32045
32046     onTextChange : function(node, text, oldText){
32047         if(this.rendered){
32048             this.textNode.innerHTML = text;
32049         }
32050     },
32051
32052     onDisableChange : function(node, state){
32053         this.disabled = state;
32054         if(state){
32055             this.addClass("x-tree-node-disabled");
32056         }else{
32057             this.removeClass("x-tree-node-disabled");
32058         }
32059     },
32060
32061     onSelectedChange : function(state){
32062         if(state){
32063             this.focus();
32064             this.addClass("x-tree-selected");
32065         }else{
32066             //this.blur();
32067             this.removeClass("x-tree-selected");
32068         }
32069     },
32070
32071     onMove : function(tree, node, oldParent, newParent, index, refNode){
32072         this.childIndent = null;
32073         if(this.rendered){
32074             var targetNode = newParent.ui.getContainer();
32075             if(!targetNode){//target not rendered
32076                 this.holder = document.createElement("div");
32077                 this.holder.appendChild(this.wrap);
32078                 return;
32079             }
32080             var insertBefore = refNode ? refNode.ui.getEl() : null;
32081             if(insertBefore){
32082                 targetNode.insertBefore(this.wrap, insertBefore);
32083             }else{
32084                 targetNode.appendChild(this.wrap);
32085             }
32086             this.node.renderIndent(true);
32087         }
32088     },
32089
32090     addClass : function(cls){
32091         if(this.elNode){
32092             Roo.fly(this.elNode).addClass(cls);
32093         }
32094     },
32095
32096     removeClass : function(cls){
32097         if(this.elNode){
32098             Roo.fly(this.elNode).removeClass(cls);
32099         }
32100     },
32101
32102     remove : function(){
32103         if(this.rendered){
32104             this.holder = document.createElement("div");
32105             this.holder.appendChild(this.wrap);
32106         }
32107     },
32108
32109     fireEvent : function(){
32110         return this.node.fireEvent.apply(this.node, arguments);
32111     },
32112
32113     initEvents : function(){
32114         this.node.on("move", this.onMove, this);
32115         var E = Roo.EventManager;
32116         var a = this.anchor;
32117
32118         var el = Roo.fly(a, '_treeui');
32119
32120         if(Roo.isOpera){ // opera render bug ignores the CSS
32121             el.setStyle("text-decoration", "none");
32122         }
32123
32124         el.on("click", this.onClick, this);
32125         el.on("dblclick", this.onDblClick, this);
32126
32127         if(this.checkbox){
32128             Roo.EventManager.on(this.checkbox,
32129                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32130         }
32131
32132         el.on("contextmenu", this.onContextMenu, this);
32133
32134         var icon = Roo.fly(this.iconNode);
32135         icon.on("click", this.onClick, this);
32136         icon.on("dblclick", this.onDblClick, this);
32137         icon.on("contextmenu", this.onContextMenu, this);
32138         E.on(this.ecNode, "click", this.ecClick, this, true);
32139
32140         if(this.node.disabled){
32141             this.addClass("x-tree-node-disabled");
32142         }
32143         if(this.node.hidden){
32144             this.addClass("x-tree-node-disabled");
32145         }
32146         var ot = this.node.getOwnerTree();
32147         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32148         if(dd && (!this.node.isRoot || ot.rootVisible)){
32149             Roo.dd.Registry.register(this.elNode, {
32150                 node: this.node,
32151                 handles: this.getDDHandles(),
32152                 isHandle: false
32153             });
32154         }
32155     },
32156
32157     getDDHandles : function(){
32158         return [this.iconNode, this.textNode];
32159     },
32160
32161     hide : function(){
32162         if(this.rendered){
32163             this.wrap.style.display = "none";
32164         }
32165     },
32166
32167     show : function(){
32168         if(this.rendered){
32169             this.wrap.style.display = "";
32170         }
32171     },
32172
32173     onContextMenu : function(e){
32174         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32175             e.preventDefault();
32176             this.focus();
32177             this.fireEvent("contextmenu", this.node, e);
32178         }
32179     },
32180
32181     onClick : function(e){
32182         if(this.dropping){
32183             e.stopEvent();
32184             return;
32185         }
32186         if(this.fireEvent("beforeclick", this.node, e) !== false){
32187             if(!this.disabled && this.node.attributes.href){
32188                 this.fireEvent("click", this.node, e);
32189                 return;
32190             }
32191             e.preventDefault();
32192             if(this.disabled){
32193                 return;
32194             }
32195
32196             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32197                 this.node.toggle();
32198             }
32199
32200             this.fireEvent("click", this.node, e);
32201         }else{
32202             e.stopEvent();
32203         }
32204     },
32205
32206     onDblClick : function(e){
32207         e.preventDefault();
32208         if(this.disabled){
32209             return;
32210         }
32211         if(this.checkbox){
32212             this.toggleCheck();
32213         }
32214         if(!this.animating && this.node.hasChildNodes()){
32215             this.node.toggle();
32216         }
32217         this.fireEvent("dblclick", this.node, e);
32218     },
32219
32220     onCheckChange : function(){
32221         var checked = this.checkbox.checked;
32222         this.node.attributes.checked = checked;
32223         this.fireEvent('checkchange', this.node, checked);
32224     },
32225
32226     ecClick : function(e){
32227         if(!this.animating && this.node.hasChildNodes()){
32228             this.node.toggle();
32229         }
32230     },
32231
32232     startDrop : function(){
32233         this.dropping = true;
32234     },
32235
32236     // delayed drop so the click event doesn't get fired on a drop
32237     endDrop : function(){
32238        setTimeout(function(){
32239            this.dropping = false;
32240        }.createDelegate(this), 50);
32241     },
32242
32243     expand : function(){
32244         this.updateExpandIcon();
32245         this.ctNode.style.display = "";
32246     },
32247
32248     focus : function(){
32249         if(!this.node.preventHScroll){
32250             try{this.anchor.focus();
32251             }catch(e){}
32252         }else if(!Roo.isIE){
32253             try{
32254                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32255                 var l = noscroll.scrollLeft;
32256                 this.anchor.focus();
32257                 noscroll.scrollLeft = l;
32258             }catch(e){}
32259         }
32260     },
32261
32262     toggleCheck : function(value){
32263         var cb = this.checkbox;
32264         if(cb){
32265             cb.checked = (value === undefined ? !cb.checked : value);
32266         }
32267     },
32268
32269     blur : function(){
32270         try{
32271             this.anchor.blur();
32272         }catch(e){}
32273     },
32274
32275     animExpand : function(callback){
32276         var ct = Roo.get(this.ctNode);
32277         ct.stopFx();
32278         if(!this.node.hasChildNodes()){
32279             this.updateExpandIcon();
32280             this.ctNode.style.display = "";
32281             Roo.callback(callback);
32282             return;
32283         }
32284         this.animating = true;
32285         this.updateExpandIcon();
32286
32287         ct.slideIn('t', {
32288            callback : function(){
32289                this.animating = false;
32290                Roo.callback(callback);
32291             },
32292             scope: this,
32293             duration: this.node.ownerTree.duration || .25
32294         });
32295     },
32296
32297     highlight : function(){
32298         var tree = this.node.getOwnerTree();
32299         Roo.fly(this.wrap).highlight(
32300             tree.hlColor || "C3DAF9",
32301             {endColor: tree.hlBaseColor}
32302         );
32303     },
32304
32305     collapse : function(){
32306         this.updateExpandIcon();
32307         this.ctNode.style.display = "none";
32308     },
32309
32310     animCollapse : function(callback){
32311         var ct = Roo.get(this.ctNode);
32312         ct.enableDisplayMode('block');
32313         ct.stopFx();
32314
32315         this.animating = true;
32316         this.updateExpandIcon();
32317
32318         ct.slideOut('t', {
32319             callback : function(){
32320                this.animating = false;
32321                Roo.callback(callback);
32322             },
32323             scope: this,
32324             duration: this.node.ownerTree.duration || .25
32325         });
32326     },
32327
32328     getContainer : function(){
32329         return this.ctNode;
32330     },
32331
32332     getEl : function(){
32333         return this.wrap;
32334     },
32335
32336     appendDDGhost : function(ghostNode){
32337         ghostNode.appendChild(this.elNode.cloneNode(true));
32338     },
32339
32340     getDDRepairXY : function(){
32341         return Roo.lib.Dom.getXY(this.iconNode);
32342     },
32343
32344     onRender : function(){
32345         this.render();
32346     },
32347
32348     render : function(bulkRender){
32349         var n = this.node, a = n.attributes;
32350         var targetNode = n.parentNode ?
32351               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32352
32353         if(!this.rendered){
32354             this.rendered = true;
32355
32356             this.renderElements(n, a, targetNode, bulkRender);
32357
32358             if(a.qtip){
32359                if(this.textNode.setAttributeNS){
32360                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32361                    if(a.qtipTitle){
32362                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32363                    }
32364                }else{
32365                    this.textNode.setAttribute("ext:qtip", a.qtip);
32366                    if(a.qtipTitle){
32367                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32368                    }
32369                }
32370             }else if(a.qtipCfg){
32371                 a.qtipCfg.target = Roo.id(this.textNode);
32372                 Roo.QuickTips.register(a.qtipCfg);
32373             }
32374             this.initEvents();
32375             if(!this.node.expanded){
32376                 this.updateExpandIcon();
32377             }
32378         }else{
32379             if(bulkRender === true) {
32380                 targetNode.appendChild(this.wrap);
32381             }
32382         }
32383     },
32384
32385     renderElements : function(n, a, targetNode, bulkRender){
32386         // add some indent caching, this helps performance when rendering a large tree
32387         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32388         var t = n.getOwnerTree();
32389         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32390         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32391         var cb = typeof a.checked == 'boolean';
32392         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32393         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32394             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32395             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32396             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32397             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32398             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32399              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32400                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32401             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32402             "</li>"];
32403
32404         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32405             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32406                                 n.nextSibling.ui.getEl(), buf.join(""));
32407         }else{
32408             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32409         }
32410
32411         this.elNode = this.wrap.childNodes[0];
32412         this.ctNode = this.wrap.childNodes[1];
32413         var cs = this.elNode.childNodes;
32414         this.indentNode = cs[0];
32415         this.ecNode = cs[1];
32416         this.iconNode = cs[2];
32417         var index = 3;
32418         if(cb){
32419             this.checkbox = cs[3];
32420             index++;
32421         }
32422         this.anchor = cs[index];
32423         this.textNode = cs[index].firstChild;
32424     },
32425
32426     getAnchor : function(){
32427         return this.anchor;
32428     },
32429
32430     getTextEl : function(){
32431         return this.textNode;
32432     },
32433
32434     getIconEl : function(){
32435         return this.iconNode;
32436     },
32437
32438     isChecked : function(){
32439         return this.checkbox ? this.checkbox.checked : false;
32440     },
32441
32442     updateExpandIcon : function(){
32443         if(this.rendered){
32444             var n = this.node, c1, c2;
32445             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32446             var hasChild = n.hasChildNodes();
32447             if(hasChild){
32448                 if(n.expanded){
32449                     cls += "-minus";
32450                     c1 = "x-tree-node-collapsed";
32451                     c2 = "x-tree-node-expanded";
32452                 }else{
32453                     cls += "-plus";
32454                     c1 = "x-tree-node-expanded";
32455                     c2 = "x-tree-node-collapsed";
32456                 }
32457                 if(this.wasLeaf){
32458                     this.removeClass("x-tree-node-leaf");
32459                     this.wasLeaf = false;
32460                 }
32461                 if(this.c1 != c1 || this.c2 != c2){
32462                     Roo.fly(this.elNode).replaceClass(c1, c2);
32463                     this.c1 = c1; this.c2 = c2;
32464                 }
32465             }else{
32466                 if(!this.wasLeaf){
32467                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32468                     delete this.c1;
32469                     delete this.c2;
32470                     this.wasLeaf = true;
32471                 }
32472             }
32473             var ecc = "x-tree-ec-icon "+cls;
32474             if(this.ecc != ecc){
32475                 this.ecNode.className = ecc;
32476                 this.ecc = ecc;
32477             }
32478         }
32479     },
32480
32481     getChildIndent : function(){
32482         if(!this.childIndent){
32483             var buf = [];
32484             var p = this.node;
32485             while(p){
32486                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32487                     if(!p.isLast()) {
32488                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32489                     } else {
32490                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32491                     }
32492                 }
32493                 p = p.parentNode;
32494             }
32495             this.childIndent = buf.join("");
32496         }
32497         return this.childIndent;
32498     },
32499
32500     renderIndent : function(){
32501         if(this.rendered){
32502             var indent = "";
32503             var p = this.node.parentNode;
32504             if(p){
32505                 indent = p.ui.getChildIndent();
32506             }
32507             if(this.indentMarkup != indent){ // don't rerender if not required
32508                 this.indentNode.innerHTML = indent;
32509                 this.indentMarkup = indent;
32510             }
32511             this.updateExpandIcon();
32512         }
32513     }
32514 };
32515
32516 Roo.tree.RootTreeNodeUI = function(){
32517     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32518 };
32519 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32520     render : function(){
32521         if(!this.rendered){
32522             var targetNode = this.node.ownerTree.innerCt.dom;
32523             this.node.expanded = true;
32524             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32525             this.wrap = this.ctNode = targetNode.firstChild;
32526         }
32527     },
32528     collapse : function(){
32529     },
32530     expand : function(){
32531     }
32532 });/*
32533  * Based on:
32534  * Ext JS Library 1.1.1
32535  * Copyright(c) 2006-2007, Ext JS, LLC.
32536  *
32537  * Originally Released Under LGPL - original licence link has changed is not relivant.
32538  *
32539  * Fork - LGPL
32540  * <script type="text/javascript">
32541  */
32542 /**
32543  * @class Roo.tree.TreeLoader
32544  * @extends Roo.util.Observable
32545  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32546  * nodes from a specified URL. The response must be a javascript Array definition
32547  * who's elements are node definition objects. eg:
32548  * <pre><code>
32549    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32550     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32551 </code></pre>
32552  * <br><br>
32553  * A server request is sent, and child nodes are loaded only when a node is expanded.
32554  * The loading node's id is passed to the server under the parameter name "node" to
32555  * enable the server to produce the correct child nodes.
32556  * <br><br>
32557  * To pass extra parameters, an event handler may be attached to the "beforeload"
32558  * event, and the parameters specified in the TreeLoader's baseParams property:
32559  * <pre><code>
32560     myTreeLoader.on("beforeload", function(treeLoader, node) {
32561         this.baseParams.category = node.attributes.category;
32562     }, this);
32563 </code></pre><
32564  * This would pass an HTTP parameter called "category" to the server containing
32565  * the value of the Node's "category" attribute.
32566  * @constructor
32567  * Creates a new Treeloader.
32568  * @param {Object} config A config object containing config properties.
32569  */
32570 Roo.tree.TreeLoader = function(config){
32571     this.baseParams = {};
32572     this.requestMethod = "POST";
32573     Roo.apply(this, config);
32574
32575     this.addEvents({
32576     
32577         /**
32578          * @event beforeload
32579          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32580          * @param {Object} This TreeLoader object.
32581          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32582          * @param {Object} callback The callback function specified in the {@link #load} call.
32583          */
32584         beforeload : true,
32585         /**
32586          * @event load
32587          * Fires when the node has been successfuly loaded.
32588          * @param {Object} This TreeLoader object.
32589          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32590          * @param {Object} response The response object containing the data from the server.
32591          */
32592         load : true,
32593         /**
32594          * @event loadexception
32595          * Fires if the network request failed.
32596          * @param {Object} This TreeLoader object.
32597          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32598          * @param {Object} response The response object containing the data from the server.
32599          */
32600         loadexception : true,
32601         /**
32602          * @event create
32603          * Fires before a node is created, enabling you to return custom Node types 
32604          * @param {Object} This TreeLoader object.
32605          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32606          */
32607         create : true
32608     });
32609
32610     Roo.tree.TreeLoader.superclass.constructor.call(this);
32611 };
32612
32613 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32614     /**
32615     * @cfg {String} dataUrl The URL from which to request a Json string which
32616     * specifies an array of node definition object representing the child nodes
32617     * to be loaded.
32618     */
32619     /**
32620     * @cfg {Object} baseParams (optional) An object containing properties which
32621     * specify HTTP parameters to be passed to each request for child nodes.
32622     */
32623     /**
32624     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32625     * created by this loader. If the attributes sent by the server have an attribute in this object,
32626     * they take priority.
32627     */
32628     /**
32629     * @cfg {Object} uiProviders (optional) An object containing properties which
32630     * 
32631     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32632     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32633     * <i>uiProvider</i> attribute of a returned child node is a string rather
32634     * than a reference to a TreeNodeUI implementation, this that string value
32635     * is used as a property name in the uiProviders object. You can define the provider named
32636     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32637     */
32638     uiProviders : {},
32639
32640     /**
32641     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32642     * child nodes before loading.
32643     */
32644     clearOnLoad : true,
32645
32646     /**
32647     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32648     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32649     * Grid query { data : [ .....] }
32650     */
32651     
32652     root : false,
32653      /**
32654     * @cfg {String} queryParam (optional) 
32655     * Name of the query as it will be passed on the querystring (defaults to 'node')
32656     * eg. the request will be ?node=[id]
32657     */
32658     
32659     
32660     queryParam: false,
32661     
32662     /**
32663      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32664      * This is called automatically when a node is expanded, but may be used to reload
32665      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32666      * @param {Roo.tree.TreeNode} node
32667      * @param {Function} callback
32668      */
32669     load : function(node, callback){
32670         if(this.clearOnLoad){
32671             while(node.firstChild){
32672                 node.removeChild(node.firstChild);
32673             }
32674         }
32675         if(node.attributes.children){ // preloaded json children
32676             var cs = node.attributes.children;
32677             for(var i = 0, len = cs.length; i < len; i++){
32678                 node.appendChild(this.createNode(cs[i]));
32679             }
32680             if(typeof callback == "function"){
32681                 callback();
32682             }
32683         }else if(this.dataUrl){
32684             this.requestData(node, callback);
32685         }
32686     },
32687
32688     getParams: function(node){
32689         var buf = [], bp = this.baseParams;
32690         for(var key in bp){
32691             if(typeof bp[key] != "function"){
32692                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32693             }
32694         }
32695         var n = this.queryParam === false ? 'node' : this.queryParam;
32696         buf.push(n + "=", encodeURIComponent(node.id));
32697         return buf.join("");
32698     },
32699
32700     requestData : function(node, callback){
32701         if(this.fireEvent("beforeload", this, node, callback) !== false){
32702             this.transId = Roo.Ajax.request({
32703                 method:this.requestMethod,
32704                 url: this.dataUrl||this.url,
32705                 success: this.handleResponse,
32706                 failure: this.handleFailure,
32707                 scope: this,
32708                 argument: {callback: callback, node: node},
32709                 params: this.getParams(node)
32710             });
32711         }else{
32712             // if the load is cancelled, make sure we notify
32713             // the node that we are done
32714             if(typeof callback == "function"){
32715                 callback();
32716             }
32717         }
32718     },
32719
32720     isLoading : function(){
32721         return this.transId ? true : false;
32722     },
32723
32724     abort : function(){
32725         if(this.isLoading()){
32726             Roo.Ajax.abort(this.transId);
32727         }
32728     },
32729
32730     // private
32731     createNode : function(attr){
32732         // apply baseAttrs, nice idea Corey!
32733         if(this.baseAttrs){
32734             Roo.applyIf(attr, this.baseAttrs);
32735         }
32736         if(this.applyLoader !== false){
32737             attr.loader = this;
32738         }
32739         // uiProvider = depreciated..
32740         
32741         if(typeof(attr.uiProvider) == 'string'){
32742            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32743                 /**  eval:var:attr */ eval(attr.uiProvider);
32744         }
32745         if(typeof(this.uiProviders['default']) != 'undefined') {
32746             attr.uiProvider = this.uiProviders['default'];
32747         }
32748         
32749         this.fireEvent('create', this, attr);
32750         
32751         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32752         return(attr.leaf ?
32753                         new Roo.tree.TreeNode(attr) :
32754                         new Roo.tree.AsyncTreeNode(attr));
32755     },
32756
32757     processResponse : function(response, node, callback){
32758         var json = response.responseText;
32759         try {
32760             
32761             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32762             if (this.root !== false) {
32763                 o = o[this.root];
32764             }
32765             
32766             for(var i = 0, len = o.length; i < len; i++){
32767                 var n = this.createNode(o[i]);
32768                 if(n){
32769                     node.appendChild(n);
32770                 }
32771             }
32772             if(typeof callback == "function"){
32773                 callback(this, node);
32774             }
32775         }catch(e){
32776             this.handleFailure(response);
32777         }
32778     },
32779
32780     handleResponse : function(response){
32781         this.transId = false;
32782         var a = response.argument;
32783         this.processResponse(response, a.node, a.callback);
32784         this.fireEvent("load", this, a.node, response);
32785     },
32786
32787     handleFailure : function(response){
32788         this.transId = false;
32789         var a = response.argument;
32790         this.fireEvent("loadexception", this, a.node, response);
32791         if(typeof a.callback == "function"){
32792             a.callback(this, a.node);
32793         }
32794     }
32795 });/*
32796  * Based on:
32797  * Ext JS Library 1.1.1
32798  * Copyright(c) 2006-2007, Ext JS, LLC.
32799  *
32800  * Originally Released Under LGPL - original licence link has changed is not relivant.
32801  *
32802  * Fork - LGPL
32803  * <script type="text/javascript">
32804  */
32805
32806 /**
32807 * @class Roo.tree.TreeFilter
32808 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32809 * @param {TreePanel} tree
32810 * @param {Object} config (optional)
32811  */
32812 Roo.tree.TreeFilter = function(tree, config){
32813     this.tree = tree;
32814     this.filtered = {};
32815     Roo.apply(this, config);
32816 };
32817
32818 Roo.tree.TreeFilter.prototype = {
32819     clearBlank:false,
32820     reverse:false,
32821     autoClear:false,
32822     remove:false,
32823
32824      /**
32825      * Filter the data by a specific attribute.
32826      * @param {String/RegExp} value Either string that the attribute value
32827      * should start with or a RegExp to test against the attribute
32828      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32829      * @param {TreeNode} startNode (optional) The node to start the filter at.
32830      */
32831     filter : function(value, attr, startNode){
32832         attr = attr || "text";
32833         var f;
32834         if(typeof value == "string"){
32835             var vlen = value.length;
32836             // auto clear empty filter
32837             if(vlen == 0 && this.clearBlank){
32838                 this.clear();
32839                 return;
32840             }
32841             value = value.toLowerCase();
32842             f = function(n){
32843                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32844             };
32845         }else if(value.exec){ // regex?
32846             f = function(n){
32847                 return value.test(n.attributes[attr]);
32848             };
32849         }else{
32850             throw 'Illegal filter type, must be string or regex';
32851         }
32852         this.filterBy(f, null, startNode);
32853         },
32854
32855     /**
32856      * Filter by a function. The passed function will be called with each
32857      * node in the tree (or from the startNode). If the function returns true, the node is kept
32858      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32859      * @param {Function} fn The filter function
32860      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32861      */
32862     filterBy : function(fn, scope, startNode){
32863         startNode = startNode || this.tree.root;
32864         if(this.autoClear){
32865             this.clear();
32866         }
32867         var af = this.filtered, rv = this.reverse;
32868         var f = function(n){
32869             if(n == startNode){
32870                 return true;
32871             }
32872             if(af[n.id]){
32873                 return false;
32874             }
32875             var m = fn.call(scope || n, n);
32876             if(!m || rv){
32877                 af[n.id] = n;
32878                 n.ui.hide();
32879                 return false;
32880             }
32881             return true;
32882         };
32883         startNode.cascade(f);
32884         if(this.remove){
32885            for(var id in af){
32886                if(typeof id != "function"){
32887                    var n = af[id];
32888                    if(n && n.parentNode){
32889                        n.parentNode.removeChild(n);
32890                    }
32891                }
32892            }
32893         }
32894     },
32895
32896     /**
32897      * Clears the current filter. Note: with the "remove" option
32898      * set a filter cannot be cleared.
32899      */
32900     clear : function(){
32901         var t = this.tree;
32902         var af = this.filtered;
32903         for(var id in af){
32904             if(typeof id != "function"){
32905                 var n = af[id];
32906                 if(n){
32907                     n.ui.show();
32908                 }
32909             }
32910         }
32911         this.filtered = {};
32912     }
32913 };
32914 /*
32915  * Based on:
32916  * Ext JS Library 1.1.1
32917  * Copyright(c) 2006-2007, Ext JS, LLC.
32918  *
32919  * Originally Released Under LGPL - original licence link has changed is not relivant.
32920  *
32921  * Fork - LGPL
32922  * <script type="text/javascript">
32923  */
32924  
32925
32926 /**
32927  * @class Roo.tree.TreeSorter
32928  * Provides sorting of nodes in a TreePanel
32929  * 
32930  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32931  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32932  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32933  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32934  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32935  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32936  * @constructor
32937  * @param {TreePanel} tree
32938  * @param {Object} config
32939  */
32940 Roo.tree.TreeSorter = function(tree, config){
32941     Roo.apply(this, config);
32942     tree.on("beforechildrenrendered", this.doSort, this);
32943     tree.on("append", this.updateSort, this);
32944     tree.on("insert", this.updateSort, this);
32945     
32946     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32947     var p = this.property || "text";
32948     var sortType = this.sortType;
32949     var fs = this.folderSort;
32950     var cs = this.caseSensitive === true;
32951     var leafAttr = this.leafAttr || 'leaf';
32952
32953     this.sortFn = function(n1, n2){
32954         if(fs){
32955             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32956                 return 1;
32957             }
32958             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32959                 return -1;
32960             }
32961         }
32962         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32963         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32964         if(v1 < v2){
32965                         return dsc ? +1 : -1;
32966                 }else if(v1 > v2){
32967                         return dsc ? -1 : +1;
32968         }else{
32969                 return 0;
32970         }
32971     };
32972 };
32973
32974 Roo.tree.TreeSorter.prototype = {
32975     doSort : function(node){
32976         node.sort(this.sortFn);
32977     },
32978     
32979     compareNodes : function(n1, n2){
32980         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32981     },
32982     
32983     updateSort : function(tree, node){
32984         if(node.childrenRendered){
32985             this.doSort.defer(1, this, [node]);
32986         }
32987     }
32988 };/*
32989  * Based on:
32990  * Ext JS Library 1.1.1
32991  * Copyright(c) 2006-2007, Ext JS, LLC.
32992  *
32993  * Originally Released Under LGPL - original licence link has changed is not relivant.
32994  *
32995  * Fork - LGPL
32996  * <script type="text/javascript">
32997  */
32998
32999 if(Roo.dd.DropZone){
33000     
33001 Roo.tree.TreeDropZone = function(tree, config){
33002     this.allowParentInsert = false;
33003     this.allowContainerDrop = false;
33004     this.appendOnly = false;
33005     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33006     this.tree = tree;
33007     this.lastInsertClass = "x-tree-no-status";
33008     this.dragOverData = {};
33009 };
33010
33011 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33012     ddGroup : "TreeDD",
33013     
33014     expandDelay : 1000,
33015     
33016     expandNode : function(node){
33017         if(node.hasChildNodes() && !node.isExpanded()){
33018             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33019         }
33020     },
33021     
33022     queueExpand : function(node){
33023         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33024     },
33025     
33026     cancelExpand : function(){
33027         if(this.expandProcId){
33028             clearTimeout(this.expandProcId);
33029             this.expandProcId = false;
33030         }
33031     },
33032     
33033     isValidDropPoint : function(n, pt, dd, e, data){
33034         if(!n || !data){ return false; }
33035         var targetNode = n.node;
33036         var dropNode = data.node;
33037         // default drop rules
33038         if(!(targetNode && targetNode.isTarget && pt)){
33039             return false;
33040         }
33041         if(pt == "append" && targetNode.allowChildren === false){
33042             return false;
33043         }
33044         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33045             return false;
33046         }
33047         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33048             return false;
33049         }
33050         // reuse the object
33051         var overEvent = this.dragOverData;
33052         overEvent.tree = this.tree;
33053         overEvent.target = targetNode;
33054         overEvent.data = data;
33055         overEvent.point = pt;
33056         overEvent.source = dd;
33057         overEvent.rawEvent = e;
33058         overEvent.dropNode = dropNode;
33059         overEvent.cancel = false;  
33060         var result = this.tree.fireEvent("nodedragover", overEvent);
33061         return overEvent.cancel === false && result !== false;
33062     },
33063     
33064     getDropPoint : function(e, n, dd){
33065         var tn = n.node;
33066         if(tn.isRoot){
33067             return tn.allowChildren !== false ? "append" : false; // always append for root
33068         }
33069         var dragEl = n.ddel;
33070         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33071         var y = Roo.lib.Event.getPageY(e);
33072         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33073         
33074         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33075         var noAppend = tn.allowChildren === false;
33076         if(this.appendOnly || tn.parentNode.allowChildren === false){
33077             return noAppend ? false : "append";
33078         }
33079         var noBelow = false;
33080         if(!this.allowParentInsert){
33081             noBelow = tn.hasChildNodes() && tn.isExpanded();
33082         }
33083         var q = (b - t) / (noAppend ? 2 : 3);
33084         if(y >= t && y < (t + q)){
33085             return "above";
33086         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33087             return "below";
33088         }else{
33089             return "append";
33090         }
33091     },
33092     
33093     onNodeEnter : function(n, dd, e, data){
33094         this.cancelExpand();
33095     },
33096     
33097     onNodeOver : function(n, dd, e, data){
33098         var pt = this.getDropPoint(e, n, dd);
33099         var node = n.node;
33100         
33101         // auto node expand check
33102         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33103             this.queueExpand(node);
33104         }else if(pt != "append"){
33105             this.cancelExpand();
33106         }
33107         
33108         // set the insert point style on the target node
33109         var returnCls = this.dropNotAllowed;
33110         if(this.isValidDropPoint(n, pt, dd, e, data)){
33111            if(pt){
33112                var el = n.ddel;
33113                var cls;
33114                if(pt == "above"){
33115                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33116                    cls = "x-tree-drag-insert-above";
33117                }else if(pt == "below"){
33118                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33119                    cls = "x-tree-drag-insert-below";
33120                }else{
33121                    returnCls = "x-tree-drop-ok-append";
33122                    cls = "x-tree-drag-append";
33123                }
33124                if(this.lastInsertClass != cls){
33125                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33126                    this.lastInsertClass = cls;
33127                }
33128            }
33129        }
33130        return returnCls;
33131     },
33132     
33133     onNodeOut : function(n, dd, e, data){
33134         this.cancelExpand();
33135         this.removeDropIndicators(n);
33136     },
33137     
33138     onNodeDrop : function(n, dd, e, data){
33139         var point = this.getDropPoint(e, n, dd);
33140         var targetNode = n.node;
33141         targetNode.ui.startDrop();
33142         if(!this.isValidDropPoint(n, point, dd, e, data)){
33143             targetNode.ui.endDrop();
33144             return false;
33145         }
33146         // first try to find the drop node
33147         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33148         var dropEvent = {
33149             tree : this.tree,
33150             target: targetNode,
33151             data: data,
33152             point: point,
33153             source: dd,
33154             rawEvent: e,
33155             dropNode: dropNode,
33156             cancel: !dropNode   
33157         };
33158         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33159         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33160             targetNode.ui.endDrop();
33161             return false;
33162         }
33163         // allow target changing
33164         targetNode = dropEvent.target;
33165         if(point == "append" && !targetNode.isExpanded()){
33166             targetNode.expand(false, null, function(){
33167                 this.completeDrop(dropEvent);
33168             }.createDelegate(this));
33169         }else{
33170             this.completeDrop(dropEvent);
33171         }
33172         return true;
33173     },
33174     
33175     completeDrop : function(de){
33176         var ns = de.dropNode, p = de.point, t = de.target;
33177         if(!(ns instanceof Array)){
33178             ns = [ns];
33179         }
33180         var n;
33181         for(var i = 0, len = ns.length; i < len; i++){
33182             n = ns[i];
33183             if(p == "above"){
33184                 t.parentNode.insertBefore(n, t);
33185             }else if(p == "below"){
33186                 t.parentNode.insertBefore(n, t.nextSibling);
33187             }else{
33188                 t.appendChild(n);
33189             }
33190         }
33191         n.ui.focus();
33192         if(this.tree.hlDrop){
33193             n.ui.highlight();
33194         }
33195         t.ui.endDrop();
33196         this.tree.fireEvent("nodedrop", de);
33197     },
33198     
33199     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33200         if(this.tree.hlDrop){
33201             dropNode.ui.focus();
33202             dropNode.ui.highlight();
33203         }
33204         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33205     },
33206     
33207     getTree : function(){
33208         return this.tree;
33209     },
33210     
33211     removeDropIndicators : function(n){
33212         if(n && n.ddel){
33213             var el = n.ddel;
33214             Roo.fly(el).removeClass([
33215                     "x-tree-drag-insert-above",
33216                     "x-tree-drag-insert-below",
33217                     "x-tree-drag-append"]);
33218             this.lastInsertClass = "_noclass";
33219         }
33220     },
33221     
33222     beforeDragDrop : function(target, e, id){
33223         this.cancelExpand();
33224         return true;
33225     },
33226     
33227     afterRepair : function(data){
33228         if(data && Roo.enableFx){
33229             data.node.ui.highlight();
33230         }
33231         this.hideProxy();
33232     }    
33233 });
33234
33235 }
33236 /*
33237  * Based on:
33238  * Ext JS Library 1.1.1
33239  * Copyright(c) 2006-2007, Ext JS, LLC.
33240  *
33241  * Originally Released Under LGPL - original licence link has changed is not relivant.
33242  *
33243  * Fork - LGPL
33244  * <script type="text/javascript">
33245  */
33246  
33247
33248 if(Roo.dd.DragZone){
33249 Roo.tree.TreeDragZone = function(tree, config){
33250     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33251     this.tree = tree;
33252 };
33253
33254 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33255     ddGroup : "TreeDD",
33256     
33257     onBeforeDrag : function(data, e){
33258         var n = data.node;
33259         return n && n.draggable && !n.disabled;
33260     },
33261     
33262     onInitDrag : function(e){
33263         var data = this.dragData;
33264         this.tree.getSelectionModel().select(data.node);
33265         this.proxy.update("");
33266         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33267         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33268     },
33269     
33270     getRepairXY : function(e, data){
33271         return data.node.ui.getDDRepairXY();
33272     },
33273     
33274     onEndDrag : function(data, e){
33275         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33276     },
33277     
33278     onValidDrop : function(dd, e, id){
33279         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33280         this.hideProxy();
33281     },
33282     
33283     beforeInvalidDrop : function(e, id){
33284         // this scrolls the original position back into view
33285         var sm = this.tree.getSelectionModel();
33286         sm.clearSelections();
33287         sm.select(this.dragData.node);
33288     }
33289 });
33290 }/*
33291  * Based on:
33292  * Ext JS Library 1.1.1
33293  * Copyright(c) 2006-2007, Ext JS, LLC.
33294  *
33295  * Originally Released Under LGPL - original licence link has changed is not relivant.
33296  *
33297  * Fork - LGPL
33298  * <script type="text/javascript">
33299  */
33300 /**
33301  * @class Roo.tree.TreeEditor
33302  * @extends Roo.Editor
33303  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33304  * as the editor field.
33305  * @constructor
33306  * @param {TreePanel} tree
33307  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33308  */
33309 Roo.tree.TreeEditor = function(tree, config){
33310     config = config || {};
33311     var field = config.events ? config : new Roo.form.TextField(config);
33312     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33313
33314     this.tree = tree;
33315
33316     tree.on('beforeclick', this.beforeNodeClick, this);
33317     tree.getTreeEl().on('mousedown', this.hide, this);
33318     this.on('complete', this.updateNode, this);
33319     this.on('beforestartedit', this.fitToTree, this);
33320     this.on('startedit', this.bindScroll, this, {delay:10});
33321     this.on('specialkey', this.onSpecialKey, this);
33322 };
33323
33324 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33325     /**
33326      * @cfg {String} alignment
33327      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33328      */
33329     alignment: "l-l",
33330     // inherit
33331     autoSize: false,
33332     /**
33333      * @cfg {Boolean} hideEl
33334      * True to hide the bound element while the editor is displayed (defaults to false)
33335      */
33336     hideEl : false,
33337     /**
33338      * @cfg {String} cls
33339      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33340      */
33341     cls: "x-small-editor x-tree-editor",
33342     /**
33343      * @cfg {Boolean} shim
33344      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33345      */
33346     shim:false,
33347     // inherit
33348     shadow:"frame",
33349     /**
33350      * @cfg {Number} maxWidth
33351      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33352      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33353      * scroll and client offsets into account prior to each edit.
33354      */
33355     maxWidth: 250,
33356
33357     editDelay : 350,
33358
33359     // private
33360     fitToTree : function(ed, el){
33361         var td = this.tree.getTreeEl().dom, nd = el.dom;
33362         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33363             td.scrollLeft = nd.offsetLeft;
33364         }
33365         var w = Math.min(
33366                 this.maxWidth,
33367                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33368         this.setSize(w, '');
33369     },
33370
33371     // private
33372     triggerEdit : function(node){
33373         this.completeEdit();
33374         this.editNode = node;
33375         this.startEdit(node.ui.textNode, node.text);
33376     },
33377
33378     // private
33379     bindScroll : function(){
33380         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33381     },
33382
33383     // private
33384     beforeNodeClick : function(node, e){
33385         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33386         this.lastClick = new Date();
33387         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33388             e.stopEvent();
33389             this.triggerEdit(node);
33390             return false;
33391         }
33392     },
33393
33394     // private
33395     updateNode : function(ed, value){
33396         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33397         this.editNode.setText(value);
33398     },
33399
33400     // private
33401     onHide : function(){
33402         Roo.tree.TreeEditor.superclass.onHide.call(this);
33403         if(this.editNode){
33404             this.editNode.ui.focus();
33405         }
33406     },
33407
33408     // private
33409     onSpecialKey : function(field, e){
33410         var k = e.getKey();
33411         if(k == e.ESC){
33412             e.stopEvent();
33413             this.cancelEdit();
33414         }else if(k == e.ENTER && !e.hasModifier()){
33415             e.stopEvent();
33416             this.completeEdit();
33417         }
33418     }
33419 });//<Script type="text/javascript">
33420 /*
33421  * Based on:
33422  * Ext JS Library 1.1.1
33423  * Copyright(c) 2006-2007, Ext JS, LLC.
33424  *
33425  * Originally Released Under LGPL - original licence link has changed is not relivant.
33426  *
33427  * Fork - LGPL
33428  * <script type="text/javascript">
33429  */
33430  
33431 /**
33432  * Not documented??? - probably should be...
33433  */
33434
33435 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33436     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33437     
33438     renderElements : function(n, a, targetNode, bulkRender){
33439         //consel.log("renderElements?");
33440         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33441
33442         var t = n.getOwnerTree();
33443         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33444         
33445         var cols = t.columns;
33446         var bw = t.borderWidth;
33447         var c = cols[0];
33448         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33449          var cb = typeof a.checked == "boolean";
33450         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33451         var colcls = 'x-t-' + tid + '-c0';
33452         var buf = [
33453             '<li class="x-tree-node">',
33454             
33455                 
33456                 '<div class="x-tree-node-el ', a.cls,'">',
33457                     // extran...
33458                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33459                 
33460                 
33461                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33462                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33463                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33464                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33465                            (a.iconCls ? ' '+a.iconCls : ''),
33466                            '" unselectable="on" />',
33467                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33468                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33469                              
33470                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33471                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33472                             '<span unselectable="on" qtip="' + tx + '">',
33473                              tx,
33474                              '</span></a>' ,
33475                     '</div>',
33476                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33477                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33478                  ];
33479         for(var i = 1, len = cols.length; i < len; i++){
33480             c = cols[i];
33481             colcls = 'x-t-' + tid + '-c' +i;
33482             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33483             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33484                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33485                       "</div>");
33486          }
33487          
33488          buf.push(
33489             '</a>',
33490             '<div class="x-clear"></div></div>',
33491             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33492             "</li>");
33493         
33494         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33495             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33496                                 n.nextSibling.ui.getEl(), buf.join(""));
33497         }else{
33498             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33499         }
33500         var el = this.wrap.firstChild;
33501         this.elRow = el;
33502         this.elNode = el.firstChild;
33503         this.ranchor = el.childNodes[1];
33504         this.ctNode = this.wrap.childNodes[1];
33505         var cs = el.firstChild.childNodes;
33506         this.indentNode = cs[0];
33507         this.ecNode = cs[1];
33508         this.iconNode = cs[2];
33509         var index = 3;
33510         if(cb){
33511             this.checkbox = cs[3];
33512             index++;
33513         }
33514         this.anchor = cs[index];
33515         
33516         this.textNode = cs[index].firstChild;
33517         
33518         //el.on("click", this.onClick, this);
33519         //el.on("dblclick", this.onDblClick, this);
33520         
33521         
33522        // console.log(this);
33523     },
33524     initEvents : function(){
33525         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33526         
33527             
33528         var a = this.ranchor;
33529
33530         var el = Roo.get(a);
33531
33532         if(Roo.isOpera){ // opera render bug ignores the CSS
33533             el.setStyle("text-decoration", "none");
33534         }
33535
33536         el.on("click", this.onClick, this);
33537         el.on("dblclick", this.onDblClick, this);
33538         el.on("contextmenu", this.onContextMenu, this);
33539         
33540     },
33541     
33542     /*onSelectedChange : function(state){
33543         if(state){
33544             this.focus();
33545             this.addClass("x-tree-selected");
33546         }else{
33547             //this.blur();
33548             this.removeClass("x-tree-selected");
33549         }
33550     },*/
33551     addClass : function(cls){
33552         if(this.elRow){
33553             Roo.fly(this.elRow).addClass(cls);
33554         }
33555         
33556     },
33557     
33558     
33559     removeClass : function(cls){
33560         if(this.elRow){
33561             Roo.fly(this.elRow).removeClass(cls);
33562         }
33563     }
33564
33565     
33566     
33567 });//<Script type="text/javascript">
33568
33569 /*
33570  * Based on:
33571  * Ext JS Library 1.1.1
33572  * Copyright(c) 2006-2007, Ext JS, LLC.
33573  *
33574  * Originally Released Under LGPL - original licence link has changed is not relivant.
33575  *
33576  * Fork - LGPL
33577  * <script type="text/javascript">
33578  */
33579  
33580
33581 /**
33582  * @class Roo.tree.ColumnTree
33583  * @extends Roo.data.TreePanel
33584  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33585  * @cfg {int} borderWidth  compined right/left border allowance
33586  * @constructor
33587  * @param {String/HTMLElement/Element} el The container element
33588  * @param {Object} config
33589  */
33590 Roo.tree.ColumnTree =  function(el, config)
33591 {
33592    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33593    this.addEvents({
33594         /**
33595         * @event resize
33596         * Fire this event on a container when it resizes
33597         * @param {int} w Width
33598         * @param {int} h Height
33599         */
33600        "resize" : true
33601     });
33602     this.on('resize', this.onResize, this);
33603 };
33604
33605 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33606     //lines:false,
33607     
33608     
33609     borderWidth: Roo.isBorderBox ? 0 : 2, 
33610     headEls : false,
33611     
33612     render : function(){
33613         // add the header.....
33614        
33615         Roo.tree.ColumnTree.superclass.render.apply(this);
33616         
33617         this.el.addClass('x-column-tree');
33618         
33619         this.headers = this.el.createChild(
33620             {cls:'x-tree-headers'},this.innerCt.dom);
33621    
33622         var cols = this.columns, c;
33623         var totalWidth = 0;
33624         this.headEls = [];
33625         var  len = cols.length;
33626         for(var i = 0; i < len; i++){
33627              c = cols[i];
33628              totalWidth += c.width;
33629             this.headEls.push(this.headers.createChild({
33630                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33631                  cn: {
33632                      cls:'x-tree-hd-text',
33633                      html: c.header
33634                  },
33635                  style:'width:'+(c.width-this.borderWidth)+'px;'
33636              }));
33637         }
33638         this.headers.createChild({cls:'x-clear'});
33639         // prevent floats from wrapping when clipped
33640         this.headers.setWidth(totalWidth);
33641         //this.innerCt.setWidth(totalWidth);
33642         this.innerCt.setStyle({ overflow: 'auto' });
33643         this.onResize(this.width, this.height);
33644              
33645         
33646     },
33647     onResize : function(w,h)
33648     {
33649         this.height = h;
33650         this.width = w;
33651         // resize cols..
33652         this.innerCt.setWidth(this.width);
33653         this.innerCt.setHeight(this.height-20);
33654         
33655         // headers...
33656         var cols = this.columns, c;
33657         var totalWidth = 0;
33658         var expEl = false;
33659         var len = cols.length;
33660         for(var i = 0; i < len; i++){
33661             c = cols[i];
33662             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33663                 // it's the expander..
33664                 expEl  = this.headEls[i];
33665                 continue;
33666             }
33667             totalWidth += c.width;
33668             
33669         }
33670         if (expEl) {
33671             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33672         }
33673         this.headers.setWidth(w-20);
33674
33675         
33676         
33677         
33678     }
33679 });
33680 /*
33681  * Based on:
33682  * Ext JS Library 1.1.1
33683  * Copyright(c) 2006-2007, Ext JS, LLC.
33684  *
33685  * Originally Released Under LGPL - original licence link has changed is not relivant.
33686  *
33687  * Fork - LGPL
33688  * <script type="text/javascript">
33689  */
33690  
33691 /**
33692  * @class Roo.menu.Menu
33693  * @extends Roo.util.Observable
33694  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33695  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33696  * @constructor
33697  * Creates a new Menu
33698  * @param {Object} config Configuration options
33699  */
33700 Roo.menu.Menu = function(config){
33701     Roo.apply(this, config);
33702     this.id = this.id || Roo.id();
33703     this.addEvents({
33704         /**
33705          * @event beforeshow
33706          * Fires before this menu is displayed
33707          * @param {Roo.menu.Menu} this
33708          */
33709         beforeshow : true,
33710         /**
33711          * @event beforehide
33712          * Fires before this menu is hidden
33713          * @param {Roo.menu.Menu} this
33714          */
33715         beforehide : true,
33716         /**
33717          * @event show
33718          * Fires after this menu is displayed
33719          * @param {Roo.menu.Menu} this
33720          */
33721         show : true,
33722         /**
33723          * @event hide
33724          * Fires after this menu is hidden
33725          * @param {Roo.menu.Menu} this
33726          */
33727         hide : true,
33728         /**
33729          * @event click
33730          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33731          * @param {Roo.menu.Menu} this
33732          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33733          * @param {Roo.EventObject} e
33734          */
33735         click : true,
33736         /**
33737          * @event mouseover
33738          * Fires when the mouse is hovering over this menu
33739          * @param {Roo.menu.Menu} this
33740          * @param {Roo.EventObject} e
33741          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33742          */
33743         mouseover : true,
33744         /**
33745          * @event mouseout
33746          * Fires when the mouse exits this menu
33747          * @param {Roo.menu.Menu} this
33748          * @param {Roo.EventObject} e
33749          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33750          */
33751         mouseout : true,
33752         /**
33753          * @event itemclick
33754          * Fires when a menu item contained in this menu is clicked
33755          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33756          * @param {Roo.EventObject} e
33757          */
33758         itemclick: true
33759     });
33760     if (this.registerMenu) {
33761         Roo.menu.MenuMgr.register(this);
33762     }
33763     
33764     var mis = this.items;
33765     this.items = new Roo.util.MixedCollection();
33766     if(mis){
33767         this.add.apply(this, mis);
33768     }
33769 };
33770
33771 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33772     /**
33773      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33774      */
33775     minWidth : 120,
33776     /**
33777      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33778      * for bottom-right shadow (defaults to "sides")
33779      */
33780     shadow : "sides",
33781     /**
33782      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33783      * this menu (defaults to "tl-tr?")
33784      */
33785     subMenuAlign : "tl-tr?",
33786     /**
33787      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33788      * relative to its element of origin (defaults to "tl-bl?")
33789      */
33790     defaultAlign : "tl-bl?",
33791     /**
33792      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33793      */
33794     allowOtherMenus : false,
33795     /**
33796      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33797      */
33798     registerMenu : true,
33799
33800     hidden:true,
33801
33802     // private
33803     render : function(){
33804         if(this.el){
33805             return;
33806         }
33807         var el = this.el = new Roo.Layer({
33808             cls: "x-menu",
33809             shadow:this.shadow,
33810             constrain: false,
33811             parentEl: this.parentEl || document.body,
33812             zindex:15000
33813         });
33814
33815         this.keyNav = new Roo.menu.MenuNav(this);
33816
33817         if(this.plain){
33818             el.addClass("x-menu-plain");
33819         }
33820         if(this.cls){
33821             el.addClass(this.cls);
33822         }
33823         // generic focus element
33824         this.focusEl = el.createChild({
33825             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33826         });
33827         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33828         ul.on("click", this.onClick, this);
33829         ul.on("mouseover", this.onMouseOver, this);
33830         ul.on("mouseout", this.onMouseOut, this);
33831         this.items.each(function(item){
33832             var li = document.createElement("li");
33833             li.className = "x-menu-list-item";
33834             ul.dom.appendChild(li);
33835             item.render(li, this);
33836         }, this);
33837         this.ul = ul;
33838         this.autoWidth();
33839     },
33840
33841     // private
33842     autoWidth : function(){
33843         var el = this.el, ul = this.ul;
33844         if(!el){
33845             return;
33846         }
33847         var w = this.width;
33848         if(w){
33849             el.setWidth(w);
33850         }else if(Roo.isIE){
33851             el.setWidth(this.minWidth);
33852             var t = el.dom.offsetWidth; // force recalc
33853             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33854         }
33855     },
33856
33857     // private
33858     delayAutoWidth : function(){
33859         if(this.rendered){
33860             if(!this.awTask){
33861                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33862             }
33863             this.awTask.delay(20);
33864         }
33865     },
33866
33867     // private
33868     findTargetItem : function(e){
33869         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33870         if(t && t.menuItemId){
33871             return this.items.get(t.menuItemId);
33872         }
33873     },
33874
33875     // private
33876     onClick : function(e){
33877         var t;
33878         if(t = this.findTargetItem(e)){
33879             t.onClick(e);
33880             this.fireEvent("click", this, t, e);
33881         }
33882     },
33883
33884     // private
33885     setActiveItem : function(item, autoExpand){
33886         if(item != this.activeItem){
33887             if(this.activeItem){
33888                 this.activeItem.deactivate();
33889             }
33890             this.activeItem = item;
33891             item.activate(autoExpand);
33892         }else if(autoExpand){
33893             item.expandMenu();
33894         }
33895     },
33896
33897     // private
33898     tryActivate : function(start, step){
33899         var items = this.items;
33900         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33901             var item = items.get(i);
33902             if(!item.disabled && item.canActivate){
33903                 this.setActiveItem(item, false);
33904                 return item;
33905             }
33906         }
33907         return false;
33908     },
33909
33910     // private
33911     onMouseOver : function(e){
33912         var t;
33913         if(t = this.findTargetItem(e)){
33914             if(t.canActivate && !t.disabled){
33915                 this.setActiveItem(t, true);
33916             }
33917         }
33918         this.fireEvent("mouseover", this, e, t);
33919     },
33920
33921     // private
33922     onMouseOut : function(e){
33923         var t;
33924         if(t = this.findTargetItem(e)){
33925             if(t == this.activeItem && t.shouldDeactivate(e)){
33926                 this.activeItem.deactivate();
33927                 delete this.activeItem;
33928             }
33929         }
33930         this.fireEvent("mouseout", this, e, t);
33931     },
33932
33933     /**
33934      * Read-only.  Returns true if the menu is currently displayed, else false.
33935      * @type Boolean
33936      */
33937     isVisible : function(){
33938         return this.el && !this.hidden;
33939     },
33940
33941     /**
33942      * Displays this menu relative to another element
33943      * @param {String/HTMLElement/Roo.Element} element The element to align to
33944      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33945      * the element (defaults to this.defaultAlign)
33946      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33947      */
33948     show : function(el, pos, parentMenu){
33949         this.parentMenu = parentMenu;
33950         if(!this.el){
33951             this.render();
33952         }
33953         this.fireEvent("beforeshow", this);
33954         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33955     },
33956
33957     /**
33958      * Displays this menu at a specific xy position
33959      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33960      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33961      */
33962     showAt : function(xy, parentMenu, /* private: */_e){
33963         this.parentMenu = parentMenu;
33964         if(!this.el){
33965             this.render();
33966         }
33967         if(_e !== false){
33968             this.fireEvent("beforeshow", this);
33969             xy = this.el.adjustForConstraints(xy);
33970         }
33971         this.el.setXY(xy);
33972         this.el.show();
33973         this.hidden = false;
33974         this.focus();
33975         this.fireEvent("show", this);
33976     },
33977
33978     focus : function(){
33979         if(!this.hidden){
33980             this.doFocus.defer(50, this);
33981         }
33982     },
33983
33984     doFocus : function(){
33985         if(!this.hidden){
33986             this.focusEl.focus();
33987         }
33988     },
33989
33990     /**
33991      * Hides this menu and optionally all parent menus
33992      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33993      */
33994     hide : function(deep){
33995         if(this.el && this.isVisible()){
33996             this.fireEvent("beforehide", this);
33997             if(this.activeItem){
33998                 this.activeItem.deactivate();
33999                 this.activeItem = null;
34000             }
34001             this.el.hide();
34002             this.hidden = true;
34003             this.fireEvent("hide", this);
34004         }
34005         if(deep === true && this.parentMenu){
34006             this.parentMenu.hide(true);
34007         }
34008     },
34009
34010     /**
34011      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34012      * Any of the following are valid:
34013      * <ul>
34014      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34015      * <li>An HTMLElement object which will be converted to a menu item</li>
34016      * <li>A menu item config object that will be created as a new menu item</li>
34017      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34018      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34019      * </ul>
34020      * Usage:
34021      * <pre><code>
34022 // Create the menu
34023 var menu = new Roo.menu.Menu();
34024
34025 // Create a menu item to add by reference
34026 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34027
34028 // Add a bunch of items at once using different methods.
34029 // Only the last item added will be returned.
34030 var item = menu.add(
34031     menuItem,                // add existing item by ref
34032     'Dynamic Item',          // new TextItem
34033     '-',                     // new separator
34034     { text: 'Config Item' }  // new item by config
34035 );
34036 </code></pre>
34037      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34038      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34039      */
34040     add : function(){
34041         var a = arguments, l = a.length, item;
34042         for(var i = 0; i < l; i++){
34043             var el = a[i];
34044             if ((typeof(el) == "object") && el.xtype && el.xns) {
34045                 el = Roo.factory(el, Roo.menu);
34046             }
34047             
34048             if(el.render){ // some kind of Item
34049                 item = this.addItem(el);
34050             }else if(typeof el == "string"){ // string
34051                 if(el == "separator" || el == "-"){
34052                     item = this.addSeparator();
34053                 }else{
34054                     item = this.addText(el);
34055                 }
34056             }else if(el.tagName || el.el){ // element
34057                 item = this.addElement(el);
34058             }else if(typeof el == "object"){ // must be menu item config?
34059                 item = this.addMenuItem(el);
34060             }
34061         }
34062         return item;
34063     },
34064
34065     /**
34066      * Returns this menu's underlying {@link Roo.Element} object
34067      * @return {Roo.Element} The element
34068      */
34069     getEl : function(){
34070         if(!this.el){
34071             this.render();
34072         }
34073         return this.el;
34074     },
34075
34076     /**
34077      * Adds a separator bar to the menu
34078      * @return {Roo.menu.Item} The menu item that was added
34079      */
34080     addSeparator : function(){
34081         return this.addItem(new Roo.menu.Separator());
34082     },
34083
34084     /**
34085      * Adds an {@link Roo.Element} object to the menu
34086      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34087      * @return {Roo.menu.Item} The menu item that was added
34088      */
34089     addElement : function(el){
34090         return this.addItem(new Roo.menu.BaseItem(el));
34091     },
34092
34093     /**
34094      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34095      * @param {Roo.menu.Item} item The menu item to add
34096      * @return {Roo.menu.Item} The menu item that was added
34097      */
34098     addItem : function(item){
34099         this.items.add(item);
34100         if(this.ul){
34101             var li = document.createElement("li");
34102             li.className = "x-menu-list-item";
34103             this.ul.dom.appendChild(li);
34104             item.render(li, this);
34105             this.delayAutoWidth();
34106         }
34107         return item;
34108     },
34109
34110     /**
34111      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34112      * @param {Object} config A MenuItem config object
34113      * @return {Roo.menu.Item} The menu item that was added
34114      */
34115     addMenuItem : function(config){
34116         if(!(config instanceof Roo.menu.Item)){
34117             if(typeof config.checked == "boolean"){ // must be check menu item config?
34118                 config = new Roo.menu.CheckItem(config);
34119             }else{
34120                 config = new Roo.menu.Item(config);
34121             }
34122         }
34123         return this.addItem(config);
34124     },
34125
34126     /**
34127      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34128      * @param {String} text The text to display in the menu item
34129      * @return {Roo.menu.Item} The menu item that was added
34130      */
34131     addText : function(text){
34132         return this.addItem(new Roo.menu.TextItem({ text : text }));
34133     },
34134
34135     /**
34136      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34137      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34138      * @param {Roo.menu.Item} item The menu item to add
34139      * @return {Roo.menu.Item} The menu item that was added
34140      */
34141     insert : function(index, item){
34142         this.items.insert(index, item);
34143         if(this.ul){
34144             var li = document.createElement("li");
34145             li.className = "x-menu-list-item";
34146             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34147             item.render(li, this);
34148             this.delayAutoWidth();
34149         }
34150         return item;
34151     },
34152
34153     /**
34154      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34155      * @param {Roo.menu.Item} item The menu item to remove
34156      */
34157     remove : function(item){
34158         this.items.removeKey(item.id);
34159         item.destroy();
34160     },
34161
34162     /**
34163      * Removes and destroys all items in the menu
34164      */
34165     removeAll : function(){
34166         var f;
34167         while(f = this.items.first()){
34168             this.remove(f);
34169         }
34170     }
34171 });
34172
34173 // MenuNav is a private utility class used internally by the Menu
34174 Roo.menu.MenuNav = function(menu){
34175     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34176     this.scope = this.menu = menu;
34177 };
34178
34179 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34180     doRelay : function(e, h){
34181         var k = e.getKey();
34182         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34183             this.menu.tryActivate(0, 1);
34184             return false;
34185         }
34186         return h.call(this.scope || this, e, this.menu);
34187     },
34188
34189     up : function(e, m){
34190         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34191             m.tryActivate(m.items.length-1, -1);
34192         }
34193     },
34194
34195     down : function(e, m){
34196         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34197             m.tryActivate(0, 1);
34198         }
34199     },
34200
34201     right : function(e, m){
34202         if(m.activeItem){
34203             m.activeItem.expandMenu(true);
34204         }
34205     },
34206
34207     left : function(e, m){
34208         m.hide();
34209         if(m.parentMenu && m.parentMenu.activeItem){
34210             m.parentMenu.activeItem.activate();
34211         }
34212     },
34213
34214     enter : function(e, m){
34215         if(m.activeItem){
34216             e.stopPropagation();
34217             m.activeItem.onClick(e);
34218             m.fireEvent("click", this, m.activeItem);
34219             return true;
34220         }
34221     }
34222 });/*
34223  * Based on:
34224  * Ext JS Library 1.1.1
34225  * Copyright(c) 2006-2007, Ext JS, LLC.
34226  *
34227  * Originally Released Under LGPL - original licence link has changed is not relivant.
34228  *
34229  * Fork - LGPL
34230  * <script type="text/javascript">
34231  */
34232  
34233 /**
34234  * @class Roo.menu.MenuMgr
34235  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34236  * @singleton
34237  */
34238 Roo.menu.MenuMgr = function(){
34239    var menus, active, groups = {}, attached = false, lastShow = new Date();
34240
34241    // private - called when first menu is created
34242    function init(){
34243        menus = {};
34244        active = new Roo.util.MixedCollection();
34245        Roo.get(document).addKeyListener(27, function(){
34246            if(active.length > 0){
34247                hideAll();
34248            }
34249        });
34250    }
34251
34252    // private
34253    function hideAll(){
34254        if(active && active.length > 0){
34255            var c = active.clone();
34256            c.each(function(m){
34257                m.hide();
34258            });
34259        }
34260    }
34261
34262    // private
34263    function onHide(m){
34264        active.remove(m);
34265        if(active.length < 1){
34266            Roo.get(document).un("mousedown", onMouseDown);
34267            attached = false;
34268        }
34269    }
34270
34271    // private
34272    function onShow(m){
34273        var last = active.last();
34274        lastShow = new Date();
34275        active.add(m);
34276        if(!attached){
34277            Roo.get(document).on("mousedown", onMouseDown);
34278            attached = true;
34279        }
34280        if(m.parentMenu){
34281           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34282           m.parentMenu.activeChild = m;
34283        }else if(last && last.isVisible()){
34284           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34285        }
34286    }
34287
34288    // private
34289    function onBeforeHide(m){
34290        if(m.activeChild){
34291            m.activeChild.hide();
34292        }
34293        if(m.autoHideTimer){
34294            clearTimeout(m.autoHideTimer);
34295            delete m.autoHideTimer;
34296        }
34297    }
34298
34299    // private
34300    function onBeforeShow(m){
34301        var pm = m.parentMenu;
34302        if(!pm && !m.allowOtherMenus){
34303            hideAll();
34304        }else if(pm && pm.activeChild && active != m){
34305            pm.activeChild.hide();
34306        }
34307    }
34308
34309    // private
34310    function onMouseDown(e){
34311        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34312            hideAll();
34313        }
34314    }
34315
34316    // private
34317    function onBeforeCheck(mi, state){
34318        if(state){
34319            var g = groups[mi.group];
34320            for(var i = 0, l = g.length; i < l; i++){
34321                if(g[i] != mi){
34322                    g[i].setChecked(false);
34323                }
34324            }
34325        }
34326    }
34327
34328    return {
34329
34330        /**
34331         * Hides all menus that are currently visible
34332         */
34333        hideAll : function(){
34334             hideAll();  
34335        },
34336
34337        // private
34338        register : function(menu){
34339            if(!menus){
34340                init();
34341            }
34342            menus[menu.id] = menu;
34343            menu.on("beforehide", onBeforeHide);
34344            menu.on("hide", onHide);
34345            menu.on("beforeshow", onBeforeShow);
34346            menu.on("show", onShow);
34347            var g = menu.group;
34348            if(g && menu.events["checkchange"]){
34349                if(!groups[g]){
34350                    groups[g] = [];
34351                }
34352                groups[g].push(menu);
34353                menu.on("checkchange", onCheck);
34354            }
34355        },
34356
34357         /**
34358          * Returns a {@link Roo.menu.Menu} object
34359          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34360          * be used to generate and return a new Menu instance.
34361          */
34362        get : function(menu){
34363            if(typeof menu == "string"){ // menu id
34364                return menus[menu];
34365            }else if(menu.events){  // menu instance
34366                return menu;
34367            }else if(typeof menu.length == 'number'){ // array of menu items?
34368                return new Roo.menu.Menu({items:menu});
34369            }else{ // otherwise, must be a config
34370                return new Roo.menu.Menu(menu);
34371            }
34372        },
34373
34374        // private
34375        unregister : function(menu){
34376            delete menus[menu.id];
34377            menu.un("beforehide", onBeforeHide);
34378            menu.un("hide", onHide);
34379            menu.un("beforeshow", onBeforeShow);
34380            menu.un("show", onShow);
34381            var g = menu.group;
34382            if(g && menu.events["checkchange"]){
34383                groups[g].remove(menu);
34384                menu.un("checkchange", onCheck);
34385            }
34386        },
34387
34388        // private
34389        registerCheckable : function(menuItem){
34390            var g = menuItem.group;
34391            if(g){
34392                if(!groups[g]){
34393                    groups[g] = [];
34394                }
34395                groups[g].push(menuItem);
34396                menuItem.on("beforecheckchange", onBeforeCheck);
34397            }
34398        },
34399
34400        // private
34401        unregisterCheckable : function(menuItem){
34402            var g = menuItem.group;
34403            if(g){
34404                groups[g].remove(menuItem);
34405                menuItem.un("beforecheckchange", onBeforeCheck);
34406            }
34407        }
34408    };
34409 }();/*
34410  * Based on:
34411  * Ext JS Library 1.1.1
34412  * Copyright(c) 2006-2007, Ext JS, LLC.
34413  *
34414  * Originally Released Under LGPL - original licence link has changed is not relivant.
34415  *
34416  * Fork - LGPL
34417  * <script type="text/javascript">
34418  */
34419  
34420
34421 /**
34422  * @class Roo.menu.BaseItem
34423  * @extends Roo.Component
34424  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34425  * management and base configuration options shared by all menu components.
34426  * @constructor
34427  * Creates a new BaseItem
34428  * @param {Object} config Configuration options
34429  */
34430 Roo.menu.BaseItem = function(config){
34431     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34432
34433     this.addEvents({
34434         /**
34435          * @event click
34436          * Fires when this item is clicked
34437          * @param {Roo.menu.BaseItem} this
34438          * @param {Roo.EventObject} e
34439          */
34440         click: true,
34441         /**
34442          * @event activate
34443          * Fires when this item is activated
34444          * @param {Roo.menu.BaseItem} this
34445          */
34446         activate : true,
34447         /**
34448          * @event deactivate
34449          * Fires when this item is deactivated
34450          * @param {Roo.menu.BaseItem} this
34451          */
34452         deactivate : true
34453     });
34454
34455     if(this.handler){
34456         this.on("click", this.handler, this.scope, true);
34457     }
34458 };
34459
34460 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34461     /**
34462      * @cfg {Function} handler
34463      * A function that will handle the click event of this menu item (defaults to undefined)
34464      */
34465     /**
34466      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34467      */
34468     canActivate : false,
34469     /**
34470      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34471      */
34472     activeClass : "x-menu-item-active",
34473     /**
34474      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34475      */
34476     hideOnClick : true,
34477     /**
34478      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34479      */
34480     hideDelay : 100,
34481
34482     // private
34483     ctype: "Roo.menu.BaseItem",
34484
34485     // private
34486     actionMode : "container",
34487
34488     // private
34489     render : function(container, parentMenu){
34490         this.parentMenu = parentMenu;
34491         Roo.menu.BaseItem.superclass.render.call(this, container);
34492         this.container.menuItemId = this.id;
34493     },
34494
34495     // private
34496     onRender : function(container, position){
34497         this.el = Roo.get(this.el);
34498         container.dom.appendChild(this.el.dom);
34499     },
34500
34501     // private
34502     onClick : function(e){
34503         if(!this.disabled && this.fireEvent("click", this, e) !== false
34504                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34505             this.handleClick(e);
34506         }else{
34507             e.stopEvent();
34508         }
34509     },
34510
34511     // private
34512     activate : function(){
34513         if(this.disabled){
34514             return false;
34515         }
34516         var li = this.container;
34517         li.addClass(this.activeClass);
34518         this.region = li.getRegion().adjust(2, 2, -2, -2);
34519         this.fireEvent("activate", this);
34520         return true;
34521     },
34522
34523     // private
34524     deactivate : function(){
34525         this.container.removeClass(this.activeClass);
34526         this.fireEvent("deactivate", this);
34527     },
34528
34529     // private
34530     shouldDeactivate : function(e){
34531         return !this.region || !this.region.contains(e.getPoint());
34532     },
34533
34534     // private
34535     handleClick : function(e){
34536         if(this.hideOnClick){
34537             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34538         }
34539     },
34540
34541     // private
34542     expandMenu : function(autoActivate){
34543         // do nothing
34544     },
34545
34546     // private
34547     hideMenu : function(){
34548         // do nothing
34549     }
34550 });/*
34551  * Based on:
34552  * Ext JS Library 1.1.1
34553  * Copyright(c) 2006-2007, Ext JS, LLC.
34554  *
34555  * Originally Released Under LGPL - original licence link has changed is not relivant.
34556  *
34557  * Fork - LGPL
34558  * <script type="text/javascript">
34559  */
34560  
34561 /**
34562  * @class Roo.menu.Adapter
34563  * @extends Roo.menu.BaseItem
34564  * 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.
34565  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34566  * @constructor
34567  * Creates a new Adapter
34568  * @param {Object} config Configuration options
34569  */
34570 Roo.menu.Adapter = function(component, config){
34571     Roo.menu.Adapter.superclass.constructor.call(this, config);
34572     this.component = component;
34573 };
34574 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34575     // private
34576     canActivate : true,
34577
34578     // private
34579     onRender : function(container, position){
34580         this.component.render(container);
34581         this.el = this.component.getEl();
34582     },
34583
34584     // private
34585     activate : function(){
34586         if(this.disabled){
34587             return false;
34588         }
34589         this.component.focus();
34590         this.fireEvent("activate", this);
34591         return true;
34592     },
34593
34594     // private
34595     deactivate : function(){
34596         this.fireEvent("deactivate", this);
34597     },
34598
34599     // private
34600     disable : function(){
34601         this.component.disable();
34602         Roo.menu.Adapter.superclass.disable.call(this);
34603     },
34604
34605     // private
34606     enable : function(){
34607         this.component.enable();
34608         Roo.menu.Adapter.superclass.enable.call(this);
34609     }
34610 });/*
34611  * Based on:
34612  * Ext JS Library 1.1.1
34613  * Copyright(c) 2006-2007, Ext JS, LLC.
34614  *
34615  * Originally Released Under LGPL - original licence link has changed is not relivant.
34616  *
34617  * Fork - LGPL
34618  * <script type="text/javascript">
34619  */
34620
34621 /**
34622  * @class Roo.menu.TextItem
34623  * @extends Roo.menu.BaseItem
34624  * Adds a static text string to a menu, usually used as either a heading or group separator.
34625  * Note: old style constructor with text is still supported.
34626  * 
34627  * @constructor
34628  * Creates a new TextItem
34629  * @param {Object} cfg Configuration
34630  */
34631 Roo.menu.TextItem = function(cfg){
34632     if (typeof(cfg) == 'string') {
34633         this.text = cfg;
34634     } else {
34635         Roo.apply(this,cfg);
34636     }
34637     
34638     Roo.menu.TextItem.superclass.constructor.call(this);
34639 };
34640
34641 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34642     /**
34643      * @cfg {Boolean} text Text to show on item.
34644      */
34645     text : '',
34646     
34647     /**
34648      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34649      */
34650     hideOnClick : false,
34651     /**
34652      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34653      */
34654     itemCls : "x-menu-text",
34655
34656     // private
34657     onRender : function(){
34658         var s = document.createElement("span");
34659         s.className = this.itemCls;
34660         s.innerHTML = this.text;
34661         this.el = s;
34662         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34663     }
34664 });/*
34665  * Based on:
34666  * Ext JS Library 1.1.1
34667  * Copyright(c) 2006-2007, Ext JS, LLC.
34668  *
34669  * Originally Released Under LGPL - original licence link has changed is not relivant.
34670  *
34671  * Fork - LGPL
34672  * <script type="text/javascript">
34673  */
34674
34675 /**
34676  * @class Roo.menu.Separator
34677  * @extends Roo.menu.BaseItem
34678  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34679  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34680  * @constructor
34681  * @param {Object} config Configuration options
34682  */
34683 Roo.menu.Separator = function(config){
34684     Roo.menu.Separator.superclass.constructor.call(this, config);
34685 };
34686
34687 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34688     /**
34689      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34690      */
34691     itemCls : "x-menu-sep",
34692     /**
34693      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34694      */
34695     hideOnClick : false,
34696
34697     // private
34698     onRender : function(li){
34699         var s = document.createElement("span");
34700         s.className = this.itemCls;
34701         s.innerHTML = "&#160;";
34702         this.el = s;
34703         li.addClass("x-menu-sep-li");
34704         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34705     }
34706 });/*
34707  * Based on:
34708  * Ext JS Library 1.1.1
34709  * Copyright(c) 2006-2007, Ext JS, LLC.
34710  *
34711  * Originally Released Under LGPL - original licence link has changed is not relivant.
34712  *
34713  * Fork - LGPL
34714  * <script type="text/javascript">
34715  */
34716 /**
34717  * @class Roo.menu.Item
34718  * @extends Roo.menu.BaseItem
34719  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34720  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34721  * activation and click handling.
34722  * @constructor
34723  * Creates a new Item
34724  * @param {Object} config Configuration options
34725  */
34726 Roo.menu.Item = function(config){
34727     Roo.menu.Item.superclass.constructor.call(this, config);
34728     if(this.menu){
34729         this.menu = Roo.menu.MenuMgr.get(this.menu);
34730     }
34731 };
34732 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34733     
34734     /**
34735      * @cfg {String} text
34736      * The text to show on the menu item.
34737      */
34738     text: '',
34739      /**
34740      * @cfg {String} HTML to render in menu
34741      * The text to show on the menu item (HTML version).
34742      */
34743     html: '',
34744     /**
34745      * @cfg {String} icon
34746      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34747      */
34748     icon: undefined,
34749     /**
34750      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34751      */
34752     itemCls : "x-menu-item",
34753     /**
34754      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34755      */
34756     canActivate : true,
34757     /**
34758      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34759      */
34760     showDelay: 200,
34761     // doc'd in BaseItem
34762     hideDelay: 200,
34763
34764     // private
34765     ctype: "Roo.menu.Item",
34766     
34767     // private
34768     onRender : function(container, position){
34769         var el = document.createElement("a");
34770         el.hideFocus = true;
34771         el.unselectable = "on";
34772         el.href = this.href || "#";
34773         if(this.hrefTarget){
34774             el.target = this.hrefTarget;
34775         }
34776         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34777         
34778         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34779         
34780         el.innerHTML = String.format(
34781                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34782                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34783         this.el = el;
34784         Roo.menu.Item.superclass.onRender.call(this, container, position);
34785     },
34786
34787     /**
34788      * Sets the text to display in this menu item
34789      * @param {String} text The text to display
34790      * @param {Boolean} isHTML true to indicate text is pure html.
34791      */
34792     setText : function(text, isHTML){
34793         if (isHTML) {
34794             this.html = text;
34795         } else {
34796             this.text = text;
34797             this.html = '';
34798         }
34799         if(this.rendered){
34800             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34801      
34802             this.el.update(String.format(
34803                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34804                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34805             this.parentMenu.autoWidth();
34806         }
34807     },
34808
34809     // private
34810     handleClick : function(e){
34811         if(!this.href){ // if no link defined, stop the event automatically
34812             e.stopEvent();
34813         }
34814         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34815     },
34816
34817     // private
34818     activate : function(autoExpand){
34819         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34820             this.focus();
34821             if(autoExpand){
34822                 this.expandMenu();
34823             }
34824         }
34825         return true;
34826     },
34827
34828     // private
34829     shouldDeactivate : function(e){
34830         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34831             if(this.menu && this.menu.isVisible()){
34832                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34833             }
34834             return true;
34835         }
34836         return false;
34837     },
34838
34839     // private
34840     deactivate : function(){
34841         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34842         this.hideMenu();
34843     },
34844
34845     // private
34846     expandMenu : function(autoActivate){
34847         if(!this.disabled && this.menu){
34848             clearTimeout(this.hideTimer);
34849             delete this.hideTimer;
34850             if(!this.menu.isVisible() && !this.showTimer){
34851                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34852             }else if (this.menu.isVisible() && autoActivate){
34853                 this.menu.tryActivate(0, 1);
34854             }
34855         }
34856     },
34857
34858     // private
34859     deferExpand : function(autoActivate){
34860         delete this.showTimer;
34861         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34862         if(autoActivate){
34863             this.menu.tryActivate(0, 1);
34864         }
34865     },
34866
34867     // private
34868     hideMenu : function(){
34869         clearTimeout(this.showTimer);
34870         delete this.showTimer;
34871         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34872             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34873         }
34874     },
34875
34876     // private
34877     deferHide : function(){
34878         delete this.hideTimer;
34879         this.menu.hide();
34880     }
34881 });/*
34882  * Based on:
34883  * Ext JS Library 1.1.1
34884  * Copyright(c) 2006-2007, Ext JS, LLC.
34885  *
34886  * Originally Released Under LGPL - original licence link has changed is not relivant.
34887  *
34888  * Fork - LGPL
34889  * <script type="text/javascript">
34890  */
34891  
34892 /**
34893  * @class Roo.menu.CheckItem
34894  * @extends Roo.menu.Item
34895  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34896  * @constructor
34897  * Creates a new CheckItem
34898  * @param {Object} config Configuration options
34899  */
34900 Roo.menu.CheckItem = function(config){
34901     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34902     this.addEvents({
34903         /**
34904          * @event beforecheckchange
34905          * Fires before the checked value is set, providing an opportunity to cancel if needed
34906          * @param {Roo.menu.CheckItem} this
34907          * @param {Boolean} checked The new checked value that will be set
34908          */
34909         "beforecheckchange" : true,
34910         /**
34911          * @event checkchange
34912          * Fires after the checked value has been set
34913          * @param {Roo.menu.CheckItem} this
34914          * @param {Boolean} checked The checked value that was set
34915          */
34916         "checkchange" : true
34917     });
34918     if(this.checkHandler){
34919         this.on('checkchange', this.checkHandler, this.scope);
34920     }
34921 };
34922 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34923     /**
34924      * @cfg {String} group
34925      * All check items with the same group name will automatically be grouped into a single-select
34926      * radio button group (defaults to '')
34927      */
34928     /**
34929      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34930      */
34931     itemCls : "x-menu-item x-menu-check-item",
34932     /**
34933      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34934      */
34935     groupClass : "x-menu-group-item",
34936
34937     /**
34938      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34939      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34940      * initialized with checked = true will be rendered as checked.
34941      */
34942     checked: false,
34943
34944     // private
34945     ctype: "Roo.menu.CheckItem",
34946
34947     // private
34948     onRender : function(c){
34949         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34950         if(this.group){
34951             this.el.addClass(this.groupClass);
34952         }
34953         Roo.menu.MenuMgr.registerCheckable(this);
34954         if(this.checked){
34955             this.checked = false;
34956             this.setChecked(true, true);
34957         }
34958     },
34959
34960     // private
34961     destroy : function(){
34962         if(this.rendered){
34963             Roo.menu.MenuMgr.unregisterCheckable(this);
34964         }
34965         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34966     },
34967
34968     /**
34969      * Set the checked state of this item
34970      * @param {Boolean} checked The new checked value
34971      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34972      */
34973     setChecked : function(state, suppressEvent){
34974         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34975             if(this.container){
34976                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34977             }
34978             this.checked = state;
34979             if(suppressEvent !== true){
34980                 this.fireEvent("checkchange", this, state);
34981             }
34982         }
34983     },
34984
34985     // private
34986     handleClick : function(e){
34987        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34988            this.setChecked(!this.checked);
34989        }
34990        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34991     }
34992 });/*
34993  * Based on:
34994  * Ext JS Library 1.1.1
34995  * Copyright(c) 2006-2007, Ext JS, LLC.
34996  *
34997  * Originally Released Under LGPL - original licence link has changed is not relivant.
34998  *
34999  * Fork - LGPL
35000  * <script type="text/javascript">
35001  */
35002  
35003 /**
35004  * @class Roo.menu.DateItem
35005  * @extends Roo.menu.Adapter
35006  * A menu item that wraps the {@link Roo.DatPicker} component.
35007  * @constructor
35008  * Creates a new DateItem
35009  * @param {Object} config Configuration options
35010  */
35011 Roo.menu.DateItem = function(config){
35012     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35013     /** The Roo.DatePicker object @type Roo.DatePicker */
35014     this.picker = this.component;
35015     this.addEvents({select: true});
35016     
35017     this.picker.on("render", function(picker){
35018         picker.getEl().swallowEvent("click");
35019         picker.container.addClass("x-menu-date-item");
35020     });
35021
35022     this.picker.on("select", this.onSelect, this);
35023 };
35024
35025 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35026     // private
35027     onSelect : function(picker, date){
35028         this.fireEvent("select", this, date, picker);
35029         Roo.menu.DateItem.superclass.handleClick.call(this);
35030     }
35031 });/*
35032  * Based on:
35033  * Ext JS Library 1.1.1
35034  * Copyright(c) 2006-2007, Ext JS, LLC.
35035  *
35036  * Originally Released Under LGPL - original licence link has changed is not relivant.
35037  *
35038  * Fork - LGPL
35039  * <script type="text/javascript">
35040  */
35041  
35042 /**
35043  * @class Roo.menu.ColorItem
35044  * @extends Roo.menu.Adapter
35045  * A menu item that wraps the {@link Roo.ColorPalette} component.
35046  * @constructor
35047  * Creates a new ColorItem
35048  * @param {Object} config Configuration options
35049  */
35050 Roo.menu.ColorItem = function(config){
35051     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35052     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35053     this.palette = this.component;
35054     this.relayEvents(this.palette, ["select"]);
35055     if(this.selectHandler){
35056         this.on('select', this.selectHandler, this.scope);
35057     }
35058 };
35059 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35060  * Based on:
35061  * Ext JS Library 1.1.1
35062  * Copyright(c) 2006-2007, Ext JS, LLC.
35063  *
35064  * Originally Released Under LGPL - original licence link has changed is not relivant.
35065  *
35066  * Fork - LGPL
35067  * <script type="text/javascript">
35068  */
35069  
35070
35071 /**
35072  * @class Roo.menu.DateMenu
35073  * @extends Roo.menu.Menu
35074  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35075  * @constructor
35076  * Creates a new DateMenu
35077  * @param {Object} config Configuration options
35078  */
35079 Roo.menu.DateMenu = function(config){
35080     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35081     this.plain = true;
35082     var di = new Roo.menu.DateItem(config);
35083     this.add(di);
35084     /**
35085      * The {@link Roo.DatePicker} instance for this DateMenu
35086      * @type DatePicker
35087      */
35088     this.picker = di.picker;
35089     /**
35090      * @event select
35091      * @param {DatePicker} picker
35092      * @param {Date} date
35093      */
35094     this.relayEvents(di, ["select"]);
35095
35096     this.on('beforeshow', function(){
35097         if(this.picker){
35098             this.picker.hideMonthPicker(true);
35099         }
35100     }, this);
35101 };
35102 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35103     cls:'x-date-menu'
35104 });/*
35105  * Based on:
35106  * Ext JS Library 1.1.1
35107  * Copyright(c) 2006-2007, Ext JS, LLC.
35108  *
35109  * Originally Released Under LGPL - original licence link has changed is not relivant.
35110  *
35111  * Fork - LGPL
35112  * <script type="text/javascript">
35113  */
35114  
35115
35116 /**
35117  * @class Roo.menu.ColorMenu
35118  * @extends Roo.menu.Menu
35119  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35120  * @constructor
35121  * Creates a new ColorMenu
35122  * @param {Object} config Configuration options
35123  */
35124 Roo.menu.ColorMenu = function(config){
35125     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35126     this.plain = true;
35127     var ci = new Roo.menu.ColorItem(config);
35128     this.add(ci);
35129     /**
35130      * The {@link Roo.ColorPalette} instance for this ColorMenu
35131      * @type ColorPalette
35132      */
35133     this.palette = ci.palette;
35134     /**
35135      * @event select
35136      * @param {ColorPalette} palette
35137      * @param {String} color
35138      */
35139     this.relayEvents(ci, ["select"]);
35140 };
35141 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35142  * Based on:
35143  * Ext JS Library 1.1.1
35144  * Copyright(c) 2006-2007, Ext JS, LLC.
35145  *
35146  * Originally Released Under LGPL - original licence link has changed is not relivant.
35147  *
35148  * Fork - LGPL
35149  * <script type="text/javascript">
35150  */
35151  
35152 /**
35153  * @class Roo.form.Field
35154  * @extends Roo.BoxComponent
35155  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35156  * @constructor
35157  * Creates a new Field
35158  * @param {Object} config Configuration options
35159  */
35160 Roo.form.Field = function(config){
35161     Roo.form.Field.superclass.constructor.call(this, config);
35162 };
35163
35164 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35165     /**
35166      * @cfg {String} fieldLabel Label to use when rendering a form.
35167      */
35168        /**
35169      * @cfg {String} qtip Mouse over tip
35170      */
35171      
35172     /**
35173      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35174      */
35175     invalidClass : "x-form-invalid",
35176     /**
35177      * @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")
35178      */
35179     invalidText : "The value in this field is invalid",
35180     /**
35181      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35182      */
35183     focusClass : "x-form-focus",
35184     /**
35185      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35186       automatic validation (defaults to "keyup").
35187      */
35188     validationEvent : "keyup",
35189     /**
35190      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35191      */
35192     validateOnBlur : true,
35193     /**
35194      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35195      */
35196     validationDelay : 250,
35197     /**
35198      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35199      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35200      */
35201     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35202     /**
35203      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35204      */
35205     fieldClass : "x-form-field",
35206     /**
35207      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35208      *<pre>
35209 Value         Description
35210 -----------   ----------------------------------------------------------------------
35211 qtip          Display a quick tip when the user hovers over the field
35212 title         Display a default browser title attribute popup
35213 under         Add a block div beneath the field containing the error text
35214 side          Add an error icon to the right of the field with a popup on hover
35215 [element id]  Add the error text directly to the innerHTML of the specified element
35216 </pre>
35217      */
35218     msgTarget : 'qtip',
35219     /**
35220      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35221      */
35222     msgFx : 'normal',
35223
35224     /**
35225      * @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.
35226      */
35227     readOnly : false,
35228
35229     /**
35230      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35231      */
35232     disabled : false,
35233
35234     /**
35235      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35236      */
35237     inputType : undefined,
35238     
35239     /**
35240      * @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).
35241          */
35242         tabIndex : undefined,
35243         
35244     // private
35245     isFormField : true,
35246
35247     // private
35248     hasFocus : false,
35249     /**
35250      * @property {Roo.Element} fieldEl
35251      * Element Containing the rendered Field (with label etc.)
35252      */
35253     /**
35254      * @cfg {Mixed} value A value to initialize this field with.
35255      */
35256     value : undefined,
35257
35258     /**
35259      * @cfg {String} name The field's HTML name attribute.
35260      */
35261     /**
35262      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35263      */
35264
35265         // private ??
35266         initComponent : function(){
35267         Roo.form.Field.superclass.initComponent.call(this);
35268         this.addEvents({
35269             /**
35270              * @event focus
35271              * Fires when this field receives input focus.
35272              * @param {Roo.form.Field} this
35273              */
35274             focus : true,
35275             /**
35276              * @event blur
35277              * Fires when this field loses input focus.
35278              * @param {Roo.form.Field} this
35279              */
35280             blur : true,
35281             /**
35282              * @event specialkey
35283              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35284              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35285              * @param {Roo.form.Field} this
35286              * @param {Roo.EventObject} e The event object
35287              */
35288             specialkey : true,
35289             /**
35290              * @event change
35291              * Fires just before the field blurs if the field value has changed.
35292              * @param {Roo.form.Field} this
35293              * @param {Mixed} newValue The new value
35294              * @param {Mixed} oldValue The original value
35295              */
35296             change : true,
35297             /**
35298              * @event invalid
35299              * Fires after the field has been marked as invalid.
35300              * @param {Roo.form.Field} this
35301              * @param {String} msg The validation message
35302              */
35303             invalid : true,
35304             /**
35305              * @event valid
35306              * Fires after the field has been validated with no errors.
35307              * @param {Roo.form.Field} this
35308              */
35309             valid : true,
35310              /**
35311              * @event keyup
35312              * Fires after the key up
35313              * @param {Roo.form.Field} this
35314              * @param {Roo.EventObject}  e The event Object
35315              */
35316             keyup : true
35317         });
35318     },
35319
35320     /**
35321      * Returns the name attribute of the field if available
35322      * @return {String} name The field name
35323      */
35324     getName: function(){
35325          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35326     },
35327
35328     // private
35329     onRender : function(ct, position){
35330         Roo.form.Field.superclass.onRender.call(this, ct, position);
35331         if(!this.el){
35332             var cfg = this.getAutoCreate();
35333             if(!cfg.name){
35334                 cfg.name = this.name || this.id;
35335             }
35336             if(this.inputType){
35337                 cfg.type = this.inputType;
35338             }
35339             this.el = ct.createChild(cfg, position);
35340         }
35341         var type = this.el.dom.type;
35342         if(type){
35343             if(type == 'password'){
35344                 type = 'text';
35345             }
35346             this.el.addClass('x-form-'+type);
35347         }
35348         if(this.readOnly){
35349             this.el.dom.readOnly = true;
35350         }
35351         if(this.tabIndex !== undefined){
35352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35353         }
35354
35355         this.el.addClass([this.fieldClass, this.cls]);
35356         this.initValue();
35357     },
35358
35359     /**
35360      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35361      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35362      * @return {Roo.form.Field} this
35363      */
35364     applyTo : function(target){
35365         this.allowDomMove = false;
35366         this.el = Roo.get(target);
35367         this.render(this.el.dom.parentNode);
35368         return this;
35369     },
35370
35371     // private
35372     initValue : function(){
35373         if(this.value !== undefined){
35374             this.setValue(this.value);
35375         }else if(this.el.dom.value.length > 0){
35376             this.setValue(this.el.dom.value);
35377         }
35378     },
35379
35380     /**
35381      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35382      */
35383     isDirty : function() {
35384         if(this.disabled) {
35385             return false;
35386         }
35387         return String(this.getValue()) !== String(this.originalValue);
35388     },
35389
35390     // private
35391     afterRender : function(){
35392         Roo.form.Field.superclass.afterRender.call(this);
35393         this.initEvents();
35394     },
35395
35396     // private
35397     fireKey : function(e){
35398         //Roo.log('field ' + e.getKey());
35399         if(e.isNavKeyPress()){
35400             this.fireEvent("specialkey", this, e);
35401         }
35402     },
35403
35404     /**
35405      * Resets the current field value to the originally loaded value and clears any validation messages
35406      */
35407     reset : function(){
35408         this.setValue(this.originalValue);
35409         this.clearInvalid();
35410     },
35411
35412     // private
35413     initEvents : function(){
35414         // safari killled keypress - so keydown is now used..
35415         this.el.on("keydown" , this.fireKey,  this);
35416         this.el.on("focus", this.onFocus,  this);
35417         this.el.on("blur", this.onBlur,  this);
35418         this.el.relayEvent('keyup', this);
35419
35420         // reference to original value for reset
35421         this.originalValue = this.getValue();
35422     },
35423
35424     // private
35425     onFocus : function(){
35426         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35427             this.el.addClass(this.focusClass);
35428         }
35429         if(!this.hasFocus){
35430             this.hasFocus = true;
35431             this.startValue = this.getValue();
35432             this.fireEvent("focus", this);
35433         }
35434     },
35435
35436     beforeBlur : Roo.emptyFn,
35437
35438     // private
35439     onBlur : function(){
35440         this.beforeBlur();
35441         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35442             this.el.removeClass(this.focusClass);
35443         }
35444         this.hasFocus = false;
35445         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35446             this.validate();
35447         }
35448         var v = this.getValue();
35449         if(String(v) !== String(this.startValue)){
35450             this.fireEvent('change', this, v, this.startValue);
35451         }
35452         this.fireEvent("blur", this);
35453     },
35454
35455     /**
35456      * Returns whether or not the field value is currently valid
35457      * @param {Boolean} preventMark True to disable marking the field invalid
35458      * @return {Boolean} True if the value is valid, else false
35459      */
35460     isValid : function(preventMark){
35461         if(this.disabled){
35462             return true;
35463         }
35464         var restore = this.preventMark;
35465         this.preventMark = preventMark === true;
35466         var v = this.validateValue(this.processValue(this.getRawValue()));
35467         this.preventMark = restore;
35468         return v;
35469     },
35470
35471     /**
35472      * Validates the field value
35473      * @return {Boolean} True if the value is valid, else false
35474      */
35475     validate : function(){
35476         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35477             this.clearInvalid();
35478             return true;
35479         }
35480         return false;
35481     },
35482
35483     processValue : function(value){
35484         return value;
35485     },
35486
35487     // private
35488     // Subclasses should provide the validation implementation by overriding this
35489     validateValue : function(value){
35490         return true;
35491     },
35492
35493     /**
35494      * Mark this field as invalid
35495      * @param {String} msg The validation message
35496      */
35497     markInvalid : function(msg){
35498         if(!this.rendered || this.preventMark){ // not rendered
35499             return;
35500         }
35501         this.el.addClass(this.invalidClass);
35502         msg = msg || this.invalidText;
35503         switch(this.msgTarget){
35504             case 'qtip':
35505                 this.el.dom.qtip = msg;
35506                 this.el.dom.qclass = 'x-form-invalid-tip';
35507                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35508                     Roo.QuickTips.enable();
35509                 }
35510                 break;
35511             case 'title':
35512                 this.el.dom.title = msg;
35513                 break;
35514             case 'under':
35515                 if(!this.errorEl){
35516                     var elp = this.el.findParent('.x-form-element', 5, true);
35517                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35518                     this.errorEl.setWidth(elp.getWidth(true)-20);
35519                 }
35520                 this.errorEl.update(msg);
35521                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35522                 break;
35523             case 'side':
35524                 if(!this.errorIcon){
35525                     var elp = this.el.findParent('.x-form-element', 5, true);
35526                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35527                 }
35528                 this.alignErrorIcon();
35529                 this.errorIcon.dom.qtip = msg;
35530                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35531                 this.errorIcon.show();
35532                 this.on('resize', this.alignErrorIcon, this);
35533                 break;
35534             default:
35535                 var t = Roo.getDom(this.msgTarget);
35536                 t.innerHTML = msg;
35537                 t.style.display = this.msgDisplay;
35538                 break;
35539         }
35540         this.fireEvent('invalid', this, msg);
35541     },
35542
35543     // private
35544     alignErrorIcon : function(){
35545         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35546     },
35547
35548     /**
35549      * Clear any invalid styles/messages for this field
35550      */
35551     clearInvalid : function(){
35552         if(!this.rendered || this.preventMark){ // not rendered
35553             return;
35554         }
35555         this.el.removeClass(this.invalidClass);
35556         switch(this.msgTarget){
35557             case 'qtip':
35558                 this.el.dom.qtip = '';
35559                 break;
35560             case 'title':
35561                 this.el.dom.title = '';
35562                 break;
35563             case 'under':
35564                 if(this.errorEl){
35565                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35566                 }
35567                 break;
35568             case 'side':
35569                 if(this.errorIcon){
35570                     this.errorIcon.dom.qtip = '';
35571                     this.errorIcon.hide();
35572                     this.un('resize', this.alignErrorIcon, this);
35573                 }
35574                 break;
35575             default:
35576                 var t = Roo.getDom(this.msgTarget);
35577                 t.innerHTML = '';
35578                 t.style.display = 'none';
35579                 break;
35580         }
35581         this.fireEvent('valid', this);
35582     },
35583
35584     /**
35585      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35586      * @return {Mixed} value The field value
35587      */
35588     getRawValue : function(){
35589         var v = this.el.getValue();
35590         if(v === this.emptyText){
35591             v = '';
35592         }
35593         return v;
35594     },
35595
35596     /**
35597      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35598      * @return {Mixed} value The field value
35599      */
35600     getValue : function(){
35601         var v = this.el.getValue();
35602         if(v === this.emptyText || v === undefined){
35603             v = '';
35604         }
35605         return v;
35606     },
35607
35608     /**
35609      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35610      * @param {Mixed} value The value to set
35611      */
35612     setRawValue : function(v){
35613         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35614     },
35615
35616     /**
35617      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35618      * @param {Mixed} value The value to set
35619      */
35620     setValue : function(v){
35621         this.value = v;
35622         if(this.rendered){
35623             this.el.dom.value = (v === null || v === undefined ? '' : v);
35624             this.validate();
35625         }
35626     },
35627
35628     adjustSize : function(w, h){
35629         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35630         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35631         return s;
35632     },
35633
35634     adjustWidth : function(tag, w){
35635         tag = tag.toLowerCase();
35636         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35637             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35638                 if(tag == 'input'){
35639                     return w + 2;
35640                 }
35641                 if(tag = 'textarea'){
35642                     return w-2;
35643                 }
35644             }else if(Roo.isOpera){
35645                 if(tag == 'input'){
35646                     return w + 2;
35647                 }
35648                 if(tag = 'textarea'){
35649                     return w-2;
35650                 }
35651             }
35652         }
35653         return w;
35654     }
35655 });
35656
35657
35658 // anything other than normal should be considered experimental
35659 Roo.form.Field.msgFx = {
35660     normal : {
35661         show: function(msgEl, f){
35662             msgEl.setDisplayed('block');
35663         },
35664
35665         hide : function(msgEl, f){
35666             msgEl.setDisplayed(false).update('');
35667         }
35668     },
35669
35670     slide : {
35671         show: function(msgEl, f){
35672             msgEl.slideIn('t', {stopFx:true});
35673         },
35674
35675         hide : function(msgEl, f){
35676             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35677         }
35678     },
35679
35680     slideRight : {
35681         show: function(msgEl, f){
35682             msgEl.fixDisplay();
35683             msgEl.alignTo(f.el, 'tl-tr');
35684             msgEl.slideIn('l', {stopFx:true});
35685         },
35686
35687         hide : function(msgEl, f){
35688             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35689         }
35690     }
35691 };/*
35692  * Based on:
35693  * Ext JS Library 1.1.1
35694  * Copyright(c) 2006-2007, Ext JS, LLC.
35695  *
35696  * Originally Released Under LGPL - original licence link has changed is not relivant.
35697  *
35698  * Fork - LGPL
35699  * <script type="text/javascript">
35700  */
35701  
35702
35703 /**
35704  * @class Roo.form.TextField
35705  * @extends Roo.form.Field
35706  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35707  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35708  * @constructor
35709  * Creates a new TextField
35710  * @param {Object} config Configuration options
35711  */
35712 Roo.form.TextField = function(config){
35713     Roo.form.TextField.superclass.constructor.call(this, config);
35714     this.addEvents({
35715         /**
35716          * @event autosize
35717          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35718          * according to the default logic, but this event provides a hook for the developer to apply additional
35719          * logic at runtime to resize the field if needed.
35720              * @param {Roo.form.Field} this This text field
35721              * @param {Number} width The new field width
35722              */
35723         autosize : true
35724     });
35725 };
35726
35727 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35728     /**
35729      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35730      */
35731     grow : false,
35732     /**
35733      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35734      */
35735     growMin : 30,
35736     /**
35737      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35738      */
35739     growMax : 800,
35740     /**
35741      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35742      */
35743     vtype : null,
35744     /**
35745      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35746      */
35747     maskRe : null,
35748     /**
35749      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35750      */
35751     disableKeyFilter : false,
35752     /**
35753      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35754      */
35755     allowBlank : true,
35756     /**
35757      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35758      */
35759     minLength : 0,
35760     /**
35761      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35762      */
35763     maxLength : Number.MAX_VALUE,
35764     /**
35765      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35766      */
35767     minLengthText : "The minimum length for this field is {0}",
35768     /**
35769      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35770      */
35771     maxLengthText : "The maximum length for this field is {0}",
35772     /**
35773      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35774      */
35775     selectOnFocus : false,
35776     /**
35777      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35778      */
35779     blankText : "This field is required",
35780     /**
35781      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35782      * If available, this function will be called only after the basic validators all return true, and will be passed the
35783      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35784      */
35785     validator : null,
35786     /**
35787      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35788      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35789      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35790      */
35791     regex : null,
35792     /**
35793      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35794      */
35795     regexText : "",
35796     /**
35797      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35798      */
35799     emptyText : null,
35800     /**
35801      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35802      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35803      */
35804     emptyClass : 'x-form-empty-field',
35805
35806     // private
35807     initEvents : function(){
35808         Roo.form.TextField.superclass.initEvents.call(this);
35809         if(this.validationEvent == 'keyup'){
35810             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35811             this.el.on('keyup', this.filterValidation, this);
35812         }
35813         else if(this.validationEvent !== false){
35814             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35815         }
35816         if(this.selectOnFocus || this.emptyText){
35817             this.on("focus", this.preFocus, this);
35818             if(this.emptyText){
35819                 this.on('blur', this.postBlur, this);
35820                 this.applyEmptyText();
35821             }
35822         }
35823         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35824             this.el.on("keypress", this.filterKeys, this);
35825         }
35826         if(this.grow){
35827             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35828             this.el.on("click", this.autoSize,  this);
35829         }
35830     },
35831
35832     processValue : function(value){
35833         if(this.stripCharsRe){
35834             var newValue = value.replace(this.stripCharsRe, '');
35835             if(newValue !== value){
35836                 this.setRawValue(newValue);
35837                 return newValue;
35838             }
35839         }
35840         return value;
35841     },
35842
35843     filterValidation : function(e){
35844         if(!e.isNavKeyPress()){
35845             this.validationTask.delay(this.validationDelay);
35846         }
35847     },
35848
35849     // private
35850     onKeyUp : function(e){
35851         if(!e.isNavKeyPress()){
35852             this.autoSize();
35853         }
35854     },
35855
35856     /**
35857      * Resets the current field value to the originally-loaded value and clears any validation messages.
35858      * Also adds emptyText and emptyClass if the original value was blank.
35859      */
35860     reset : function(){
35861         Roo.form.TextField.superclass.reset.call(this);
35862         this.applyEmptyText();
35863     },
35864
35865     applyEmptyText : function(){
35866         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35867             this.setRawValue(this.emptyText);
35868             this.el.addClass(this.emptyClass);
35869         }
35870     },
35871
35872     // private
35873     preFocus : function(){
35874         if(this.emptyText){
35875             if(this.el.dom.value == this.emptyText){
35876                 this.setRawValue('');
35877             }
35878             this.el.removeClass(this.emptyClass);
35879         }
35880         if(this.selectOnFocus){
35881             this.el.dom.select();
35882         }
35883     },
35884
35885     // private
35886     postBlur : function(){
35887         this.applyEmptyText();
35888     },
35889
35890     // private
35891     filterKeys : function(e){
35892         var k = e.getKey();
35893         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35894             return;
35895         }
35896         var c = e.getCharCode(), cc = String.fromCharCode(c);
35897         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35898             return;
35899         }
35900         if(!this.maskRe.test(cc)){
35901             e.stopEvent();
35902         }
35903     },
35904
35905     setValue : function(v){
35906         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35907             this.el.removeClass(this.emptyClass);
35908         }
35909         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35910         this.applyEmptyText();
35911         this.autoSize();
35912     },
35913
35914     /**
35915      * Validates a value according to the field's validation rules and marks the field as invalid
35916      * if the validation fails
35917      * @param {Mixed} value The value to validate
35918      * @return {Boolean} True if the value is valid, else false
35919      */
35920     validateValue : function(value){
35921         if(value.length < 1 || value === this.emptyText){ // if it's blank
35922              if(this.allowBlank){
35923                 this.clearInvalid();
35924                 return true;
35925              }else{
35926                 this.markInvalid(this.blankText);
35927                 return false;
35928              }
35929         }
35930         if(value.length < this.minLength){
35931             this.markInvalid(String.format(this.minLengthText, this.minLength));
35932             return false;
35933         }
35934         if(value.length > this.maxLength){
35935             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35936             return false;
35937         }
35938         if(this.vtype){
35939             var vt = Roo.form.VTypes;
35940             if(!vt[this.vtype](value, this)){
35941                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35942                 return false;
35943             }
35944         }
35945         if(typeof this.validator == "function"){
35946             var msg = this.validator(value);
35947             if(msg !== true){
35948                 this.markInvalid(msg);
35949                 return false;
35950             }
35951         }
35952         if(this.regex && !this.regex.test(value)){
35953             this.markInvalid(this.regexText);
35954             return false;
35955         }
35956         return true;
35957     },
35958
35959     /**
35960      * Selects text in this field
35961      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35962      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35963      */
35964     selectText : function(start, end){
35965         var v = this.getRawValue();
35966         if(v.length > 0){
35967             start = start === undefined ? 0 : start;
35968             end = end === undefined ? v.length : end;
35969             var d = this.el.dom;
35970             if(d.setSelectionRange){
35971                 d.setSelectionRange(start, end);
35972             }else if(d.createTextRange){
35973                 var range = d.createTextRange();
35974                 range.moveStart("character", start);
35975                 range.moveEnd("character", v.length-end);
35976                 range.select();
35977             }
35978         }
35979     },
35980
35981     /**
35982      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35983      * This only takes effect if grow = true, and fires the autosize event.
35984      */
35985     autoSize : function(){
35986         if(!this.grow || !this.rendered){
35987             return;
35988         }
35989         if(!this.metrics){
35990             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35991         }
35992         var el = this.el;
35993         var v = el.dom.value;
35994         var d = document.createElement('div');
35995         d.appendChild(document.createTextNode(v));
35996         v = d.innerHTML;
35997         d = null;
35998         v += "&#160;";
35999         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36000         this.el.setWidth(w);
36001         this.fireEvent("autosize", this, w);
36002     }
36003 });/*
36004  * Based on:
36005  * Ext JS Library 1.1.1
36006  * Copyright(c) 2006-2007, Ext JS, LLC.
36007  *
36008  * Originally Released Under LGPL - original licence link has changed is not relivant.
36009  *
36010  * Fork - LGPL
36011  * <script type="text/javascript">
36012  */
36013  
36014 /**
36015  * @class Roo.form.Hidden
36016  * @extends Roo.form.TextField
36017  * Simple Hidden element used on forms 
36018  * 
36019  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36020  * 
36021  * @constructor
36022  * Creates a new Hidden form element.
36023  * @param {Object} config Configuration options
36024  */
36025
36026
36027
36028 // easy hidden field...
36029 Roo.form.Hidden = function(config){
36030     Roo.form.Hidden.superclass.constructor.call(this, config);
36031 };
36032   
36033 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36034     fieldLabel:      '',
36035     inputType:      'hidden',
36036     width:          50,
36037     allowBlank:     true,
36038     labelSeparator: '',
36039     hidden:         true,
36040     itemCls :       'x-form-item-display-none'
36041
36042
36043 });
36044
36045
36046 /*
36047  * Based on:
36048  * Ext JS Library 1.1.1
36049  * Copyright(c) 2006-2007, Ext JS, LLC.
36050  *
36051  * Originally Released Under LGPL - original licence link has changed is not relivant.
36052  *
36053  * Fork - LGPL
36054  * <script type="text/javascript">
36055  */
36056  
36057 /**
36058  * @class Roo.form.TriggerField
36059  * @extends Roo.form.TextField
36060  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36061  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36062  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36063  * for which you can provide a custom implementation.  For example:
36064  * <pre><code>
36065 var trigger = new Roo.form.TriggerField();
36066 trigger.onTriggerClick = myTriggerFn;
36067 trigger.applyTo('my-field');
36068 </code></pre>
36069  *
36070  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36071  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36072  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36073  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36074  * @constructor
36075  * Create a new TriggerField.
36076  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36077  * to the base TextField)
36078  */
36079 Roo.form.TriggerField = function(config){
36080     this.mimicing = false;
36081     Roo.form.TriggerField.superclass.constructor.call(this, config);
36082 };
36083
36084 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36085     /**
36086      * @cfg {String} triggerClass A CSS class to apply to the trigger
36087      */
36088     /**
36089      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36090      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36091      */
36092     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36093     /**
36094      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36095      */
36096     hideTrigger:false,
36097
36098     /** @cfg {Boolean} grow @hide */
36099     /** @cfg {Number} growMin @hide */
36100     /** @cfg {Number} growMax @hide */
36101
36102     /**
36103      * @hide 
36104      * @method
36105      */
36106     autoSize: Roo.emptyFn,
36107     // private
36108     monitorTab : true,
36109     // private
36110     deferHeight : true,
36111
36112     
36113     actionMode : 'wrap',
36114     // private
36115     onResize : function(w, h){
36116         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36117         if(typeof w == 'number'){
36118             var x = w - this.trigger.getWidth();
36119             this.el.setWidth(this.adjustWidth('input', x));
36120             this.trigger.setStyle('left', x+'px');
36121         }
36122     },
36123
36124     // private
36125     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36126
36127     // private
36128     getResizeEl : function(){
36129         return this.wrap;
36130     },
36131
36132     // private
36133     getPositionEl : function(){
36134         return this.wrap;
36135     },
36136
36137     // private
36138     alignErrorIcon : function(){
36139         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36140     },
36141
36142     // private
36143     onRender : function(ct, position){
36144         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36145         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36146         this.trigger = this.wrap.createChild(this.triggerConfig ||
36147                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36148         if(this.hideTrigger){
36149             this.trigger.setDisplayed(false);
36150         }
36151         this.initTrigger();
36152         if(!this.width){
36153             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36154         }
36155     },
36156
36157     // private
36158     initTrigger : function(){
36159         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36160         this.trigger.addClassOnOver('x-form-trigger-over');
36161         this.trigger.addClassOnClick('x-form-trigger-click');
36162     },
36163
36164     // private
36165     onDestroy : function(){
36166         if(this.trigger){
36167             this.trigger.removeAllListeners();
36168             this.trigger.remove();
36169         }
36170         if(this.wrap){
36171             this.wrap.remove();
36172         }
36173         Roo.form.TriggerField.superclass.onDestroy.call(this);
36174     },
36175
36176     // private
36177     onFocus : function(){
36178         Roo.form.TriggerField.superclass.onFocus.call(this);
36179         if(!this.mimicing){
36180             this.wrap.addClass('x-trigger-wrap-focus');
36181             this.mimicing = true;
36182             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36183             if(this.monitorTab){
36184                 this.el.on("keydown", this.checkTab, this);
36185             }
36186         }
36187     },
36188
36189     // private
36190     checkTab : function(e){
36191         if(e.getKey() == e.TAB){
36192             this.triggerBlur();
36193         }
36194     },
36195
36196     // private
36197     onBlur : function(){
36198         // do nothing
36199     },
36200
36201     // private
36202     mimicBlur : function(e, t){
36203         if(!this.wrap.contains(t) && this.validateBlur()){
36204             this.triggerBlur();
36205         }
36206     },
36207
36208     // private
36209     triggerBlur : function(){
36210         this.mimicing = false;
36211         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36212         if(this.monitorTab){
36213             this.el.un("keydown", this.checkTab, this);
36214         }
36215         this.wrap.removeClass('x-trigger-wrap-focus');
36216         Roo.form.TriggerField.superclass.onBlur.call(this);
36217     },
36218
36219     // private
36220     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36221     validateBlur : function(e, t){
36222         return true;
36223     },
36224
36225     // private
36226     onDisable : function(){
36227         Roo.form.TriggerField.superclass.onDisable.call(this);
36228         if(this.wrap){
36229             this.wrap.addClass('x-item-disabled');
36230         }
36231     },
36232
36233     // private
36234     onEnable : function(){
36235         Roo.form.TriggerField.superclass.onEnable.call(this);
36236         if(this.wrap){
36237             this.wrap.removeClass('x-item-disabled');
36238         }
36239     },
36240
36241     // private
36242     onShow : function(){
36243         var ae = this.getActionEl();
36244         
36245         if(ae){
36246             ae.dom.style.display = '';
36247             ae.dom.style.visibility = 'visible';
36248         }
36249     },
36250
36251     // private
36252     
36253     onHide : function(){
36254         var ae = this.getActionEl();
36255         ae.dom.style.display = 'none';
36256     },
36257
36258     /**
36259      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36260      * by an implementing function.
36261      * @method
36262      * @param {EventObject} e
36263      */
36264     onTriggerClick : Roo.emptyFn
36265 });
36266
36267 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36268 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36269 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36270 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36271     initComponent : function(){
36272         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36273
36274         this.triggerConfig = {
36275             tag:'span', cls:'x-form-twin-triggers', cn:[
36276             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36277             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36278         ]};
36279     },
36280
36281     getTrigger : function(index){
36282         return this.triggers[index];
36283     },
36284
36285     initTrigger : function(){
36286         var ts = this.trigger.select('.x-form-trigger', true);
36287         this.wrap.setStyle('overflow', 'hidden');
36288         var triggerField = this;
36289         ts.each(function(t, all, index){
36290             t.hide = function(){
36291                 var w = triggerField.wrap.getWidth();
36292                 this.dom.style.display = 'none';
36293                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36294             };
36295             t.show = function(){
36296                 var w = triggerField.wrap.getWidth();
36297                 this.dom.style.display = '';
36298                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36299             };
36300             var triggerIndex = 'Trigger'+(index+1);
36301
36302             if(this['hide'+triggerIndex]){
36303                 t.dom.style.display = 'none';
36304             }
36305             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36306             t.addClassOnOver('x-form-trigger-over');
36307             t.addClassOnClick('x-form-trigger-click');
36308         }, this);
36309         this.triggers = ts.elements;
36310     },
36311
36312     onTrigger1Click : Roo.emptyFn,
36313     onTrigger2Click : Roo.emptyFn
36314 });/*
36315  * Based on:
36316  * Ext JS Library 1.1.1
36317  * Copyright(c) 2006-2007, Ext JS, LLC.
36318  *
36319  * Originally Released Under LGPL - original licence link has changed is not relivant.
36320  *
36321  * Fork - LGPL
36322  * <script type="text/javascript">
36323  */
36324  
36325 /**
36326  * @class Roo.form.TextArea
36327  * @extends Roo.form.TextField
36328  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36329  * support for auto-sizing.
36330  * @constructor
36331  * Creates a new TextArea
36332  * @param {Object} config Configuration options
36333  */
36334 Roo.form.TextArea = function(config){
36335     Roo.form.TextArea.superclass.constructor.call(this, config);
36336     // these are provided exchanges for backwards compat
36337     // minHeight/maxHeight were replaced by growMin/growMax to be
36338     // compatible with TextField growing config values
36339     if(this.minHeight !== undefined){
36340         this.growMin = this.minHeight;
36341     }
36342     if(this.maxHeight !== undefined){
36343         this.growMax = this.maxHeight;
36344     }
36345 };
36346
36347 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36348     /**
36349      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36350      */
36351     growMin : 60,
36352     /**
36353      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36354      */
36355     growMax: 1000,
36356     /**
36357      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36358      * in the field (equivalent to setting overflow: hidden, defaults to false)
36359      */
36360     preventScrollbars: false,
36361     /**
36362      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36363      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36364      */
36365
36366     // private
36367     onRender : function(ct, position){
36368         if(!this.el){
36369             this.defaultAutoCreate = {
36370                 tag: "textarea",
36371                 style:"width:300px;height:60px;",
36372                 autocomplete: "off"
36373             };
36374         }
36375         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36376         if(this.grow){
36377             this.textSizeEl = Roo.DomHelper.append(document.body, {
36378                 tag: "pre", cls: "x-form-grow-sizer"
36379             });
36380             if(this.preventScrollbars){
36381                 this.el.setStyle("overflow", "hidden");
36382             }
36383             this.el.setHeight(this.growMin);
36384         }
36385     },
36386
36387     onDestroy : function(){
36388         if(this.textSizeEl){
36389             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36390         }
36391         Roo.form.TextArea.superclass.onDestroy.call(this);
36392     },
36393
36394     // private
36395     onKeyUp : function(e){
36396         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36397             this.autoSize();
36398         }
36399     },
36400
36401     /**
36402      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36403      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36404      */
36405     autoSize : function(){
36406         if(!this.grow || !this.textSizeEl){
36407             return;
36408         }
36409         var el = this.el;
36410         var v = el.dom.value;
36411         var ts = this.textSizeEl;
36412
36413         ts.innerHTML = '';
36414         ts.appendChild(document.createTextNode(v));
36415         v = ts.innerHTML;
36416
36417         Roo.fly(ts).setWidth(this.el.getWidth());
36418         if(v.length < 1){
36419             v = "&#160;&#160;";
36420         }else{
36421             if(Roo.isIE){
36422                 v = v.replace(/\n/g, '<p>&#160;</p>');
36423             }
36424             v += "&#160;\n&#160;";
36425         }
36426         ts.innerHTML = v;
36427         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36428         if(h != this.lastHeight){
36429             this.lastHeight = h;
36430             this.el.setHeight(h);
36431             this.fireEvent("autosize", this, h);
36432         }
36433     }
36434 });/*
36435  * Based on:
36436  * Ext JS Library 1.1.1
36437  * Copyright(c) 2006-2007, Ext JS, LLC.
36438  *
36439  * Originally Released Under LGPL - original licence link has changed is not relivant.
36440  *
36441  * Fork - LGPL
36442  * <script type="text/javascript">
36443  */
36444  
36445
36446 /**
36447  * @class Roo.form.NumberField
36448  * @extends Roo.form.TextField
36449  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36450  * @constructor
36451  * Creates a new NumberField
36452  * @param {Object} config Configuration options
36453  */
36454 Roo.form.NumberField = function(config){
36455     Roo.form.NumberField.superclass.constructor.call(this, config);
36456 };
36457
36458 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36459     /**
36460      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36461      */
36462     fieldClass: "x-form-field x-form-num-field",
36463     /**
36464      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36465      */
36466     allowDecimals : true,
36467     /**
36468      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36469      */
36470     decimalSeparator : ".",
36471     /**
36472      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36473      */
36474     decimalPrecision : 2,
36475     /**
36476      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36477      */
36478     allowNegative : true,
36479     /**
36480      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36481      */
36482     minValue : Number.NEGATIVE_INFINITY,
36483     /**
36484      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36485      */
36486     maxValue : Number.MAX_VALUE,
36487     /**
36488      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36489      */
36490     minText : "The minimum value for this field is {0}",
36491     /**
36492      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36493      */
36494     maxText : "The maximum value for this field is {0}",
36495     /**
36496      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36497      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36498      */
36499     nanText : "{0} is not a valid number",
36500
36501     // private
36502     initEvents : function(){
36503         Roo.form.NumberField.superclass.initEvents.call(this);
36504         var allowed = "0123456789";
36505         if(this.allowDecimals){
36506             allowed += this.decimalSeparator;
36507         }
36508         if(this.allowNegative){
36509             allowed += "-";
36510         }
36511         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36512         var keyPress = function(e){
36513             var k = e.getKey();
36514             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36515                 return;
36516             }
36517             var c = e.getCharCode();
36518             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36519                 e.stopEvent();
36520             }
36521         };
36522         this.el.on("keypress", keyPress, this);
36523     },
36524
36525     // private
36526     validateValue : function(value){
36527         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36528             return false;
36529         }
36530         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36531              return true;
36532         }
36533         var num = this.parseValue(value);
36534         if(isNaN(num)){
36535             this.markInvalid(String.format(this.nanText, value));
36536             return false;
36537         }
36538         if(num < this.minValue){
36539             this.markInvalid(String.format(this.minText, this.minValue));
36540             return false;
36541         }
36542         if(num > this.maxValue){
36543             this.markInvalid(String.format(this.maxText, this.maxValue));
36544             return false;
36545         }
36546         return true;
36547     },
36548
36549     getValue : function(){
36550         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36551     },
36552
36553     // private
36554     parseValue : function(value){
36555         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36556         return isNaN(value) ? '' : value;
36557     },
36558
36559     // private
36560     fixPrecision : function(value){
36561         var nan = isNaN(value);
36562         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36563             return nan ? '' : value;
36564         }
36565         return parseFloat(value).toFixed(this.decimalPrecision);
36566     },
36567
36568     setValue : function(v){
36569         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36570     },
36571
36572     // private
36573     decimalPrecisionFcn : function(v){
36574         return Math.floor(v);
36575     },
36576
36577     beforeBlur : function(){
36578         var v = this.parseValue(this.getRawValue());
36579         if(v){
36580             this.setValue(this.fixPrecision(v));
36581         }
36582     }
36583 });/*
36584  * Based on:
36585  * Ext JS Library 1.1.1
36586  * Copyright(c) 2006-2007, Ext JS, LLC.
36587  *
36588  * Originally Released Under LGPL - original licence link has changed is not relivant.
36589  *
36590  * Fork - LGPL
36591  * <script type="text/javascript">
36592  */
36593  
36594 /**
36595  * @class Roo.form.DateField
36596  * @extends Roo.form.TriggerField
36597  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36598 * @constructor
36599 * Create a new DateField
36600 * @param {Object} config
36601  */
36602 Roo.form.DateField = function(config){
36603     Roo.form.DateField.superclass.constructor.call(this, config);
36604     
36605       this.addEvents({
36606          
36607         /**
36608          * @event select
36609          * Fires when a date is selected
36610              * @param {Roo.form.DateField} combo This combo box
36611              * @param {Date} date The date selected
36612              */
36613         'select' : true
36614          
36615     });
36616     
36617     
36618     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36619     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36620     this.ddMatch = null;
36621     if(this.disabledDates){
36622         var dd = this.disabledDates;
36623         var re = "(?:";
36624         for(var i = 0; i < dd.length; i++){
36625             re += dd[i];
36626             if(i != dd.length-1) re += "|";
36627         }
36628         this.ddMatch = new RegExp(re + ")");
36629     }
36630 };
36631
36632 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36633     /**
36634      * @cfg {String} format
36635      * The default date format string which can be overriden for localization support.  The format must be
36636      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36637      */
36638     format : "m/d/y",
36639     /**
36640      * @cfg {String} altFormats
36641      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36642      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36643      */
36644     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36645     /**
36646      * @cfg {Array} disabledDays
36647      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36648      */
36649     disabledDays : null,
36650     /**
36651      * @cfg {String} disabledDaysText
36652      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36653      */
36654     disabledDaysText : "Disabled",
36655     /**
36656      * @cfg {Array} disabledDates
36657      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36658      * expression so they are very powerful. Some examples:
36659      * <ul>
36660      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36661      * <li>["03/08", "09/16"] would disable those days for every year</li>
36662      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36663      * <li>["03/../2006"] would disable every day in March 2006</li>
36664      * <li>["^03"] would disable every day in every March</li>
36665      * </ul>
36666      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36667      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36668      */
36669     disabledDates : null,
36670     /**
36671      * @cfg {String} disabledDatesText
36672      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36673      */
36674     disabledDatesText : "Disabled",
36675     /**
36676      * @cfg {Date/String} minValue
36677      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36678      * valid format (defaults to null).
36679      */
36680     minValue : null,
36681     /**
36682      * @cfg {Date/String} maxValue
36683      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36684      * valid format (defaults to null).
36685      */
36686     maxValue : null,
36687     /**
36688      * @cfg {String} minText
36689      * The error text to display when the date in the cell is before minValue (defaults to
36690      * 'The date in this field must be after {minValue}').
36691      */
36692     minText : "The date in this field must be equal to or after {0}",
36693     /**
36694      * @cfg {String} maxText
36695      * The error text to display when the date in the cell is after maxValue (defaults to
36696      * 'The date in this field must be before {maxValue}').
36697      */
36698     maxText : "The date in this field must be equal to or before {0}",
36699     /**
36700      * @cfg {String} invalidText
36701      * The error text to display when the date in the field is invalid (defaults to
36702      * '{value} is not a valid date - it must be in the format {format}').
36703      */
36704     invalidText : "{0} is not a valid date - it must be in the format {1}",
36705     /**
36706      * @cfg {String} triggerClass
36707      * An additional CSS class used to style the trigger button.  The trigger will always get the
36708      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36709      * which displays a calendar icon).
36710      */
36711     triggerClass : 'x-form-date-trigger',
36712     
36713
36714     /**
36715      * @cfg {bool} useIso
36716      * if enabled, then the date field will use a hidden field to store the 
36717      * real value as iso formated date. default (false)
36718      */ 
36719     useIso : false,
36720     /**
36721      * @cfg {String/Object} autoCreate
36722      * A DomHelper element spec, or true for a default element spec (defaults to
36723      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36724      */ 
36725     // private
36726     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36727     
36728     // private
36729     hiddenField: false,
36730     
36731     onRender : function(ct, position)
36732     {
36733         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36734         if (this.useIso) {
36735             this.el.dom.removeAttribute('name'); 
36736             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36737                     'before', true);
36738             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36739             // prevent input submission
36740             this.hiddenName = this.name;
36741         }
36742             
36743             
36744     },
36745     
36746     // private
36747     validateValue : function(value)
36748     {
36749         value = this.formatDate(value);
36750         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36751             return false;
36752         }
36753         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36754              return true;
36755         }
36756         var svalue = value;
36757         value = this.parseDate(value);
36758         if(!value){
36759             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36760             return false;
36761         }
36762         var time = value.getTime();
36763         if(this.minValue && time < this.minValue.getTime()){
36764             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36765             return false;
36766         }
36767         if(this.maxValue && time > this.maxValue.getTime()){
36768             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36769             return false;
36770         }
36771         if(this.disabledDays){
36772             var day = value.getDay();
36773             for(var i = 0; i < this.disabledDays.length; i++) {
36774                 if(day === this.disabledDays[i]){
36775                     this.markInvalid(this.disabledDaysText);
36776                     return false;
36777                 }
36778             }
36779         }
36780         var fvalue = this.formatDate(value);
36781         if(this.ddMatch && this.ddMatch.test(fvalue)){
36782             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36783             return false;
36784         }
36785         return true;
36786     },
36787
36788     // private
36789     // Provides logic to override the default TriggerField.validateBlur which just returns true
36790     validateBlur : function(){
36791         return !this.menu || !this.menu.isVisible();
36792     },
36793
36794     /**
36795      * Returns the current date value of the date field.
36796      * @return {Date} The date value
36797      */
36798     getValue : function(){
36799         
36800         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36801     },
36802
36803     /**
36804      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36805      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36806      * (the default format used is "m/d/y").
36807      * <br />Usage:
36808      * <pre><code>
36809 //All of these calls set the same date value (May 4, 2006)
36810
36811 //Pass a date object:
36812 var dt = new Date('5/4/06');
36813 dateField.setValue(dt);
36814
36815 //Pass a date string (default format):
36816 dateField.setValue('5/4/06');
36817
36818 //Pass a date string (custom format):
36819 dateField.format = 'Y-m-d';
36820 dateField.setValue('2006-5-4');
36821 </code></pre>
36822      * @param {String/Date} date The date or valid date string
36823      */
36824     setValue : function(date){
36825         if (this.hiddenField) {
36826             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36827         }
36828         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36829     },
36830
36831     // private
36832     parseDate : function(value){
36833         if(!value || value instanceof Date){
36834             return value;
36835         }
36836         var v = Date.parseDate(value, this.format);
36837         if(!v && this.altFormats){
36838             if(!this.altFormatsArray){
36839                 this.altFormatsArray = this.altFormats.split("|");
36840             }
36841             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36842                 v = Date.parseDate(value, this.altFormatsArray[i]);
36843             }
36844         }
36845         return v;
36846     },
36847
36848     // private
36849     formatDate : function(date, fmt){
36850         return (!date || !(date instanceof Date)) ?
36851                date : date.dateFormat(fmt || this.format);
36852     },
36853
36854     // private
36855     menuListeners : {
36856         select: function(m, d){
36857             this.setValue(d);
36858             this.fireEvent('select', this, d);
36859         },
36860         show : function(){ // retain focus styling
36861             this.onFocus();
36862         },
36863         hide : function(){
36864             this.focus.defer(10, this);
36865             var ml = this.menuListeners;
36866             this.menu.un("select", ml.select,  this);
36867             this.menu.un("show", ml.show,  this);
36868             this.menu.un("hide", ml.hide,  this);
36869         }
36870     },
36871
36872     // private
36873     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36874     onTriggerClick : function(){
36875         if(this.disabled){
36876             return;
36877         }
36878         if(this.menu == null){
36879             this.menu = new Roo.menu.DateMenu();
36880         }
36881         Roo.apply(this.menu.picker,  {
36882             showClear: this.allowBlank,
36883             minDate : this.minValue,
36884             maxDate : this.maxValue,
36885             disabledDatesRE : this.ddMatch,
36886             disabledDatesText : this.disabledDatesText,
36887             disabledDays : this.disabledDays,
36888             disabledDaysText : this.disabledDaysText,
36889             format : this.format,
36890             minText : String.format(this.minText, this.formatDate(this.minValue)),
36891             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36892         });
36893         this.menu.on(Roo.apply({}, this.menuListeners, {
36894             scope:this
36895         }));
36896         this.menu.picker.setValue(this.getValue() || new Date());
36897         this.menu.show(this.el, "tl-bl?");
36898     },
36899
36900     beforeBlur : function(){
36901         var v = this.parseDate(this.getRawValue());
36902         if(v){
36903             this.setValue(v);
36904         }
36905     }
36906
36907     /** @cfg {Boolean} grow @hide */
36908     /** @cfg {Number} growMin @hide */
36909     /** @cfg {Number} growMax @hide */
36910     /**
36911      * @hide
36912      * @method autoSize
36913      */
36914 });/*
36915  * Based on:
36916  * Ext JS Library 1.1.1
36917  * Copyright(c) 2006-2007, Ext JS, LLC.
36918  *
36919  * Originally Released Under LGPL - original licence link has changed is not relivant.
36920  *
36921  * Fork - LGPL
36922  * <script type="text/javascript">
36923  */
36924  
36925
36926 /**
36927  * @class Roo.form.ComboBox
36928  * @extends Roo.form.TriggerField
36929  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36930  * @constructor
36931  * Create a new ComboBox.
36932  * @param {Object} config Configuration options
36933  */
36934 Roo.form.ComboBox = function(config){
36935     Roo.form.ComboBox.superclass.constructor.call(this, config);
36936     this.addEvents({
36937         /**
36938          * @event expand
36939          * Fires when the dropdown list is expanded
36940              * @param {Roo.form.ComboBox} combo This combo box
36941              */
36942         'expand' : true,
36943         /**
36944          * @event collapse
36945          * Fires when the dropdown list is collapsed
36946              * @param {Roo.form.ComboBox} combo This combo box
36947              */
36948         'collapse' : true,
36949         /**
36950          * @event beforeselect
36951          * Fires before a list item is selected. Return false to cancel the selection.
36952              * @param {Roo.form.ComboBox} combo This combo box
36953              * @param {Roo.data.Record} record The data record returned from the underlying store
36954              * @param {Number} index The index of the selected item in the dropdown list
36955              */
36956         'beforeselect' : true,
36957         /**
36958          * @event select
36959          * Fires when a list item is selected
36960              * @param {Roo.form.ComboBox} combo This combo box
36961              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36962              * @param {Number} index The index of the selected item in the dropdown list
36963              */
36964         'select' : true,
36965         /**
36966          * @event beforequery
36967          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36968          * The event object passed has these properties:
36969              * @param {Roo.form.ComboBox} combo This combo box
36970              * @param {String} query The query
36971              * @param {Boolean} forceAll true to force "all" query
36972              * @param {Boolean} cancel true to cancel the query
36973              * @param {Object} e The query event object
36974              */
36975         'beforequery': true,
36976          /**
36977          * @event add
36978          * Fires when the 'add' icon is pressed (add a listener to enable add button)
36979              * @param {Roo.form.ComboBox} combo This combo box
36980              */
36981         'add' : true,
36982         /**
36983          * @event edit
36984          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
36985              * @param {Roo.form.ComboBox} combo This combo box
36986              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
36987              */
36988         'edit' : true
36989         
36990         
36991     });
36992     if(this.transform){
36993         this.allowDomMove = false;
36994         var s = Roo.getDom(this.transform);
36995         if(!this.hiddenName){
36996             this.hiddenName = s.name;
36997         }
36998         if(!this.store){
36999             this.mode = 'local';
37000             var d = [], opts = s.options;
37001             for(var i = 0, len = opts.length;i < len; i++){
37002                 var o = opts[i];
37003                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37004                 if(o.selected) {
37005                     this.value = value;
37006                 }
37007                 d.push([value, o.text]);
37008             }
37009             this.store = new Roo.data.SimpleStore({
37010                 'id': 0,
37011                 fields: ['value', 'text'],
37012                 data : d
37013             });
37014             this.valueField = 'value';
37015             this.displayField = 'text';
37016         }
37017         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37018         if(!this.lazyRender){
37019             this.target = true;
37020             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37021             s.parentNode.removeChild(s); // remove it
37022             this.render(this.el.parentNode);
37023         }else{
37024             s.parentNode.removeChild(s); // remove it
37025         }
37026
37027     }
37028     if (this.store) {
37029         this.store = Roo.factory(this.store, Roo.data);
37030     }
37031     
37032     this.selectedIndex = -1;
37033     if(this.mode == 'local'){
37034         if(config.queryDelay === undefined){
37035             this.queryDelay = 10;
37036         }
37037         if(config.minChars === undefined){
37038             this.minChars = 0;
37039         }
37040     }
37041 };
37042
37043 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37044     /**
37045      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37046      */
37047     /**
37048      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37049      * rendering into an Roo.Editor, defaults to false)
37050      */
37051     /**
37052      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37053      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37054      */
37055     /**
37056      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37057      */
37058     /**
37059      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37060      * the dropdown list (defaults to undefined, with no header element)
37061      */
37062
37063      /**
37064      * @cfg {String/Roo.Template} tpl The template to use to render the output
37065      */
37066      
37067     // private
37068     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37069     /**
37070      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37071      */
37072     listWidth: undefined,
37073     /**
37074      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37075      * mode = 'remote' or 'text' if mode = 'local')
37076      */
37077     displayField: undefined,
37078     /**
37079      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37080      * mode = 'remote' or 'value' if mode = 'local'). 
37081      * Note: use of a valueField requires the user make a selection
37082      * in order for a value to be mapped.
37083      */
37084     valueField: undefined,
37085     /**
37086      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37087      * field's data value (defaults to the underlying DOM element's name)
37088      */
37089     hiddenName: undefined,
37090     /**
37091      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37092      */
37093     listClass: '',
37094     /**
37095      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37096      */
37097     selectedClass: 'x-combo-selected',
37098     /**
37099      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37100      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37101      * which displays a downward arrow icon).
37102      */
37103     triggerClass : 'x-form-arrow-trigger',
37104     /**
37105      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37106      */
37107     shadow:'sides',
37108     /**
37109      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37110      * anchor positions (defaults to 'tl-bl')
37111      */
37112     listAlign: 'tl-bl?',
37113     /**
37114      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37115      */
37116     maxHeight: 300,
37117     /**
37118      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37119      * query specified by the allQuery config option (defaults to 'query')
37120      */
37121     triggerAction: 'query',
37122     /**
37123      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37124      * (defaults to 4, does not apply if editable = false)
37125      */
37126     minChars : 4,
37127     /**
37128      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37129      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37130      */
37131     typeAhead: false,
37132     /**
37133      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37134      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37135      */
37136     queryDelay: 500,
37137     /**
37138      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37139      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37140      */
37141     pageSize: 0,
37142     /**
37143      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37144      * when editable = true (defaults to false)
37145      */
37146     selectOnFocus:false,
37147     /**
37148      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37149      */
37150     queryParam: 'query',
37151     /**
37152      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37153      * when mode = 'remote' (defaults to 'Loading...')
37154      */
37155     loadingText: 'Loading...',
37156     /**
37157      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37158      */
37159     resizable: false,
37160     /**
37161      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37162      */
37163     handleHeight : 8,
37164     /**
37165      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37166      * traditional select (defaults to true)
37167      */
37168     editable: true,
37169     /**
37170      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37171      */
37172     allQuery: '',
37173     /**
37174      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37175      */
37176     mode: 'remote',
37177     /**
37178      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37179      * listWidth has a higher value)
37180      */
37181     minListWidth : 70,
37182     /**
37183      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37184      * allow the user to set arbitrary text into the field (defaults to false)
37185      */
37186     forceSelection:false,
37187     /**
37188      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37189      * if typeAhead = true (defaults to 250)
37190      */
37191     typeAheadDelay : 250,
37192     /**
37193      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37194      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37195      */
37196     valueNotFoundText : undefined,
37197     /**
37198      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37199      */
37200     blockFocus : false,
37201     
37202     /**
37203      * @cfg {Boolean} disableClear Disable showing of clear button.
37204      */
37205     disableClear : false,
37206     /**
37207      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37208      */
37209     alwaysQuery : false,
37210     
37211     //private
37212     addicon : false,
37213     editicon: false,
37214     
37215     
37216     // private
37217     onRender : function(ct, position){
37218         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37219         if(this.hiddenName){
37220             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37221                     'before', true);
37222             this.hiddenField.value =
37223                 this.hiddenValue !== undefined ? this.hiddenValue :
37224                 this.value !== undefined ? this.value : '';
37225
37226             // prevent input submission
37227             this.el.dom.removeAttribute('name');
37228         }
37229         if(Roo.isGecko){
37230             this.el.dom.setAttribute('autocomplete', 'off');
37231         }
37232
37233         var cls = 'x-combo-list';
37234
37235         this.list = new Roo.Layer({
37236             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37237         });
37238
37239         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37240         this.list.setWidth(lw);
37241         this.list.swallowEvent('mousewheel');
37242         this.assetHeight = 0;
37243
37244         if(this.title){
37245             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37246             this.assetHeight += this.header.getHeight();
37247         }
37248
37249         this.innerList = this.list.createChild({cls:cls+'-inner'});
37250         this.innerList.on('mouseover', this.onViewOver, this);
37251         this.innerList.on('mousemove', this.onViewMove, this);
37252         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37253         
37254         if(this.allowBlank && !this.pageSize && !this.disableClear){
37255             this.footer = this.list.createChild({cls:cls+'-ft'});
37256             this.pageTb = new Roo.Toolbar(this.footer);
37257            
37258         }
37259         if(this.pageSize){
37260             this.footer = this.list.createChild({cls:cls+'-ft'});
37261             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37262                     {pageSize: this.pageSize});
37263             
37264         }
37265         
37266         if (this.pageTb && this.allowBlank && !this.disableClear) {
37267             var _this = this;
37268             this.pageTb.add(new Roo.Toolbar.Fill(), {
37269                 cls: 'x-btn-icon x-btn-clear',
37270                 text: '&#160;',
37271                 handler: function()
37272                 {
37273                     _this.collapse();
37274                     _this.clearValue();
37275                     _this.onSelect(false, -1);
37276                 }
37277             });
37278         }
37279         if (this.footer) {
37280             this.assetHeight += this.footer.getHeight();
37281         }
37282         
37283
37284         if(!this.tpl){
37285             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37286         }
37287
37288         this.view = new Roo.View(this.innerList, this.tpl, {
37289             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37290         });
37291
37292         this.view.on('click', this.onViewClick, this);
37293
37294         this.store.on('beforeload', this.onBeforeLoad, this);
37295         this.store.on('load', this.onLoad, this);
37296         this.store.on('loadexception', this.collapse, this);
37297
37298         if(this.resizable){
37299             this.resizer = new Roo.Resizable(this.list,  {
37300                pinned:true, handles:'se'
37301             });
37302             this.resizer.on('resize', function(r, w, h){
37303                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37304                 this.listWidth = w;
37305                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37306                 this.restrictHeight();
37307             }, this);
37308             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37309         }
37310         if(!this.editable){
37311             this.editable = true;
37312             this.setEditable(false);
37313         }  
37314         
37315         
37316         if (typeof(this.events.add.listeners) != 'undefined') {
37317             
37318             this.addicon = this.wrap.createChild(
37319                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37320        
37321             this.addicon.on('click', function(e) {
37322                 this.fireEvent('add', this);
37323             }, this);
37324         }
37325         if (typeof(this.events.edit.listeners) != 'undefined') {
37326             
37327             this.editicon = this.wrap.createChild(
37328                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37329             if (this.addicon) {
37330                 this.editicon.setStyle('margin-left', '40px');
37331             }
37332             this.editicon.on('click', function(e) {
37333                 
37334                 // we fire even  if inothing is selected..
37335                 this.fireEvent('edit', this, this.lastData );
37336                 
37337             }, this);
37338         }
37339         
37340         
37341         
37342     },
37343
37344     // private
37345     initEvents : function(){
37346         Roo.form.ComboBox.superclass.initEvents.call(this);
37347
37348         this.keyNav = new Roo.KeyNav(this.el, {
37349             "up" : function(e){
37350                 this.inKeyMode = true;
37351                 this.selectPrev();
37352             },
37353
37354             "down" : function(e){
37355                 if(!this.isExpanded()){
37356                     this.onTriggerClick();
37357                 }else{
37358                     this.inKeyMode = true;
37359                     this.selectNext();
37360                 }
37361             },
37362
37363             "enter" : function(e){
37364                 this.onViewClick();
37365                 //return true;
37366             },
37367
37368             "esc" : function(e){
37369                 this.collapse();
37370             },
37371
37372             "tab" : function(e){
37373                 this.onViewClick(false);
37374                 return true;
37375             },
37376
37377             scope : this,
37378
37379             doRelay : function(foo, bar, hname){
37380                 if(hname == 'down' || this.scope.isExpanded()){
37381                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37382                 }
37383                 return true;
37384             },
37385
37386             forceKeyDown: true
37387         });
37388         this.queryDelay = Math.max(this.queryDelay || 10,
37389                 this.mode == 'local' ? 10 : 250);
37390         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37391         if(this.typeAhead){
37392             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37393         }
37394         if(this.editable !== false){
37395             this.el.on("keyup", this.onKeyUp, this);
37396         }
37397         if(this.forceSelection){
37398             this.on('blur', this.doForce, this);
37399         }
37400     },
37401
37402     onDestroy : function(){
37403         if(this.view){
37404             this.view.setStore(null);
37405             this.view.el.removeAllListeners();
37406             this.view.el.remove();
37407             this.view.purgeListeners();
37408         }
37409         if(this.list){
37410             this.list.destroy();
37411         }
37412         if(this.store){
37413             this.store.un('beforeload', this.onBeforeLoad, this);
37414             this.store.un('load', this.onLoad, this);
37415             this.store.un('loadexception', this.collapse, this);
37416         }
37417         Roo.form.ComboBox.superclass.onDestroy.call(this);
37418     },
37419
37420     // private
37421     fireKey : function(e){
37422         if(e.isNavKeyPress() && !this.list.isVisible()){
37423             this.fireEvent("specialkey", this, e);
37424         }
37425     },
37426
37427     // private
37428     onResize: function(w, h){
37429         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37430         
37431         if(typeof w != 'number'){
37432             // we do not handle it!?!?
37433             return;
37434         }
37435         var tw = this.trigger.getWidth();
37436         tw += this.addicon ? this.addicon.getWidth() : 0;
37437         tw += this.editicon ? this.editicon.getWidth() : 0;
37438         var x = w - tw;
37439         this.el.setWidth( this.adjustWidth('input', x));
37440             
37441         this.trigger.setStyle('left', x+'px');
37442         
37443         if(this.list && this.listWidth === undefined){
37444             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37445             this.list.setWidth(lw);
37446             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37447         }
37448         
37449     
37450         
37451     },
37452
37453     /**
37454      * Allow or prevent the user from directly editing the field text.  If false is passed,
37455      * the user will only be able to select from the items defined in the dropdown list.  This method
37456      * is the runtime equivalent of setting the 'editable' config option at config time.
37457      * @param {Boolean} value True to allow the user to directly edit the field text
37458      */
37459     setEditable : function(value){
37460         if(value == this.editable){
37461             return;
37462         }
37463         this.editable = value;
37464         if(!value){
37465             this.el.dom.setAttribute('readOnly', true);
37466             this.el.on('mousedown', this.onTriggerClick,  this);
37467             this.el.addClass('x-combo-noedit');
37468         }else{
37469             this.el.dom.setAttribute('readOnly', false);
37470             this.el.un('mousedown', this.onTriggerClick,  this);
37471             this.el.removeClass('x-combo-noedit');
37472         }
37473     },
37474
37475     // private
37476     onBeforeLoad : function(){
37477         if(!this.hasFocus){
37478             return;
37479         }
37480         this.innerList.update(this.loadingText ?
37481                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37482         this.restrictHeight();
37483         this.selectedIndex = -1;
37484     },
37485
37486     // private
37487     onLoad : function(){
37488         if(!this.hasFocus){
37489             return;
37490         }
37491         if(this.store.getCount() > 0){
37492             this.expand();
37493             this.restrictHeight();
37494             if(this.lastQuery == this.allQuery){
37495                 if(this.editable){
37496                     this.el.dom.select();
37497                 }
37498                 if(!this.selectByValue(this.value, true)){
37499                     this.select(0, true);
37500                 }
37501             }else{
37502                 this.selectNext();
37503                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37504                     this.taTask.delay(this.typeAheadDelay);
37505                 }
37506             }
37507         }else{
37508             this.onEmptyResults();
37509         }
37510         //this.el.focus();
37511     },
37512
37513     // private
37514     onTypeAhead : function(){
37515         if(this.store.getCount() > 0){
37516             var r = this.store.getAt(0);
37517             var newValue = r.data[this.displayField];
37518             var len = newValue.length;
37519             var selStart = this.getRawValue().length;
37520             if(selStart != len){
37521                 this.setRawValue(newValue);
37522                 this.selectText(selStart, newValue.length);
37523             }
37524         }
37525     },
37526
37527     // private
37528     onSelect : function(record, index){
37529         if(this.fireEvent('beforeselect', this, record, index) !== false){
37530             this.setFromData(index > -1 ? record.data : false);
37531             this.collapse();
37532             this.fireEvent('select', this, record, index);
37533         }
37534     },
37535
37536     /**
37537      * Returns the currently selected field value or empty string if no value is set.
37538      * @return {String} value The selected value
37539      */
37540     getValue : function(){
37541         if(this.valueField){
37542             return typeof this.value != 'undefined' ? this.value : '';
37543         }else{
37544             return Roo.form.ComboBox.superclass.getValue.call(this);
37545         }
37546     },
37547
37548     /**
37549      * Clears any text/value currently set in the field
37550      */
37551     clearValue : function(){
37552         if(this.hiddenField){
37553             this.hiddenField.value = '';
37554         }
37555         this.value = '';
37556         this.setRawValue('');
37557         this.lastSelectionText = '';
37558         this.applyEmptyText();
37559     },
37560
37561     /**
37562      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37563      * will be displayed in the field.  If the value does not match the data value of an existing item,
37564      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37565      * Otherwise the field will be blank (although the value will still be set).
37566      * @param {String} value The value to match
37567      */
37568     setValue : function(v){
37569         var text = v;
37570         if(this.valueField){
37571             var r = this.findRecord(this.valueField, v);
37572             if(r){
37573                 text = r.data[this.displayField];
37574             }else if(this.valueNotFoundText !== undefined){
37575                 text = this.valueNotFoundText;
37576             }
37577         }
37578         this.lastSelectionText = text;
37579         if(this.hiddenField){
37580             this.hiddenField.value = v;
37581         }
37582         Roo.form.ComboBox.superclass.setValue.call(this, text);
37583         this.value = v;
37584     },
37585     /**
37586      * @property {Object} the last set data for the element
37587      */
37588     
37589     lastData : false,
37590     /**
37591      * Sets the value of the field based on a object which is related to the record format for the store.
37592      * @param {Object} value the value to set as. or false on reset?
37593      */
37594     setFromData : function(o){
37595         var dv = ''; // display value
37596         var vv = ''; // value value..
37597         this.lastData = o;
37598         if (this.displayField) {
37599             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37600         } else {
37601             // this is an error condition!!!
37602             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37603         }
37604         
37605         if(this.valueField){
37606             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37607         }
37608         if(this.hiddenField){
37609             this.hiddenField.value = vv;
37610             
37611             this.lastSelectionText = dv;
37612             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37613             this.value = vv;
37614             return;
37615         }
37616         // no hidden field.. - we store the value in 'value', but still display
37617         // display field!!!!
37618         this.lastSelectionText = dv;
37619         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37620         this.value = vv;
37621         
37622         
37623     },
37624     // private
37625     reset : function(){
37626         // overridden so that last data is reset..
37627         this.setValue(this.originalValue);
37628         this.clearInvalid();
37629         this.lastData = false;
37630     },
37631     // private
37632     findRecord : function(prop, value){
37633         var record;
37634         if(this.store.getCount() > 0){
37635             this.store.each(function(r){
37636                 if(r.data[prop] == value){
37637                     record = r;
37638                     return false;
37639                 }
37640             });
37641         }
37642         return record;
37643     },
37644
37645     // private
37646     onViewMove : function(e, t){
37647         this.inKeyMode = false;
37648     },
37649
37650     // private
37651     onViewOver : function(e, t){
37652         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37653             return;
37654         }
37655         var item = this.view.findItemFromChild(t);
37656         if(item){
37657             var index = this.view.indexOf(item);
37658             this.select(index, false);
37659         }
37660     },
37661
37662     // private
37663     onViewClick : function(doFocus){
37664         var index = this.view.getSelectedIndexes()[0];
37665         var r = this.store.getAt(index);
37666         if(r){
37667             this.onSelect(r, index);
37668         }
37669         if(doFocus !== false && !this.blockFocus){
37670             this.el.focus();
37671         }
37672     },
37673
37674     // private
37675     restrictHeight : function(){
37676         this.innerList.dom.style.height = '';
37677         var inner = this.innerList.dom;
37678         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37679         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37680         this.list.beginUpdate();
37681         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37682         this.list.alignTo(this.el, this.listAlign);
37683         this.list.endUpdate();
37684     },
37685
37686     // private
37687     onEmptyResults : function(){
37688         this.collapse();
37689     },
37690
37691     /**
37692      * Returns true if the dropdown list is expanded, else false.
37693      */
37694     isExpanded : function(){
37695         return this.list.isVisible();
37696     },
37697
37698     /**
37699      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37700      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37701      * @param {String} value The data value of the item to select
37702      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37703      * selected item if it is not currently in view (defaults to true)
37704      * @return {Boolean} True if the value matched an item in the list, else false
37705      */
37706     selectByValue : function(v, scrollIntoView){
37707         if(v !== undefined && v !== null){
37708             var r = this.findRecord(this.valueField || this.displayField, v);
37709             if(r){
37710                 this.select(this.store.indexOf(r), scrollIntoView);
37711                 return true;
37712             }
37713         }
37714         return false;
37715     },
37716
37717     /**
37718      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37719      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37720      * @param {Number} index The zero-based index of the list item to select
37721      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37722      * selected item if it is not currently in view (defaults to true)
37723      */
37724     select : function(index, scrollIntoView){
37725         this.selectedIndex = index;
37726         this.view.select(index);
37727         if(scrollIntoView !== false){
37728             var el = this.view.getNode(index);
37729             if(el){
37730                 this.innerList.scrollChildIntoView(el, false);
37731             }
37732         }
37733     },
37734
37735     // private
37736     selectNext : function(){
37737         var ct = this.store.getCount();
37738         if(ct > 0){
37739             if(this.selectedIndex == -1){
37740                 this.select(0);
37741             }else if(this.selectedIndex < ct-1){
37742                 this.select(this.selectedIndex+1);
37743             }
37744         }
37745     },
37746
37747     // private
37748     selectPrev : function(){
37749         var ct = this.store.getCount();
37750         if(ct > 0){
37751             if(this.selectedIndex == -1){
37752                 this.select(0);
37753             }else if(this.selectedIndex != 0){
37754                 this.select(this.selectedIndex-1);
37755             }
37756         }
37757     },
37758
37759     // private
37760     onKeyUp : function(e){
37761         if(this.editable !== false && !e.isSpecialKey()){
37762             this.lastKey = e.getKey();
37763             this.dqTask.delay(this.queryDelay);
37764         }
37765     },
37766
37767     // private
37768     validateBlur : function(){
37769         return !this.list || !this.list.isVisible();   
37770     },
37771
37772     // private
37773     initQuery : function(){
37774         this.doQuery(this.getRawValue());
37775     },
37776
37777     // private
37778     doForce : function(){
37779         if(this.el.dom.value.length > 0){
37780             this.el.dom.value =
37781                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37782             this.applyEmptyText();
37783         }
37784     },
37785
37786     /**
37787      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37788      * query allowing the query action to be canceled if needed.
37789      * @param {String} query The SQL query to execute
37790      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37791      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37792      * saved in the current store (defaults to false)
37793      */
37794     doQuery : function(q, forceAll){
37795         if(q === undefined || q === null){
37796             q = '';
37797         }
37798         var qe = {
37799             query: q,
37800             forceAll: forceAll,
37801             combo: this,
37802             cancel:false
37803         };
37804         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37805             return false;
37806         }
37807         q = qe.query;
37808         forceAll = qe.forceAll;
37809         if(forceAll === true || (q.length >= this.minChars)){
37810             if(this.lastQuery != q || this.alwaysQuery){
37811                 this.lastQuery = q;
37812                 if(this.mode == 'local'){
37813                     this.selectedIndex = -1;
37814                     if(forceAll){
37815                         this.store.clearFilter();
37816                     }else{
37817                         this.store.filter(this.displayField, q);
37818                     }
37819                     this.onLoad();
37820                 }else{
37821                     this.store.baseParams[this.queryParam] = q;
37822                     this.store.load({
37823                         params: this.getParams(q)
37824                     });
37825                     this.expand();
37826                 }
37827             }else{
37828                 this.selectedIndex = -1;
37829                 this.onLoad();   
37830             }
37831         }
37832     },
37833
37834     // private
37835     getParams : function(q){
37836         var p = {};
37837         //p[this.queryParam] = q;
37838         if(this.pageSize){
37839             p.start = 0;
37840             p.limit = this.pageSize;
37841         }
37842         return p;
37843     },
37844
37845     /**
37846      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37847      */
37848     collapse : function(){
37849         if(!this.isExpanded()){
37850             return;
37851         }
37852         this.list.hide();
37853         Roo.get(document).un('mousedown', this.collapseIf, this);
37854         Roo.get(document).un('mousewheel', this.collapseIf, this);
37855         if (!this.editable) {
37856             Roo.get(document).un('keydown', this.listKeyPress, this);
37857         }
37858         this.fireEvent('collapse', this);
37859     },
37860
37861     // private
37862     collapseIf : function(e){
37863         if(!e.within(this.wrap) && !e.within(this.list)){
37864             this.collapse();
37865         }
37866     },
37867
37868     /**
37869      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37870      */
37871     expand : function(){
37872         if(this.isExpanded() || !this.hasFocus){
37873             return;
37874         }
37875         this.list.alignTo(this.el, this.listAlign);
37876         this.list.show();
37877         Roo.get(document).on('mousedown', this.collapseIf, this);
37878         Roo.get(document).on('mousewheel', this.collapseIf, this);
37879         if (!this.editable) {
37880             Roo.get(document).on('keydown', this.listKeyPress, this);
37881         }
37882         
37883         this.fireEvent('expand', this);
37884     },
37885
37886     // private
37887     // Implements the default empty TriggerField.onTriggerClick function
37888     onTriggerClick : function(){
37889         if(this.disabled){
37890             return;
37891         }
37892         if(this.isExpanded()){
37893             this.collapse();
37894             if (!this.blockFocus) {
37895                 this.el.focus();
37896             }
37897             
37898         }else {
37899             this.hasFocus = true;
37900             if(this.triggerAction == 'all') {
37901                 this.doQuery(this.allQuery, true);
37902             } else {
37903                 this.doQuery(this.getRawValue());
37904             }
37905             if (!this.blockFocus) {
37906                 this.el.focus();
37907             }
37908         }
37909     },
37910     listKeyPress : function(e)
37911     {
37912         //Roo.log('listkeypress');
37913         // scroll to first matching element based on key pres..
37914         if (e.isSpecialKey()) {
37915             return false;
37916         }
37917         var k = String.fromCharCode(e.getKey()).toUpperCase();
37918         //Roo.log(k);
37919         var match  = false;
37920         var csel = this.view.getSelectedNodes();
37921         var cselitem = false;
37922         if (csel.length) {
37923             var ix = this.view.indexOf(csel[0]);
37924             cselitem  = this.store.getAt(ix);
37925             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
37926                 cselitem = false;
37927             }
37928             
37929         }
37930         
37931         this.store.each(function(v) { 
37932             if (cselitem) {
37933                 // start at existing selection.
37934                 if (cselitem.id == v.id) {
37935                     cselitem = false;
37936                 }
37937                 return;
37938             }
37939                 
37940             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
37941                 match = this.store.indexOf(v);
37942                 return false;
37943             }
37944         }, this);
37945         
37946         if (match === false) {
37947             return true; // no more action?
37948         }
37949         // scroll to?
37950         this.view.select(match);
37951         var sn = Roo.get(this.view.getSelectedNodes()[0])
37952         sn.scrollIntoView(sn.dom.parentNode, false);
37953     }
37954
37955     /** 
37956     * @cfg {Boolean} grow 
37957     * @hide 
37958     */
37959     /** 
37960     * @cfg {Number} growMin 
37961     * @hide 
37962     */
37963     /** 
37964     * @cfg {Number} growMax 
37965     * @hide 
37966     */
37967     /**
37968      * @hide
37969      * @method autoSize
37970      */
37971 });/*
37972  * Based on:
37973  * Ext JS Library 1.1.1
37974  * Copyright(c) 2006-2007, Ext JS, LLC.
37975  *
37976  * Originally Released Under LGPL - original licence link has changed is not relivant.
37977  *
37978  * Fork - LGPL
37979  * <script type="text/javascript">
37980  */
37981 /**
37982  * @class Roo.form.Checkbox
37983  * @extends Roo.form.Field
37984  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37985  * @constructor
37986  * Creates a new Checkbox
37987  * @param {Object} config Configuration options
37988  */
37989 Roo.form.Checkbox = function(config){
37990     Roo.form.Checkbox.superclass.constructor.call(this, config);
37991     this.addEvents({
37992         /**
37993          * @event check
37994          * Fires when the checkbox is checked or unchecked.
37995              * @param {Roo.form.Checkbox} this This checkbox
37996              * @param {Boolean} checked The new checked value
37997              */
37998         check : true
37999     });
38000 };
38001
38002 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38003     /**
38004      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38005      */
38006     focusClass : undefined,
38007     /**
38008      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38009      */
38010     fieldClass: "x-form-field",
38011     /**
38012      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38013      */
38014     checked: false,
38015     /**
38016      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38017      * {tag: "input", type: "checkbox", autocomplete: "off"})
38018      */
38019     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38020     /**
38021      * @cfg {String} boxLabel The text that appears beside the checkbox
38022      */
38023     boxLabel : "",
38024     /**
38025      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38026      */  
38027     inputValue : '1',
38028     /**
38029      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38030      */
38031      valueOff: '0', // value when not checked..
38032
38033     actionMode : 'viewEl', 
38034     //
38035     // private
38036     itemCls : 'x-menu-check-item x-form-item',
38037     groupClass : 'x-menu-group-item',
38038     inputType : 'hidden',
38039     
38040     
38041     inSetChecked: false, // check that we are not calling self...
38042     
38043     inputElement: false, // real input element?
38044     basedOn: false, // ????
38045     
38046     isFormField: true, // not sure where this is needed!!!!
38047
38048     onResize : function(){
38049         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38050         if(!this.boxLabel){
38051             this.el.alignTo(this.wrap, 'c-c');
38052         }
38053     },
38054
38055     initEvents : function(){
38056         Roo.form.Checkbox.superclass.initEvents.call(this);
38057         this.el.on("click", this.onClick,  this);
38058         this.el.on("change", this.onClick,  this);
38059     },
38060
38061
38062     getResizeEl : function(){
38063         return this.wrap;
38064     },
38065
38066     getPositionEl : function(){
38067         return this.wrap;
38068     },
38069
38070     // private
38071     onRender : function(ct, position){
38072         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38073         /*
38074         if(this.inputValue !== undefined){
38075             this.el.dom.value = this.inputValue;
38076         }
38077         */
38078         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38079         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38080         var viewEl = this.wrap.createChild({ 
38081             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38082         this.viewEl = viewEl;   
38083         this.wrap.on('click', this.onClick,  this); 
38084         
38085         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38086         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38087         
38088         
38089         
38090         if(this.boxLabel){
38091             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38092         //    viewEl.on('click', this.onClick,  this); 
38093         }
38094         //if(this.checked){
38095             this.setChecked(this.checked);
38096         //}else{
38097             //this.checked = this.el.dom;
38098         //}
38099
38100     },
38101
38102     // private
38103     initValue : Roo.emptyFn,
38104
38105     /**
38106      * Returns the checked state of the checkbox.
38107      * @return {Boolean} True if checked, else false
38108      */
38109     getValue : function(){
38110         if(this.el){
38111             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38112         }
38113         return this.valueOff;
38114         
38115     },
38116
38117         // private
38118     onClick : function(){ 
38119         this.setChecked(!this.checked);
38120
38121         //if(this.el.dom.checked != this.checked){
38122         //    this.setValue(this.el.dom.checked);
38123        // }
38124     },
38125
38126     /**
38127      * Sets the checked state of the checkbox.
38128      * On is always based on a string comparison between inputValue and the param.
38129      * @param {Boolean/String} value - the value to set 
38130      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38131      */
38132     setValue : function(v,suppressEvent){
38133         
38134         
38135         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38136         //if(this.el && this.el.dom){
38137         //    this.el.dom.checked = this.checked;
38138         //    this.el.dom.defaultChecked = this.checked;
38139         //}
38140         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38141         //this.fireEvent("check", this, this.checked);
38142     },
38143     // private..
38144     setChecked : function(state,suppressEvent)
38145     {
38146         if (this.inSetChecked) {
38147             this.checked = state;
38148             return;
38149         }
38150         
38151     
38152         if(this.wrap){
38153             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38154         }
38155         this.checked = state;
38156         if(suppressEvent !== true){
38157             this.fireEvent('check', this, state);
38158         }
38159         this.inSetChecked = true;
38160         this.el.dom.value = state ? this.inputValue : this.valueOff;
38161         this.inSetChecked = false;
38162         
38163     },
38164     // handle setting of hidden value by some other method!!?!?
38165     setFromHidden: function()
38166     {
38167         if(!this.el){
38168             return;
38169         }
38170         //console.log("SET FROM HIDDEN");
38171         //alert('setFrom hidden');
38172         this.setValue(this.el.dom.value);
38173     },
38174     
38175     onDestroy : function()
38176     {
38177         if(this.viewEl){
38178             Roo.get(this.viewEl).remove();
38179         }
38180          
38181         Roo.form.Checkbox.superclass.onDestroy.call(this);
38182     }
38183
38184 });/*
38185  * Based on:
38186  * Ext JS Library 1.1.1
38187  * Copyright(c) 2006-2007, Ext JS, LLC.
38188  *
38189  * Originally Released Under LGPL - original licence link has changed is not relivant.
38190  *
38191  * Fork - LGPL
38192  * <script type="text/javascript">
38193  */
38194  
38195 /**
38196  * @class Roo.form.Radio
38197  * @extends Roo.form.Checkbox
38198  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38199  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38200  * @constructor
38201  * Creates a new Radio
38202  * @param {Object} config Configuration options
38203  */
38204 Roo.form.Radio = function(){
38205     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38206 };
38207 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38208     inputType: 'radio',
38209
38210     /**
38211      * If this radio is part of a group, it will return the selected value
38212      * @return {String}
38213      */
38214     getGroupValue : function(){
38215         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38216     }
38217 });//<script type="text/javascript">
38218
38219 /*
38220  * Ext JS Library 1.1.1
38221  * Copyright(c) 2006-2007, Ext JS, LLC.
38222  * licensing@extjs.com
38223  * 
38224  * http://www.extjs.com/license
38225  */
38226  
38227  /*
38228   * 
38229   * Known bugs:
38230   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38231   * - IE ? - no idea how much works there.
38232   * 
38233   * 
38234   * 
38235   */
38236  
38237
38238 /**
38239  * @class Ext.form.HtmlEditor
38240  * @extends Ext.form.Field
38241  * Provides a lightweight HTML Editor component.
38242  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38243  * 
38244  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38245  * supported by this editor.</b><br/><br/>
38246  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38247  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38248  */
38249 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38250       /**
38251      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38252      */
38253     toolbars : false,
38254     /**
38255      * @cfg {String} createLinkText The default text for the create link prompt
38256      */
38257     createLinkText : 'Please enter the URL for the link:',
38258     /**
38259      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38260      */
38261     defaultLinkValue : 'http:/'+'/',
38262    
38263     
38264     // id of frame..
38265     frameId: false,
38266     
38267     // private properties
38268     validationEvent : false,
38269     deferHeight: true,
38270     initialized : false,
38271     activated : false,
38272     sourceEditMode : false,
38273     onFocus : Roo.emptyFn,
38274     iframePad:3,
38275     hideMode:'offsets',
38276     defaultAutoCreate : {
38277         tag: "textarea",
38278         style:"width:500px;height:300px;",
38279         autocomplete: "off"
38280     },
38281
38282     // private
38283     initComponent : function(){
38284         this.addEvents({
38285             /**
38286              * @event initialize
38287              * Fires when the editor is fully initialized (including the iframe)
38288              * @param {HtmlEditor} this
38289              */
38290             initialize: true,
38291             /**
38292              * @event activate
38293              * Fires when the editor is first receives the focus. Any insertion must wait
38294              * until after this event.
38295              * @param {HtmlEditor} this
38296              */
38297             activate: true,
38298              /**
38299              * @event beforesync
38300              * Fires before the textarea is updated with content from the editor iframe. Return false
38301              * to cancel the sync.
38302              * @param {HtmlEditor} this
38303              * @param {String} html
38304              */
38305             beforesync: true,
38306              /**
38307              * @event beforepush
38308              * Fires before the iframe editor is updated with content from the textarea. Return false
38309              * to cancel the push.
38310              * @param {HtmlEditor} this
38311              * @param {String} html
38312              */
38313             beforepush: true,
38314              /**
38315              * @event sync
38316              * Fires when the textarea is updated with content from the editor iframe.
38317              * @param {HtmlEditor} this
38318              * @param {String} html
38319              */
38320             sync: true,
38321              /**
38322              * @event push
38323              * Fires when the iframe editor is updated with content from the textarea.
38324              * @param {HtmlEditor} this
38325              * @param {String} html
38326              */
38327             push: true,
38328              /**
38329              * @event editmodechange
38330              * Fires when the editor switches edit modes
38331              * @param {HtmlEditor} this
38332              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38333              */
38334             editmodechange: true,
38335             /**
38336              * @event editorevent
38337              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38338              * @param {HtmlEditor} this
38339              */
38340             editorevent: true
38341         })
38342     },
38343
38344     /**
38345      * Protected method that will not generally be called directly. It
38346      * is called when the editor creates its toolbar. Override this method if you need to
38347      * add custom toolbar buttons.
38348      * @param {HtmlEditor} editor
38349      */
38350     createToolbar : function(editor){
38351         if (!editor.toolbars || !editor.toolbars.length) {
38352             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38353         }
38354         
38355         for (var i =0 ; i < editor.toolbars.length;i++) {
38356             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38357             editor.toolbars[i].init(editor);
38358         }
38359          
38360         
38361     },
38362
38363     /**
38364      * Protected method that will not generally be called directly. It
38365      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38366      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38367      */
38368     getDocMarkup : function(){
38369         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38370     },
38371
38372     // private
38373     onRender : function(ct, position){
38374         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38375         this.el.dom.style.border = '0 none';
38376         this.el.dom.setAttribute('tabIndex', -1);
38377         this.el.addClass('x-hidden');
38378         if(Roo.isIE){ // fix IE 1px bogus margin
38379             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38380         }
38381         this.wrap = this.el.wrap({
38382             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38383         });
38384
38385         this.frameId = Roo.id();
38386         this.createToolbar(this);
38387         
38388         
38389         
38390         
38391       
38392         
38393         var iframe = this.wrap.createChild({
38394             tag: 'iframe',
38395             id: this.frameId,
38396             name: this.frameId,
38397             frameBorder : 'no',
38398             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38399         });
38400         
38401        // console.log(iframe);
38402         //this.wrap.dom.appendChild(iframe);
38403
38404         this.iframe = iframe.dom;
38405
38406          this.assignDocWin();
38407         
38408         this.doc.designMode = 'on';
38409        
38410         this.doc.open();
38411         this.doc.write(this.getDocMarkup());
38412         this.doc.close();
38413
38414         
38415         var task = { // must defer to wait for browser to be ready
38416             run : function(){
38417                 //console.log("run task?" + this.doc.readyState);
38418                 this.assignDocWin();
38419                 if(this.doc.body || this.doc.readyState == 'complete'){
38420                     try {
38421                         this.doc.designMode="on";
38422                     } catch (e) {
38423                         return;
38424                     }
38425                     Roo.TaskMgr.stop(task);
38426                     this.initEditor.defer(10, this);
38427                 }
38428             },
38429             interval : 10,
38430             duration:10000,
38431             scope: this
38432         };
38433         Roo.TaskMgr.start(task);
38434
38435         if(!this.width){
38436             this.setSize(this.el.getSize());
38437         }
38438     },
38439
38440     // private
38441     onResize : function(w, h){
38442         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38443         if(this.el && this.iframe){
38444             if(typeof w == 'number'){
38445                 var aw = w - this.wrap.getFrameWidth('lr');
38446                 this.el.setWidth(this.adjustWidth('textarea', aw));
38447                 this.iframe.style.width = aw + 'px';
38448             }
38449             if(typeof h == 'number'){
38450                 var tbh = 0;
38451                 for (var i =0; i < this.toolbars.length;i++) {
38452                     // fixme - ask toolbars for heights?
38453                     tbh += this.toolbars[i].tb.el.getHeight();
38454                 }
38455                 
38456                 
38457                 
38458                 
38459                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38460                 this.el.setHeight(this.adjustWidth('textarea', ah));
38461                 this.iframe.style.height = ah + 'px';
38462                 if(this.doc){
38463                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38464                 }
38465             }
38466         }
38467     },
38468
38469     /**
38470      * Toggles the editor between standard and source edit mode.
38471      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38472      */
38473     toggleSourceEdit : function(sourceEditMode){
38474         
38475         this.sourceEditMode = sourceEditMode === true;
38476         
38477         if(this.sourceEditMode){
38478           
38479             this.syncValue();
38480             this.iframe.className = 'x-hidden';
38481             this.el.removeClass('x-hidden');
38482             this.el.dom.removeAttribute('tabIndex');
38483             this.el.focus();
38484         }else{
38485              
38486             this.pushValue();
38487             this.iframe.className = '';
38488             this.el.addClass('x-hidden');
38489             this.el.dom.setAttribute('tabIndex', -1);
38490             this.deferFocus();
38491         }
38492         this.setSize(this.wrap.getSize());
38493         this.fireEvent('editmodechange', this, this.sourceEditMode);
38494     },
38495
38496     // private used internally
38497     createLink : function(){
38498         var url = prompt(this.createLinkText, this.defaultLinkValue);
38499         if(url && url != 'http:/'+'/'){
38500             this.relayCmd('createlink', url);
38501         }
38502     },
38503
38504     // private (for BoxComponent)
38505     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38506
38507     // private (for BoxComponent)
38508     getResizeEl : function(){
38509         return this.wrap;
38510     },
38511
38512     // private (for BoxComponent)
38513     getPositionEl : function(){
38514         return this.wrap;
38515     },
38516
38517     // private
38518     initEvents : function(){
38519         this.originalValue = this.getValue();
38520     },
38521
38522     /**
38523      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38524      * @method
38525      */
38526     markInvalid : Roo.emptyFn,
38527     /**
38528      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38529      * @method
38530      */
38531     clearInvalid : Roo.emptyFn,
38532
38533     setValue : function(v){
38534         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38535         this.pushValue();
38536     },
38537
38538     /**
38539      * Protected method that will not generally be called directly. If you need/want
38540      * custom HTML cleanup, this is the method you should override.
38541      * @param {String} html The HTML to be cleaned
38542      * return {String} The cleaned HTML
38543      */
38544     cleanHtml : function(html){
38545         html = String(html);
38546         if(html.length > 5){
38547             if(Roo.isSafari){ // strip safari nonsense
38548                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38549             }
38550         }
38551         if(html == '&nbsp;'){
38552             html = '';
38553         }
38554         return html;
38555     },
38556
38557     /**
38558      * Protected method that will not generally be called directly. Syncs the contents
38559      * of the editor iframe with the textarea.
38560      */
38561     syncValue : function(){
38562         if(this.initialized){
38563             var bd = (this.doc.body || this.doc.documentElement);
38564             var html = bd.innerHTML;
38565             if(Roo.isSafari){
38566                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38567                 var m = bs.match(/text-align:(.*?);/i);
38568                 if(m && m[1]){
38569                     html = '<div style="'+m[0]+'">' + html + '</div>';
38570                 }
38571             }
38572             html = this.cleanHtml(html);
38573             if(this.fireEvent('beforesync', this, html) !== false){
38574                 this.el.dom.value = html;
38575                 this.fireEvent('sync', this, html);
38576             }
38577         }
38578     },
38579
38580     /**
38581      * Protected method that will not generally be called directly. Pushes the value of the textarea
38582      * into the iframe editor.
38583      */
38584     pushValue : function(){
38585         if(this.initialized){
38586             var v = this.el.dom.value;
38587             if(v.length < 1){
38588                 v = '&#160;';
38589             }
38590             if(this.fireEvent('beforepush', this, v) !== false){
38591                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38592                 this.fireEvent('push', this, v);
38593             }
38594         }
38595     },
38596
38597     // private
38598     deferFocus : function(){
38599         this.focus.defer(10, this);
38600     },
38601
38602     // doc'ed in Field
38603     focus : function(){
38604         if(this.win && !this.sourceEditMode){
38605             this.win.focus();
38606         }else{
38607             this.el.focus();
38608         }
38609     },
38610     
38611     assignDocWin: function()
38612     {
38613         var iframe = this.iframe;
38614         
38615          if(Roo.isIE){
38616             this.doc = iframe.contentWindow.document;
38617             this.win = iframe.contentWindow;
38618         } else {
38619             if (!Roo.get(this.frameId)) {
38620                 return;
38621             }
38622             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38623             this.win = Roo.get(this.frameId).dom.contentWindow;
38624         }
38625     },
38626     
38627     // private
38628     initEditor : function(){
38629         //console.log("INIT EDITOR");
38630         this.assignDocWin();
38631         
38632         
38633         
38634         this.doc.designMode="on";
38635         this.doc.open();
38636         this.doc.write(this.getDocMarkup());
38637         this.doc.close();
38638         
38639         var dbody = (this.doc.body || this.doc.documentElement);
38640         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38641         // this copies styles from the containing element into thsi one..
38642         // not sure why we need all of this..
38643         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38644         ss['background-attachment'] = 'fixed'; // w3c
38645         dbody.bgProperties = 'fixed'; // ie
38646         Roo.DomHelper.applyStyles(dbody, ss);
38647         Roo.EventManager.on(this.doc, {
38648             'mousedown': this.onEditorEvent,
38649             'dblclick': this.onEditorEvent,
38650             'click': this.onEditorEvent,
38651             'keyup': this.onEditorEvent,
38652             buffer:100,
38653             scope: this
38654         });
38655         if(Roo.isGecko){
38656             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38657         }
38658         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38659             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38660         }
38661         this.initialized = true;
38662
38663         this.fireEvent('initialize', this);
38664         this.pushValue();
38665     },
38666
38667     // private
38668     onDestroy : function(){
38669         
38670         
38671         
38672         if(this.rendered){
38673             
38674             for (var i =0; i < this.toolbars.length;i++) {
38675                 // fixme - ask toolbars for heights?
38676                 this.toolbars[i].onDestroy();
38677             }
38678             
38679             this.wrap.dom.innerHTML = '';
38680             this.wrap.remove();
38681         }
38682     },
38683
38684     // private
38685     onFirstFocus : function(){
38686         
38687         this.assignDocWin();
38688         
38689         
38690         this.activated = true;
38691         for (var i =0; i < this.toolbars.length;i++) {
38692             this.toolbars[i].onFirstFocus();
38693         }
38694        
38695         if(Roo.isGecko){ // prevent silly gecko errors
38696             this.win.focus();
38697             var s = this.win.getSelection();
38698             if(!s.focusNode || s.focusNode.nodeType != 3){
38699                 var r = s.getRangeAt(0);
38700                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38701                 r.collapse(true);
38702                 this.deferFocus();
38703             }
38704             try{
38705                 this.execCmd('useCSS', true);
38706                 this.execCmd('styleWithCSS', false);
38707             }catch(e){}
38708         }
38709         this.fireEvent('activate', this);
38710     },
38711
38712     // private
38713     adjustFont: function(btn){
38714         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38715         //if(Roo.isSafari){ // safari
38716         //    adjust *= 2;
38717        // }
38718         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38719         if(Roo.isSafari){ // safari
38720             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38721             v =  (v < 10) ? 10 : v;
38722             v =  (v > 48) ? 48 : v;
38723             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38724             
38725         }
38726         
38727         
38728         v = Math.max(1, v+adjust);
38729         
38730         this.execCmd('FontSize', v  );
38731     },
38732
38733     onEditorEvent : function(e){
38734         this.fireEvent('editorevent', this, e);
38735       //  this.updateToolbar();
38736         this.syncValue();
38737     },
38738
38739     insertTag : function(tg)
38740     {
38741         // could be a bit smarter... -> wrap the current selected tRoo..
38742         
38743         this.execCmd("formatblock",   tg);
38744         
38745     },
38746     
38747     insertText : function(txt)
38748     {
38749         
38750         
38751         range = this.createRange();
38752         range.deleteContents();
38753                //alert(Sender.getAttribute('label'));
38754                
38755         range.insertNode(this.doc.createTextNode(txt));
38756     } ,
38757     
38758     // private
38759     relayBtnCmd : function(btn){
38760         this.relayCmd(btn.cmd);
38761     },
38762
38763     /**
38764      * Executes a Midas editor command on the editor document and performs necessary focus and
38765      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38766      * @param {String} cmd The Midas command
38767      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38768      */
38769     relayCmd : function(cmd, value){
38770         this.win.focus();
38771         this.execCmd(cmd, value);
38772         this.fireEvent('editorevent', this);
38773         //this.updateToolbar();
38774         this.deferFocus();
38775     },
38776
38777     /**
38778      * Executes a Midas editor command directly on the editor document.
38779      * For visual commands, you should use {@link #relayCmd} instead.
38780      * <b>This should only be called after the editor is initialized.</b>
38781      * @param {String} cmd The Midas command
38782      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38783      */
38784     execCmd : function(cmd, value){
38785         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38786         this.syncValue();
38787     },
38788
38789    
38790     /**
38791      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38792      * to insert tRoo.
38793      * @param {String} text
38794      */
38795     insertAtCursor : function(text){
38796         if(!this.activated){
38797             return;
38798         }
38799         if(Roo.isIE){
38800             this.win.focus();
38801             var r = this.doc.selection.createRange();
38802             if(r){
38803                 r.collapse(true);
38804                 r.pasteHTML(text);
38805                 this.syncValue();
38806                 this.deferFocus();
38807             }
38808         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38809             this.win.focus();
38810             this.execCmd('InsertHTML', text);
38811             this.deferFocus();
38812         }
38813     },
38814  // private
38815     mozKeyPress : function(e){
38816         if(e.ctrlKey){
38817             var c = e.getCharCode(), cmd;
38818           
38819             if(c > 0){
38820                 c = String.fromCharCode(c).toLowerCase();
38821                 switch(c){
38822                     case 'b':
38823                         cmd = 'bold';
38824                     break;
38825                     case 'i':
38826                         cmd = 'italic';
38827                     break;
38828                     case 'u':
38829                         cmd = 'underline';
38830                     case 'v':
38831                         this.cleanUpPaste.defer(100, this);
38832                         return;
38833                     break;
38834                 }
38835                 if(cmd){
38836                     this.win.focus();
38837                     this.execCmd(cmd);
38838                     this.deferFocus();
38839                     e.preventDefault();
38840                 }
38841                 
38842             }
38843         }
38844     },
38845
38846     // private
38847     fixKeys : function(){ // load time branching for fastest keydown performance
38848         if(Roo.isIE){
38849             return function(e){
38850                 var k = e.getKey(), r;
38851                 if(k == e.TAB){
38852                     e.stopEvent();
38853                     r = this.doc.selection.createRange();
38854                     if(r){
38855                         r.collapse(true);
38856                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38857                         this.deferFocus();
38858                     }
38859                     return;
38860                 }
38861                 
38862                 if(k == e.ENTER){
38863                     r = this.doc.selection.createRange();
38864                     if(r){
38865                         var target = r.parentElement();
38866                         if(!target || target.tagName.toLowerCase() != 'li'){
38867                             e.stopEvent();
38868                             r.pasteHTML('<br />');
38869                             r.collapse(false);
38870                             r.select();
38871                         }
38872                     }
38873                 }
38874                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38875                     this.cleanUpPaste.defer(100, this);
38876                     return;
38877                 }
38878                 
38879                 
38880             };
38881         }else if(Roo.isOpera){
38882             return function(e){
38883                 var k = e.getKey();
38884                 if(k == e.TAB){
38885                     e.stopEvent();
38886                     this.win.focus();
38887                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38888                     this.deferFocus();
38889                 }
38890                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38891                     this.cleanUpPaste.defer(100, this);
38892                     return;
38893                 }
38894                 
38895             };
38896         }else if(Roo.isSafari){
38897             return function(e){
38898                 var k = e.getKey();
38899                 
38900                 if(k == e.TAB){
38901                     e.stopEvent();
38902                     this.execCmd('InsertText','\t');
38903                     this.deferFocus();
38904                     return;
38905                 }
38906                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38907                     this.cleanUpPaste.defer(100, this);
38908                     return;
38909                 }
38910                 
38911              };
38912         }
38913     }(),
38914     
38915     getAllAncestors: function()
38916     {
38917         var p = this.getSelectedNode();
38918         var a = [];
38919         if (!p) {
38920             a.push(p); // push blank onto stack..
38921             p = this.getParentElement();
38922         }
38923         
38924         
38925         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38926             a.push(p);
38927             p = p.parentNode;
38928         }
38929         a.push(this.doc.body);
38930         return a;
38931     },
38932     lastSel : false,
38933     lastSelNode : false,
38934     
38935     
38936     getSelection : function() 
38937     {
38938         this.assignDocWin();
38939         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38940     },
38941     
38942     getSelectedNode: function() 
38943     {
38944         // this may only work on Gecko!!!
38945         
38946         // should we cache this!!!!
38947         
38948         
38949         
38950          
38951         var range = this.createRange(this.getSelection());
38952         
38953         if (Roo.isIE) {
38954             var parent = range.parentElement();
38955             while (true) {
38956                 var testRange = range.duplicate();
38957                 testRange.moveToElementText(parent);
38958                 if (testRange.inRange(range)) {
38959                     break;
38960                 }
38961                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38962                     break;
38963                 }
38964                 parent = parent.parentElement;
38965             }
38966             return parent;
38967         }
38968         
38969         
38970         var ar = range.endContainer.childNodes;
38971         if (!ar.length) {
38972             ar = range.commonAncestorContainer.childNodes;
38973             //alert(ar.length);
38974         }
38975         var nodes = [];
38976         var other_nodes = [];
38977         var has_other_nodes = false;
38978         for (var i=0;i<ar.length;i++) {
38979             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38980                 continue;
38981             }
38982             // fullly contained node.
38983             
38984             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38985                 nodes.push(ar[i]);
38986                 continue;
38987             }
38988             
38989             // probably selected..
38990             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38991                 other_nodes.push(ar[i]);
38992                 continue;
38993             }
38994             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38995                 continue;
38996             }
38997             
38998             
38999             has_other_nodes = true;
39000         }
39001         if (!nodes.length && other_nodes.length) {
39002             nodes= other_nodes;
39003         }
39004         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39005             return false;
39006         }
39007         
39008         return nodes[0];
39009     },
39010     createRange: function(sel)
39011     {
39012         // this has strange effects when using with 
39013         // top toolbar - not sure if it's a great idea.
39014         //this.editor.contentWindow.focus();
39015         if (typeof sel != "undefined") {
39016             try {
39017                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39018             } catch(e) {
39019                 return this.doc.createRange();
39020             }
39021         } else {
39022             return this.doc.createRange();
39023         }
39024     },
39025     getParentElement: function()
39026     {
39027         
39028         this.assignDocWin();
39029         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39030         
39031         var range = this.createRange(sel);
39032          
39033         try {
39034             var p = range.commonAncestorContainer;
39035             while (p.nodeType == 3) { // text node
39036                 p = p.parentNode;
39037             }
39038             return p;
39039         } catch (e) {
39040             return null;
39041         }
39042     
39043     },
39044     
39045     
39046     
39047     // BC Hacks - cause I cant work out what i was trying to do..
39048     rangeIntersectsNode : function(range, node)
39049     {
39050         var nodeRange = node.ownerDocument.createRange();
39051         try {
39052             nodeRange.selectNode(node);
39053         }
39054         catch (e) {
39055             nodeRange.selectNodeContents(node);
39056         }
39057
39058         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39059                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39060     },
39061     rangeCompareNode : function(range, node) {
39062         var nodeRange = node.ownerDocument.createRange();
39063         try {
39064             nodeRange.selectNode(node);
39065         } catch (e) {
39066             nodeRange.selectNodeContents(node);
39067         }
39068         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39069         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39070
39071         if (nodeIsBefore && !nodeIsAfter)
39072             return 0;
39073         if (!nodeIsBefore && nodeIsAfter)
39074             return 1;
39075         if (nodeIsBefore && nodeIsAfter)
39076             return 2;
39077
39078         return 3;
39079     },
39080
39081     // private? - in a new class?
39082     cleanUpPaste :  function()
39083     {
39084         // cleans up the whole document..
39085       //  console.log('cleanuppaste');
39086         this.cleanUpChildren(this.doc.body)
39087         
39088         
39089     },
39090     cleanUpChildren : function (n)
39091     {
39092         if (!n.childNodes.length) {
39093             return;
39094         }
39095         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39096            this.cleanUpChild(n.childNodes[i]);
39097         }
39098     },
39099     
39100     
39101         
39102     
39103     cleanUpChild : function (node)
39104     {
39105         //console.log(node);
39106         if (node.nodeName == "#text") {
39107             // clean up silly Windows -- stuff?
39108             return; 
39109         }
39110         if (node.nodeName == "#comment") {
39111             node.parentNode.removeChild(node);
39112             // clean up silly Windows -- stuff?
39113             return; 
39114         }
39115         
39116         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39117             // remove node.
39118             node.parentNode.removeChild(node);
39119             return;
39120             
39121         }
39122         if (!node.attributes || !node.attributes.length) {
39123             this.cleanUpChildren(node);
39124             return;
39125         }
39126         
39127         function cleanAttr(n,v)
39128         {
39129             
39130             if (v.match(/^\./) || v.match(/^\//)) {
39131                 return;
39132             }
39133             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39134                 return;
39135             }
39136             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39137             node.removeAttribute(n);
39138             
39139         }
39140         
39141         function cleanStyle(n,v)
39142         {
39143             if (v.match(/expression/)) { //XSS?? should we even bother..
39144                 node.removeAttribute(n);
39145                 return;
39146             }
39147             
39148             
39149             var parts = v.split(/;/);
39150             Roo.each(parts, function(p) {
39151                 p = p.replace(/\s+/g,'');
39152                 if (!p.length) {
39153                     return;
39154                 }
39155                 var l = p.split(':').shift().replace(/\s+/g,'');
39156                 
39157                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39158                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39159                     node.removeAttribute(n);
39160                     return false;
39161                 }
39162             });
39163             
39164             
39165         }
39166         
39167         
39168         for (var i = node.attributes.length-1; i > -1 ; i--) {
39169             var a = node.attributes[i];
39170             //console.log(a);
39171             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39172                 node.removeAttribute(a.name);
39173                 return;
39174             }
39175             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39176                 cleanAttr(a.name,a.value); // fixme..
39177                 return;
39178             }
39179             if (a.name == 'style') {
39180                 cleanStyle(a.name,a.value);
39181             }
39182             /// clean up MS crap..
39183             if (a.name == 'class') {
39184                 if (a.value.match(/^Mso/)) {
39185                     node.className = '';
39186                 }
39187             }
39188             
39189             // style cleanup!?
39190             // class cleanup?
39191             
39192         }
39193         
39194         
39195         this.cleanUpChildren(node);
39196         
39197         
39198     }
39199     
39200     
39201     // hide stuff that is not compatible
39202     /**
39203      * @event blur
39204      * @hide
39205      */
39206     /**
39207      * @event change
39208      * @hide
39209      */
39210     /**
39211      * @event focus
39212      * @hide
39213      */
39214     /**
39215      * @event specialkey
39216      * @hide
39217      */
39218     /**
39219      * @cfg {String} fieldClass @hide
39220      */
39221     /**
39222      * @cfg {String} focusClass @hide
39223      */
39224     /**
39225      * @cfg {String} autoCreate @hide
39226      */
39227     /**
39228      * @cfg {String} inputType @hide
39229      */
39230     /**
39231      * @cfg {String} invalidClass @hide
39232      */
39233     /**
39234      * @cfg {String} invalidText @hide
39235      */
39236     /**
39237      * @cfg {String} msgFx @hide
39238      */
39239     /**
39240      * @cfg {String} validateOnBlur @hide
39241      */
39242 });
39243
39244 Roo.form.HtmlEditor.white = [
39245         'area', 'br', 'img', 'input', 'hr', 'wbr',
39246         
39247        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39248        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39249        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39250        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39251        'table',   'ul',         'xmp', 
39252        
39253        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39254       'thead',   'tr', 
39255      
39256       'dir', 'menu', 'ol', 'ul', 'dl',
39257        
39258       'embed',  'object'
39259 ];
39260
39261
39262 Roo.form.HtmlEditor.black = [
39263     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39264         'applet', // 
39265         'base',   'basefont', 'bgsound', 'blink',  'body', 
39266         'frame',  'frameset', 'head',    'html',   'ilayer', 
39267         'iframe', 'layer',  'link',     'meta',    'object',   
39268         'script', 'style' ,'title',  'xml' // clean later..
39269 ];
39270 Roo.form.HtmlEditor.clean = [
39271     'script', 'style', 'title', 'xml'
39272 ];
39273
39274 // attributes..
39275
39276 Roo.form.HtmlEditor.ablack = [
39277     'on'
39278 ];
39279     
39280 Roo.form.HtmlEditor.aclean = [ 
39281     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39282 ];
39283
39284 // protocols..
39285 Roo.form.HtmlEditor.pwhite= [
39286         'http',  'https',  'mailto'
39287 ];
39288
39289 Roo.form.HtmlEditor.cwhite= [
39290         'text-align',
39291         'font-size'
39292 ];
39293
39294 // <script type="text/javascript">
39295 /*
39296  * Based on
39297  * Ext JS Library 1.1.1
39298  * Copyright(c) 2006-2007, Ext JS, LLC.
39299  *  
39300  
39301  */
39302
39303 /**
39304  * @class Roo.form.HtmlEditorToolbar1
39305  * Basic Toolbar
39306  * 
39307  * Usage:
39308  *
39309  new Roo.form.HtmlEditor({
39310     ....
39311     toolbars : [
39312         new Roo.form.HtmlEditorToolbar1({
39313             disable : { fonts: 1 , format: 1, ..., ... , ...],
39314             btns : [ .... ]
39315         })
39316     }
39317      
39318  * 
39319  * @cfg {Object} disable List of elements to disable..
39320  * @cfg {Array} btns List of additional buttons.
39321  * 
39322  * 
39323  * NEEDS Extra CSS? 
39324  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39325  */
39326  
39327 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39328 {
39329     
39330     Roo.apply(this, config);
39331     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39332     // dont call parent... till later.
39333 }
39334
39335 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39336     
39337     tb: false,
39338     
39339     rendered: false,
39340     
39341     editor : false,
39342     /**
39343      * @cfg {Object} disable  List of toolbar elements to disable
39344          
39345      */
39346     disable : false,
39347       /**
39348      * @cfg {Array} fontFamilies An array of available font families
39349      */
39350     fontFamilies : [
39351         'Arial',
39352         'Courier New',
39353         'Tahoma',
39354         'Times New Roman',
39355         'Verdana'
39356     ],
39357     
39358     specialChars : [
39359            "&#169;",
39360           "&#174;",     
39361           "&#8482;",    
39362           "&#163;" ,    
39363          // "&#8212;",    
39364           "&#8230;",    
39365           "&#247;" ,    
39366         //  "&#225;" ,     ?? a acute?
39367            "&#8364;"    , //Euro
39368        //   "&#8220;"    ,
39369         //  "&#8221;"    ,
39370         //  "&#8226;"    ,
39371           "&#176;"  //   , // degrees
39372
39373          // "&#233;"     , // e ecute
39374          // "&#250;"     , // u ecute?
39375     ],
39376     inputElements : [ 
39377             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39378             "input:submit", "input:button", "select", "textarea", "label" ],
39379     formats : [
39380         ["p"] ,  
39381         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39382         ["pre"],[ "code"], 
39383         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39384     ],
39385      /**
39386      * @cfg {String} defaultFont default font to use.
39387      */
39388     defaultFont: 'tahoma',
39389    
39390     fontSelect : false,
39391     
39392     
39393     formatCombo : false,
39394     
39395     init : function(editor)
39396     {
39397         this.editor = editor;
39398         
39399         
39400         var fid = editor.frameId;
39401         var etb = this;
39402         function btn(id, toggle, handler){
39403             var xid = fid + '-'+ id ;
39404             return {
39405                 id : xid,
39406                 cmd : id,
39407                 cls : 'x-btn-icon x-edit-'+id,
39408                 enableToggle:toggle !== false,
39409                 scope: editor, // was editor...
39410                 handler:handler||editor.relayBtnCmd,
39411                 clickEvent:'mousedown',
39412                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39413                 tabIndex:-1
39414             };
39415         }
39416         
39417         
39418         
39419         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39420         this.tb = tb;
39421          // stop form submits
39422         tb.el.on('click', function(e){
39423             e.preventDefault(); // what does this do?
39424         });
39425
39426         if(!this.disable.font && !Roo.isSafari){
39427             /* why no safari for fonts
39428             editor.fontSelect = tb.el.createChild({
39429                 tag:'select',
39430                 tabIndex: -1,
39431                 cls:'x-font-select',
39432                 html: editor.createFontOptions()
39433             });
39434             editor.fontSelect.on('change', function(){
39435                 var font = editor.fontSelect.dom.value;
39436                 editor.relayCmd('fontname', font);
39437                 editor.deferFocus();
39438             }, editor);
39439             tb.add(
39440                 editor.fontSelect.dom,
39441                 '-'
39442             );
39443             */
39444         };
39445         if(!this.disable.formats){
39446             this.formatCombo = new Roo.form.ComboBox({
39447                 store: new Roo.data.SimpleStore({
39448                     id : 'tag',
39449                     fields: ['tag'],
39450                     data : this.formats // from states.js
39451                 }),
39452                 blockFocus : true,
39453                 //autoCreate : {tag: "div",  size: "20"},
39454                 displayField:'tag',
39455                 typeAhead: false,
39456                 mode: 'local',
39457                 editable : false,
39458                 triggerAction: 'all',
39459                 emptyText:'Add tag',
39460                 selectOnFocus:true,
39461                 width:135,
39462                 listeners : {
39463                     'select': function(c, r, i) {
39464                         editor.insertTag(r.get('tag'));
39465                         editor.focus();
39466                     }
39467                 }
39468
39469             });
39470             tb.addField(this.formatCombo);
39471             
39472         }
39473         
39474         if(!this.disable.format){
39475             tb.add(
39476                 btn('bold'),
39477                 btn('italic'),
39478                 btn('underline')
39479             );
39480         };
39481         if(!this.disable.fontSize){
39482             tb.add(
39483                 '-',
39484                 
39485                 
39486                 btn('increasefontsize', false, editor.adjustFont),
39487                 btn('decreasefontsize', false, editor.adjustFont)
39488             );
39489         };
39490         
39491         
39492         if(this.disable.colors){
39493             tb.add(
39494                 '-', {
39495                     id:editor.frameId +'-forecolor',
39496                     cls:'x-btn-icon x-edit-forecolor',
39497                     clickEvent:'mousedown',
39498                     tooltip: this.buttonTips['forecolor'] || undefined,
39499                     tabIndex:-1,
39500                     menu : new Roo.menu.ColorMenu({
39501                         allowReselect: true,
39502                         focus: Roo.emptyFn,
39503                         value:'000000',
39504                         plain:true,
39505                         selectHandler: function(cp, color){
39506                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39507                             editor.deferFocus();
39508                         },
39509                         scope: editor,
39510                         clickEvent:'mousedown'
39511                     })
39512                 }, {
39513                     id:editor.frameId +'backcolor',
39514                     cls:'x-btn-icon x-edit-backcolor',
39515                     clickEvent:'mousedown',
39516                     tooltip: this.buttonTips['backcolor'] || undefined,
39517                     tabIndex:-1,
39518                     menu : new Roo.menu.ColorMenu({
39519                         focus: Roo.emptyFn,
39520                         value:'FFFFFF',
39521                         plain:true,
39522                         allowReselect: true,
39523                         selectHandler: function(cp, color){
39524                             if(Roo.isGecko){
39525                                 editor.execCmd('useCSS', false);
39526                                 editor.execCmd('hilitecolor', color);
39527                                 editor.execCmd('useCSS', true);
39528                                 editor.deferFocus();
39529                             }else{
39530                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39531                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39532                                 editor.deferFocus();
39533                             }
39534                         },
39535                         scope:editor,
39536                         clickEvent:'mousedown'
39537                     })
39538                 }
39539             );
39540         };
39541         // now add all the items...
39542         
39543
39544         if(!this.disable.alignments){
39545             tb.add(
39546                 '-',
39547                 btn('justifyleft'),
39548                 btn('justifycenter'),
39549                 btn('justifyright')
39550             );
39551         };
39552
39553         //if(!Roo.isSafari){
39554             if(!this.disable.links){
39555                 tb.add(
39556                     '-',
39557                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39558                 );
39559             };
39560
39561             if(!this.disable.lists){
39562                 tb.add(
39563                     '-',
39564                     btn('insertorderedlist'),
39565                     btn('insertunorderedlist')
39566                 );
39567             }
39568             if(!this.disable.sourceEdit){
39569                 tb.add(
39570                     '-',
39571                     btn('sourceedit', true, function(btn){
39572                         this.toggleSourceEdit(btn.pressed);
39573                     })
39574                 );
39575             }
39576         //}
39577         
39578         var smenu = { };
39579         // special menu.. - needs to be tidied up..
39580         if (!this.disable.special) {
39581             smenu = {
39582                 text: "&#169;",
39583                 cls: 'x-edit-none',
39584                 menu : {
39585                     items : []
39586                    }
39587             };
39588             for (var i =0; i < this.specialChars.length; i++) {
39589                 smenu.menu.items.push({
39590                     
39591                     html: this.specialChars[i],
39592                     handler: function(a,b) {
39593                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39594                         
39595                     },
39596                     tabIndex:-1
39597                 });
39598             }
39599             
39600             
39601             tb.add(smenu);
39602             
39603             
39604         }
39605         if (this.btns) {
39606             for(var i =0; i< this.btns.length;i++) {
39607                 var b = this.btns[i];
39608                 b.cls =  'x-edit-none';
39609                 b.scope = editor;
39610                 tb.add(b);
39611             }
39612         
39613         }
39614         
39615         
39616         
39617         // disable everything...
39618         
39619         this.tb.items.each(function(item){
39620            if(item.id != editor.frameId+ '-sourceedit'){
39621                 item.disable();
39622             }
39623         });
39624         this.rendered = true;
39625         
39626         // the all the btns;
39627         editor.on('editorevent', this.updateToolbar, this);
39628         // other toolbars need to implement this..
39629         //editor.on('editmodechange', this.updateToolbar, this);
39630     },
39631     
39632     
39633     
39634     /**
39635      * Protected method that will not generally be called directly. It triggers
39636      * a toolbar update by reading the markup state of the current selection in the editor.
39637      */
39638     updateToolbar: function(){
39639
39640         if(!this.editor.activated){
39641             this.editor.onFirstFocus();
39642             return;
39643         }
39644
39645         var btns = this.tb.items.map, 
39646             doc = this.editor.doc,
39647             frameId = this.editor.frameId;
39648
39649         if(!this.disable.font && !Roo.isSafari){
39650             /*
39651             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39652             if(name != this.fontSelect.dom.value){
39653                 this.fontSelect.dom.value = name;
39654             }
39655             */
39656         }
39657         if(!this.disable.format){
39658             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39659             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39660             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39661         }
39662         if(!this.disable.alignments){
39663             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39664             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39665             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39666         }
39667         if(!Roo.isSafari && !this.disable.lists){
39668             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39669             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39670         }
39671         
39672         var ans = this.editor.getAllAncestors();
39673         if (this.formatCombo) {
39674             
39675             
39676             var store = this.formatCombo.store;
39677             this.formatCombo.setValue("");
39678             for (var i =0; i < ans.length;i++) {
39679                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39680                     // select it..
39681                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39682                     break;
39683                 }
39684             }
39685         }
39686         
39687         
39688         
39689         // hides menus... - so this cant be on a menu...
39690         Roo.menu.MenuMgr.hideAll();
39691
39692         //this.editorsyncValue();
39693     },
39694    
39695     
39696     createFontOptions : function(){
39697         var buf = [], fs = this.fontFamilies, ff, lc;
39698         for(var i = 0, len = fs.length; i< len; i++){
39699             ff = fs[i];
39700             lc = ff.toLowerCase();
39701             buf.push(
39702                 '<option value="',lc,'" style="font-family:',ff,';"',
39703                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39704                     ff,
39705                 '</option>'
39706             );
39707         }
39708         return buf.join('');
39709     },
39710     
39711     toggleSourceEdit : function(sourceEditMode){
39712         if(sourceEditMode === undefined){
39713             sourceEditMode = !this.sourceEditMode;
39714         }
39715         this.sourceEditMode = sourceEditMode === true;
39716         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39717         // just toggle the button?
39718         if(btn.pressed !== this.editor.sourceEditMode){
39719             btn.toggle(this.editor.sourceEditMode);
39720             return;
39721         }
39722         
39723         if(this.sourceEditMode){
39724             this.tb.items.each(function(item){
39725                 if(item.cmd != 'sourceedit'){
39726                     item.disable();
39727                 }
39728             });
39729           
39730         }else{
39731             if(this.initialized){
39732                 this.tb.items.each(function(item){
39733                     item.enable();
39734                 });
39735             }
39736             
39737         }
39738         // tell the editor that it's been pressed..
39739         this.editor.toggleSourceEdit(sourceEditMode);
39740        
39741     },
39742      /**
39743      * Object collection of toolbar tooltips for the buttons in the editor. The key
39744      * is the command id associated with that button and the value is a valid QuickTips object.
39745      * For example:
39746 <pre><code>
39747 {
39748     bold : {
39749         title: 'Bold (Ctrl+B)',
39750         text: 'Make the selected text bold.',
39751         cls: 'x-html-editor-tip'
39752     },
39753     italic : {
39754         title: 'Italic (Ctrl+I)',
39755         text: 'Make the selected text italic.',
39756         cls: 'x-html-editor-tip'
39757     },
39758     ...
39759 </code></pre>
39760     * @type Object
39761      */
39762     buttonTips : {
39763         bold : {
39764             title: 'Bold (Ctrl+B)',
39765             text: 'Make the selected text bold.',
39766             cls: 'x-html-editor-tip'
39767         },
39768         italic : {
39769             title: 'Italic (Ctrl+I)',
39770             text: 'Make the selected text italic.',
39771             cls: 'x-html-editor-tip'
39772         },
39773         underline : {
39774             title: 'Underline (Ctrl+U)',
39775             text: 'Underline the selected text.',
39776             cls: 'x-html-editor-tip'
39777         },
39778         increasefontsize : {
39779             title: 'Grow Text',
39780             text: 'Increase the font size.',
39781             cls: 'x-html-editor-tip'
39782         },
39783         decreasefontsize : {
39784             title: 'Shrink Text',
39785             text: 'Decrease the font size.',
39786             cls: 'x-html-editor-tip'
39787         },
39788         backcolor : {
39789             title: 'Text Highlight Color',
39790             text: 'Change the background color of the selected text.',
39791             cls: 'x-html-editor-tip'
39792         },
39793         forecolor : {
39794             title: 'Font Color',
39795             text: 'Change the color of the selected text.',
39796             cls: 'x-html-editor-tip'
39797         },
39798         justifyleft : {
39799             title: 'Align Text Left',
39800             text: 'Align text to the left.',
39801             cls: 'x-html-editor-tip'
39802         },
39803         justifycenter : {
39804             title: 'Center Text',
39805             text: 'Center text in the editor.',
39806             cls: 'x-html-editor-tip'
39807         },
39808         justifyright : {
39809             title: 'Align Text Right',
39810             text: 'Align text to the right.',
39811             cls: 'x-html-editor-tip'
39812         },
39813         insertunorderedlist : {
39814             title: 'Bullet List',
39815             text: 'Start a bulleted list.',
39816             cls: 'x-html-editor-tip'
39817         },
39818         insertorderedlist : {
39819             title: 'Numbered List',
39820             text: 'Start a numbered list.',
39821             cls: 'x-html-editor-tip'
39822         },
39823         createlink : {
39824             title: 'Hyperlink',
39825             text: 'Make the selected text a hyperlink.',
39826             cls: 'x-html-editor-tip'
39827         },
39828         sourceedit : {
39829             title: 'Source Edit',
39830             text: 'Switch to source editing mode.',
39831             cls: 'x-html-editor-tip'
39832         }
39833     },
39834     // private
39835     onDestroy : function(){
39836         if(this.rendered){
39837             
39838             this.tb.items.each(function(item){
39839                 if(item.menu){
39840                     item.menu.removeAll();
39841                     if(item.menu.el){
39842                         item.menu.el.destroy();
39843                     }
39844                 }
39845                 item.destroy();
39846             });
39847              
39848         }
39849     },
39850     onFirstFocus: function() {
39851         this.tb.items.each(function(item){
39852            item.enable();
39853         });
39854     }
39855 });
39856
39857
39858
39859
39860 // <script type="text/javascript">
39861 /*
39862  * Based on
39863  * Ext JS Library 1.1.1
39864  * Copyright(c) 2006-2007, Ext JS, LLC.
39865  *  
39866  
39867  */
39868
39869  
39870 /**
39871  * @class Roo.form.HtmlEditor.ToolbarContext
39872  * Context Toolbar
39873  * 
39874  * Usage:
39875  *
39876  new Roo.form.HtmlEditor({
39877     ....
39878     toolbars : [
39879         new Roo.form.HtmlEditor.ToolbarStandard(),
39880         new Roo.form.HtmlEditor.ToolbarContext()
39881         })
39882     }
39883      
39884  * 
39885  * @config : {Object} disable List of elements to disable.. (not done yet.)
39886  * 
39887  * 
39888  */
39889
39890 Roo.form.HtmlEditor.ToolbarContext = function(config)
39891 {
39892     
39893     Roo.apply(this, config);
39894     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39895     // dont call parent... till later.
39896 }
39897 Roo.form.HtmlEditor.ToolbarContext.types = {
39898     'IMG' : {
39899         width : {
39900             title: "Width",
39901             width: 40
39902         },
39903         height:  {
39904             title: "Height",
39905             width: 40
39906         },
39907         align: {
39908             title: "Align",
39909             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39910             width : 80
39911             
39912         },
39913         border: {
39914             title: "Border",
39915             width: 40
39916         },
39917         alt: {
39918             title: "Alt",
39919             width: 120
39920         },
39921         src : {
39922             title: "Src",
39923             width: 220
39924         }
39925         
39926     },
39927     'A' : {
39928         name : {
39929             title: "Name",
39930             width: 50
39931         },
39932         href:  {
39933             title: "Href",
39934             width: 220
39935         } // border?
39936         
39937     },
39938     'TABLE' : {
39939         rows : {
39940             title: "Rows",
39941             width: 20
39942         },
39943         cols : {
39944             title: "Cols",
39945             width: 20
39946         },
39947         width : {
39948             title: "Width",
39949             width: 40
39950         },
39951         height : {
39952             title: "Height",
39953             width: 40
39954         },
39955         border : {
39956             title: "Border",
39957             width: 20
39958         }
39959     },
39960     'TD' : {
39961         width : {
39962             title: "Width",
39963             width: 40
39964         },
39965         height : {
39966             title: "Height",
39967             width: 40
39968         },   
39969         align: {
39970             title: "Align",
39971             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39972             width: 40
39973         },
39974         valign: {
39975             title: "Valign",
39976             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39977             width: 40
39978         },
39979         colspan: {
39980             title: "Colspan",
39981             width: 20
39982             
39983         }
39984     },
39985     'INPUT' : {
39986         name : {
39987             title: "name",
39988             width: 120
39989         },
39990         value : {
39991             title: "Value",
39992             width: 120
39993         },
39994         width : {
39995             title: "Width",
39996             width: 40
39997         }
39998     },
39999     'LABEL' : {
40000         'for' : {
40001             title: "For",
40002             width: 120
40003         }
40004     },
40005     'TEXTAREA' : {
40006           name : {
40007             title: "name",
40008             width: 120
40009         },
40010         rows : {
40011             title: "Rows",
40012             width: 20
40013         },
40014         cols : {
40015             title: "Cols",
40016             width: 20
40017         }
40018     },
40019     'SELECT' : {
40020         name : {
40021             title: "name",
40022             width: 120
40023         },
40024         selectoptions : {
40025             title: "Options",
40026             width: 200
40027         }
40028     },
40029     'BODY' : {
40030         title : {
40031             title: "title",
40032             width: 120,
40033             disabled : true
40034         }
40035     }
40036 };
40037
40038
40039
40040 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40041     
40042     tb: false,
40043     
40044     rendered: false,
40045     
40046     editor : false,
40047     /**
40048      * @cfg {Object} disable  List of toolbar elements to disable
40049          
40050      */
40051     disable : false,
40052     
40053     
40054     
40055     toolbars : false,
40056     
40057     init : function(editor)
40058     {
40059         this.editor = editor;
40060         
40061         
40062         var fid = editor.frameId;
40063         var etb = this;
40064         function btn(id, toggle, handler){
40065             var xid = fid + '-'+ id ;
40066             return {
40067                 id : xid,
40068                 cmd : id,
40069                 cls : 'x-btn-icon x-edit-'+id,
40070                 enableToggle:toggle !== false,
40071                 scope: editor, // was editor...
40072                 handler:handler||editor.relayBtnCmd,
40073                 clickEvent:'mousedown',
40074                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40075                 tabIndex:-1
40076             };
40077         }
40078         // create a new element.
40079         var wdiv = editor.wrap.createChild({
40080                 tag: 'div'
40081             }, editor.wrap.dom.firstChild.nextSibling, true);
40082         
40083         // can we do this more than once??
40084         
40085          // stop form submits
40086       
40087  
40088         // disable everything...
40089         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40090         this.toolbars = {};
40091            
40092         for (var i in  ty) {
40093           
40094             this.toolbars[i] = this.buildToolbar(ty[i],i);
40095         }
40096         this.tb = this.toolbars.BODY;
40097         this.tb.el.show();
40098         
40099          
40100         this.rendered = true;
40101         
40102         // the all the btns;
40103         editor.on('editorevent', this.updateToolbar, this);
40104         // other toolbars need to implement this..
40105         //editor.on('editmodechange', this.updateToolbar, this);
40106     },
40107     
40108     
40109     
40110     /**
40111      * Protected method that will not generally be called directly. It triggers
40112      * a toolbar update by reading the markup state of the current selection in the editor.
40113      */
40114     updateToolbar: function(){
40115
40116         if(!this.editor.activated){
40117             this.editor.onFirstFocus();
40118             return;
40119         }
40120
40121         
40122         var ans = this.editor.getAllAncestors();
40123         
40124         // pick
40125         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40126         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40127         sel = sel ? sel : this.editor.doc.body;
40128         sel = sel.tagName.length ? sel : this.editor.doc.body;
40129         var tn = sel.tagName.toUpperCase();
40130         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40131         tn = sel.tagName.toUpperCase();
40132         if (this.tb.name  == tn) {
40133             return; // no change
40134         }
40135         this.tb.el.hide();
40136         ///console.log("show: " + tn);
40137         this.tb =  this.toolbars[tn];
40138         this.tb.el.show();
40139         this.tb.fields.each(function(e) {
40140             e.setValue(sel.getAttribute(e.name));
40141         });
40142         this.tb.selectedNode = sel;
40143         
40144         
40145         Roo.menu.MenuMgr.hideAll();
40146
40147         //this.editorsyncValue();
40148     },
40149    
40150        
40151     // private
40152     onDestroy : function(){
40153         if(this.rendered){
40154             
40155             this.tb.items.each(function(item){
40156                 if(item.menu){
40157                     item.menu.removeAll();
40158                     if(item.menu.el){
40159                         item.menu.el.destroy();
40160                     }
40161                 }
40162                 item.destroy();
40163             });
40164              
40165         }
40166     },
40167     onFirstFocus: function() {
40168         // need to do this for all the toolbars..
40169         this.tb.items.each(function(item){
40170            item.enable();
40171         });
40172     },
40173     buildToolbar: function(tlist, nm)
40174     {
40175         var editor = this.editor;
40176          // create a new element.
40177         var wdiv = editor.wrap.createChild({
40178                 tag: 'div'
40179             }, editor.wrap.dom.firstChild.nextSibling, true);
40180         
40181        
40182         var tb = new Roo.Toolbar(wdiv);
40183         tb.add(nm+ ":&nbsp;");
40184         for (var i in tlist) {
40185             var item = tlist[i];
40186             tb.add(item.title + ":&nbsp;");
40187             if (item.opts) {
40188                 // fixme
40189                 
40190               
40191                 tb.addField( new Roo.form.ComboBox({
40192                     store: new Roo.data.SimpleStore({
40193                         id : 'val',
40194                         fields: ['val'],
40195                         data : item.opts // from states.js
40196                     }),
40197                     name : i,
40198                     displayField:'val',
40199                     typeAhead: false,
40200                     mode: 'local',
40201                     editable : false,
40202                     triggerAction: 'all',
40203                     emptyText:'Select',
40204                     selectOnFocus:true,
40205                     width: item.width ? item.width  : 130,
40206                     listeners : {
40207                         'select': function(c, r, i) {
40208                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40209                         }
40210                     }
40211
40212                 }));
40213                 continue;
40214                     
40215                 
40216                 
40217                 
40218                 
40219                 tb.addField( new Roo.form.TextField({
40220                     name: i,
40221                     width: 100,
40222                     //allowBlank:false,
40223                     value: ''
40224                 }));
40225                 continue;
40226             }
40227             tb.addField( new Roo.form.TextField({
40228                 name: i,
40229                 width: item.width,
40230                 //allowBlank:true,
40231                 value: '',
40232                 listeners: {
40233                     'change' : function(f, nv, ov) {
40234                         tb.selectedNode.setAttribute(f.name, nv);
40235                     }
40236                 }
40237             }));
40238              
40239         }
40240         tb.el.on('click', function(e){
40241             e.preventDefault(); // what does this do?
40242         });
40243         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40244         tb.el.hide();
40245         tb.name = nm;
40246         // dont need to disable them... as they will get hidden
40247         return tb;
40248          
40249         
40250     }
40251     
40252     
40253     
40254     
40255 });
40256
40257
40258
40259
40260
40261 /*
40262  * Based on:
40263  * Ext JS Library 1.1.1
40264  * Copyright(c) 2006-2007, Ext JS, LLC.
40265  *
40266  * Originally Released Under LGPL - original licence link has changed is not relivant.
40267  *
40268  * Fork - LGPL
40269  * <script type="text/javascript">
40270  */
40271  
40272 /**
40273  * @class Roo.form.BasicForm
40274  * @extends Roo.util.Observable
40275  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40276  * @constructor
40277  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40278  * @param {Object} config Configuration options
40279  */
40280 Roo.form.BasicForm = function(el, config){
40281     this.allItems = [];
40282     this.childForms = [];
40283     Roo.apply(this, config);
40284     /*
40285      * The Roo.form.Field items in this form.
40286      * @type MixedCollection
40287      */
40288      
40289      
40290     this.items = new Roo.util.MixedCollection(false, function(o){
40291         return o.id || (o.id = Roo.id());
40292     });
40293     this.addEvents({
40294         /**
40295          * @event beforeaction
40296          * Fires before any action is performed. Return false to cancel the action.
40297          * @param {Form} this
40298          * @param {Action} action The action to be performed
40299          */
40300         beforeaction: true,
40301         /**
40302          * @event actionfailed
40303          * Fires when an action fails.
40304          * @param {Form} this
40305          * @param {Action} action The action that failed
40306          */
40307         actionfailed : true,
40308         /**
40309          * @event actioncomplete
40310          * Fires when an action is completed.
40311          * @param {Form} this
40312          * @param {Action} action The action that completed
40313          */
40314         actioncomplete : true
40315     });
40316     if(el){
40317         this.initEl(el);
40318     }
40319     Roo.form.BasicForm.superclass.constructor.call(this);
40320 };
40321
40322 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40323     /**
40324      * @cfg {String} method
40325      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40326      */
40327     /**
40328      * @cfg {DataReader} reader
40329      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40330      * This is optional as there is built-in support for processing JSON.
40331      */
40332     /**
40333      * @cfg {DataReader} errorReader
40334      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40335      * This is completely optional as there is built-in support for processing JSON.
40336      */
40337     /**
40338      * @cfg {String} url
40339      * The URL to use for form actions if one isn't supplied in the action options.
40340      */
40341     /**
40342      * @cfg {Boolean} fileUpload
40343      * Set to true if this form is a file upload.
40344      */
40345     /**
40346      * @cfg {Object} baseParams
40347      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40348      */
40349     /**
40350      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40351      */
40352     timeout: 30,
40353
40354     // private
40355     activeAction : null,
40356
40357     /**
40358      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40359      * or setValues() data instead of when the form was first created.
40360      */
40361     trackResetOnLoad : false,
40362     
40363     
40364     /**
40365      * childForms - used for multi-tab forms
40366      * @type {Array}
40367      */
40368     childForms : false,
40369     
40370     /**
40371      * allItems - full list of fields.
40372      * @type {Array}
40373      */
40374     allItems : false,
40375     
40376     /**
40377      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40378      * element by passing it or its id or mask the form itself by passing in true.
40379      * @type Mixed
40380      */
40381     waitMsgTarget : undefined,
40382
40383     // private
40384     initEl : function(el){
40385         this.el = Roo.get(el);
40386         this.id = this.el.id || Roo.id();
40387         this.el.on('submit', this.onSubmit, this);
40388         this.el.addClass('x-form');
40389     },
40390
40391     // private
40392     onSubmit : function(e){
40393         e.stopEvent();
40394     },
40395
40396     /**
40397      * Returns true if client-side validation on the form is successful.
40398      * @return Boolean
40399      */
40400     isValid : function(){
40401         var valid = true;
40402         this.items.each(function(f){
40403            if(!f.validate()){
40404                valid = false;
40405            }
40406         });
40407         return valid;
40408     },
40409
40410     /**
40411      * Returns true if any fields in this form have changed since their original load.
40412      * @return Boolean
40413      */
40414     isDirty : function(){
40415         var dirty = false;
40416         this.items.each(function(f){
40417            if(f.isDirty()){
40418                dirty = true;
40419                return false;
40420            }
40421         });
40422         return dirty;
40423     },
40424
40425     /**
40426      * Performs a predefined action (submit or load) or custom actions you define on this form.
40427      * @param {String} actionName The name of the action type
40428      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40429      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40430      * accept other config options):
40431      * <pre>
40432 Property          Type             Description
40433 ----------------  ---------------  ----------------------------------------------------------------------------------
40434 url               String           The url for the action (defaults to the form's url)
40435 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40436 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40437 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40438                                    validate the form on the client (defaults to false)
40439      * </pre>
40440      * @return {BasicForm} this
40441      */
40442     doAction : function(action, options){
40443         if(typeof action == 'string'){
40444             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40445         }
40446         if(this.fireEvent('beforeaction', this, action) !== false){
40447             this.beforeAction(action);
40448             action.run.defer(100, action);
40449         }
40450         return this;
40451     },
40452
40453     /**
40454      * Shortcut to do a submit action.
40455      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40456      * @return {BasicForm} this
40457      */
40458     submit : function(options){
40459         this.doAction('submit', options);
40460         return this;
40461     },
40462
40463     /**
40464      * Shortcut to do a load action.
40465      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40466      * @return {BasicForm} this
40467      */
40468     load : function(options){
40469         this.doAction('load', options);
40470         return this;
40471     },
40472
40473     /**
40474      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40475      * @param {Record} record The record to edit
40476      * @return {BasicForm} this
40477      */
40478     updateRecord : function(record){
40479         record.beginEdit();
40480         var fs = record.fields;
40481         fs.each(function(f){
40482             var field = this.findField(f.name);
40483             if(field){
40484                 record.set(f.name, field.getValue());
40485             }
40486         }, this);
40487         record.endEdit();
40488         return this;
40489     },
40490
40491     /**
40492      * Loads an Roo.data.Record into this form.
40493      * @param {Record} record The record to load
40494      * @return {BasicForm} this
40495      */
40496     loadRecord : function(record){
40497         this.setValues(record.data);
40498         return this;
40499     },
40500
40501     // private
40502     beforeAction : function(action){
40503         var o = action.options;
40504         if(o.waitMsg){
40505             if(this.waitMsgTarget === true){
40506                 this.el.mask(o.waitMsg, 'x-mask-loading');
40507             }else if(this.waitMsgTarget){
40508                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40509                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
40510             }else{
40511                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
40512             }
40513         }
40514     },
40515
40516     // private
40517     afterAction : function(action, success){
40518         this.activeAction = null;
40519         var o = action.options;
40520         if(o.waitMsg){
40521             if(this.waitMsgTarget === true){
40522                 this.el.unmask();
40523             }else if(this.waitMsgTarget){
40524                 this.waitMsgTarget.unmask();
40525             }else{
40526                 Roo.MessageBox.updateProgress(1);
40527                 Roo.MessageBox.hide();
40528             }
40529         }
40530         if(success){
40531             if(o.reset){
40532                 this.reset();
40533             }
40534             Roo.callback(o.success, o.scope, [this, action]);
40535             this.fireEvent('actioncomplete', this, action);
40536         }else{
40537             Roo.callback(o.failure, o.scope, [this, action]);
40538             this.fireEvent('actionfailed', this, action);
40539         }
40540     },
40541
40542     /**
40543      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40544      * @param {String} id The value to search for
40545      * @return Field
40546      */
40547     findField : function(id){
40548         var field = this.items.get(id);
40549         if(!field){
40550             this.items.each(function(f){
40551                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40552                     field = f;
40553                     return false;
40554                 }
40555             });
40556         }
40557         return field || null;
40558     },
40559
40560     /**
40561      * Add a secondary form to this one, 
40562      * Used to provide tabbed forms. One form is primary, with hidden values 
40563      * which mirror the elements from the other forms.
40564      * 
40565      * @param {Roo.form.Form} form to add.
40566      * 
40567      */
40568     addForm : function(form)
40569     {
40570        
40571         if (this.childForms.indexOf(form) > -1) {
40572             // already added..
40573             return;
40574         }
40575         this.childForms.push(form);
40576         var n = '';
40577         Roo.each(form.allItems, function (fe) {
40578             
40579             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40580             if (this.findField(n)) { // already added..
40581                 return;
40582             }
40583             var add = new Roo.form.Hidden({
40584                 name : n
40585             });
40586             add.render(this.el);
40587             
40588             this.add( add );
40589         }, this);
40590         
40591     },
40592     /**
40593      * Mark fields in this form invalid in bulk.
40594      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40595      * @return {BasicForm} this
40596      */
40597     markInvalid : function(errors){
40598         if(errors instanceof Array){
40599             for(var i = 0, len = errors.length; i < len; i++){
40600                 var fieldError = errors[i];
40601                 var f = this.findField(fieldError.id);
40602                 if(f){
40603                     f.markInvalid(fieldError.msg);
40604                 }
40605             }
40606         }else{
40607             var field, id;
40608             for(id in errors){
40609                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40610                     field.markInvalid(errors[id]);
40611                 }
40612             }
40613         }
40614         Roo.each(this.childForms || [], function (f) {
40615             f.markInvalid(errors);
40616         });
40617         
40618         return this;
40619     },
40620
40621     /**
40622      * Set values for fields in this form in bulk.
40623      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40624      * @return {BasicForm} this
40625      */
40626     setValues : function(values){
40627         if(values instanceof Array){ // array of objects
40628             for(var i = 0, len = values.length; i < len; i++){
40629                 var v = values[i];
40630                 var f = this.findField(v.id);
40631                 if(f){
40632                     f.setValue(v.value);
40633                     if(this.trackResetOnLoad){
40634                         f.originalValue = f.getValue();
40635                     }
40636                 }
40637             }
40638         }else{ // object hash
40639             var field, id;
40640             for(id in values){
40641                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40642                     
40643                     if (field.setFromData && 
40644                         field.valueField && 
40645                         field.displayField &&
40646                         // combos' with local stores can 
40647                         // be queried via setValue()
40648                         // to set their value..
40649                         (field.store && !field.store.isLocal)
40650                         ) {
40651                         // it's a combo
40652                         var sd = { };
40653                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40654                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40655                         field.setFromData(sd);
40656                         
40657                     } else {
40658                         field.setValue(values[id]);
40659                     }
40660                     
40661                     
40662                     if(this.trackResetOnLoad){
40663                         field.originalValue = field.getValue();
40664                     }
40665                 }
40666             }
40667         }
40668          
40669         Roo.each(this.childForms || [], function (f) {
40670             f.setValues(values);
40671         });
40672                 
40673         return this;
40674     },
40675
40676     /**
40677      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40678      * they are returned as an array.
40679      * @param {Boolean} asString
40680      * @return {Object}
40681      */
40682     getValues : function(asString){
40683         if (this.childForms) {
40684             // copy values from the child forms
40685             Roo.each(this.childForms, function (f) {
40686                 this.setValues(f.getValues());
40687             }, this);
40688         }
40689         
40690         
40691         
40692         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40693         if(asString === true){
40694             return fs;
40695         }
40696         return Roo.urlDecode(fs);
40697     },
40698     
40699     /**
40700      * Returns the fields in this form as an object with key/value pairs. 
40701      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40702      * @return {Object}
40703      */
40704     getFieldValues : function()
40705     {
40706         if (this.childForms) {
40707             // copy values from the child forms
40708             Roo.each(this.childForms, function (f) {
40709                 this.setValues(f.getValues());
40710             }, this);
40711         }
40712         
40713         var ret = {};
40714         this.items.each(function(f){
40715             if (!f.getName()) {
40716                 return;
40717             }
40718             var v = f.getValue();
40719             if ((typeof(v) == 'object') && f.getRawValue) {
40720                 v = f.getRawValue() ; // dates..
40721             }
40722             ret[f.getName()] = v;
40723         });
40724         
40725         return ret;
40726     },
40727
40728     /**
40729      * Clears all invalid messages in this form.
40730      * @return {BasicForm} this
40731      */
40732     clearInvalid : function(){
40733         this.items.each(function(f){
40734            f.clearInvalid();
40735         });
40736         
40737         Roo.each(this.childForms || [], function (f) {
40738             f.clearInvalid();
40739         });
40740         
40741         
40742         return this;
40743     },
40744
40745     /**
40746      * Resets this form.
40747      * @return {BasicForm} this
40748      */
40749     reset : function(){
40750         this.items.each(function(f){
40751             f.reset();
40752         });
40753         
40754         Roo.each(this.childForms || [], function (f) {
40755             f.reset();
40756         });
40757        
40758         
40759         return this;
40760     },
40761
40762     /**
40763      * Add Roo.form components to this form.
40764      * @param {Field} field1
40765      * @param {Field} field2 (optional)
40766      * @param {Field} etc (optional)
40767      * @return {BasicForm} this
40768      */
40769     add : function(){
40770         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40771         return this;
40772     },
40773
40774
40775     /**
40776      * Removes a field from the items collection (does NOT remove its markup).
40777      * @param {Field} field
40778      * @return {BasicForm} this
40779      */
40780     remove : function(field){
40781         this.items.remove(field);
40782         return this;
40783     },
40784
40785     /**
40786      * Looks at the fields in this form, checks them for an id attribute,
40787      * and calls applyTo on the existing dom element with that id.
40788      * @return {BasicForm} this
40789      */
40790     render : function(){
40791         this.items.each(function(f){
40792             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40793                 f.applyTo(f.id);
40794             }
40795         });
40796         return this;
40797     },
40798
40799     /**
40800      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40801      * @param {Object} values
40802      * @return {BasicForm} this
40803      */
40804     applyToFields : function(o){
40805         this.items.each(function(f){
40806            Roo.apply(f, o);
40807         });
40808         return this;
40809     },
40810
40811     /**
40812      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40813      * @param {Object} values
40814      * @return {BasicForm} this
40815      */
40816     applyIfToFields : function(o){
40817         this.items.each(function(f){
40818            Roo.applyIf(f, o);
40819         });
40820         return this;
40821     }
40822 });
40823
40824 // back compat
40825 Roo.BasicForm = Roo.form.BasicForm;/*
40826  * Based on:
40827  * Ext JS Library 1.1.1
40828  * Copyright(c) 2006-2007, Ext JS, LLC.
40829  *
40830  * Originally Released Under LGPL - original licence link has changed is not relivant.
40831  *
40832  * Fork - LGPL
40833  * <script type="text/javascript">
40834  */
40835
40836 /**
40837  * @class Roo.form.Form
40838  * @extends Roo.form.BasicForm
40839  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40840  * @constructor
40841  * @param {Object} config Configuration options
40842  */
40843 Roo.form.Form = function(config){
40844     var xitems =  [];
40845     if (config.items) {
40846         xitems = config.items;
40847         delete config.items;
40848     }
40849    
40850     
40851     Roo.form.Form.superclass.constructor.call(this, null, config);
40852     this.url = this.url || this.action;
40853     if(!this.root){
40854         this.root = new Roo.form.Layout(Roo.applyIf({
40855             id: Roo.id()
40856         }, config));
40857     }
40858     this.active = this.root;
40859     /**
40860      * Array of all the buttons that have been added to this form via {@link addButton}
40861      * @type Array
40862      */
40863     this.buttons = [];
40864     this.allItems = [];
40865     this.addEvents({
40866         /**
40867          * @event clientvalidation
40868          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40869          * @param {Form} this
40870          * @param {Boolean} valid true if the form has passed client-side validation
40871          */
40872         clientvalidation: true,
40873         /**
40874          * @event rendered
40875          * Fires when the form is rendered
40876          * @param {Roo.form.Form} form
40877          */
40878         rendered : true
40879     });
40880     
40881     if (this.progressUrl) {
40882             // push a hidden field onto the list of fields..
40883             this.addxtype( {
40884                     xns: Roo.form, 
40885                     xtype : 'Hidden', 
40886                     name : 'UPLOAD_IDENTIFIER' 
40887             });
40888         }
40889         
40890     
40891     Roo.each(xitems, this.addxtype, this);
40892     
40893     
40894     
40895 };
40896
40897 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40898     /**
40899      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40900      */
40901     /**
40902      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40903      */
40904     /**
40905      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40906      */
40907     buttonAlign:'center',
40908
40909     /**
40910      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40911      */
40912     minButtonWidth:75,
40913
40914     /**
40915      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40916      * This property cascades to child containers if not set.
40917      */
40918     labelAlign:'left',
40919
40920     /**
40921      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40922      * fires a looping event with that state. This is required to bind buttons to the valid
40923      * state using the config value formBind:true on the button.
40924      */
40925     monitorValid : false,
40926
40927     /**
40928      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40929      */
40930     monitorPoll : 200,
40931     
40932     /**
40933      * @cfg {String} progressUrl - Url to return progress data 
40934      */
40935     
40936     progressUrl : false,
40937   
40938     /**
40939      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40940      * fields are added and the column is closed. If no fields are passed the column remains open
40941      * until end() is called.
40942      * @param {Object} config The config to pass to the column
40943      * @param {Field} field1 (optional)
40944      * @param {Field} field2 (optional)
40945      * @param {Field} etc (optional)
40946      * @return Column The column container object
40947      */
40948     column : function(c){
40949         var col = new Roo.form.Column(c);
40950         this.start(col);
40951         if(arguments.length > 1){ // duplicate code required because of Opera
40952             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40953             this.end();
40954         }
40955         return col;
40956     },
40957
40958     /**
40959      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40960      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40961      * until end() is called.
40962      * @param {Object} config The config to pass to the fieldset
40963      * @param {Field} field1 (optional)
40964      * @param {Field} field2 (optional)
40965      * @param {Field} etc (optional)
40966      * @return FieldSet The fieldset container object
40967      */
40968     fieldset : function(c){
40969         var fs = new Roo.form.FieldSet(c);
40970         this.start(fs);
40971         if(arguments.length > 1){ // duplicate code required because of Opera
40972             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40973             this.end();
40974         }
40975         return fs;
40976     },
40977
40978     /**
40979      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40980      * fields are added and the container is closed. If no fields are passed the container remains open
40981      * until end() is called.
40982      * @param {Object} config The config to pass to the Layout
40983      * @param {Field} field1 (optional)
40984      * @param {Field} field2 (optional)
40985      * @param {Field} etc (optional)
40986      * @return Layout The container object
40987      */
40988     container : function(c){
40989         var l = new Roo.form.Layout(c);
40990         this.start(l);
40991         if(arguments.length > 1){ // duplicate code required because of Opera
40992             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40993             this.end();
40994         }
40995         return l;
40996     },
40997
40998     /**
40999      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41000      * @param {Object} container A Roo.form.Layout or subclass of Layout
41001      * @return {Form} this
41002      */
41003     start : function(c){
41004         // cascade label info
41005         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41006         this.active.stack.push(c);
41007         c.ownerCt = this.active;
41008         this.active = c;
41009         return this;
41010     },
41011
41012     /**
41013      * Closes the current open container
41014      * @return {Form} this
41015      */
41016     end : function(){
41017         if(this.active == this.root){
41018             return this;
41019         }
41020         this.active = this.active.ownerCt;
41021         return this;
41022     },
41023
41024     /**
41025      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41026      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41027      * as the label of the field.
41028      * @param {Field} field1
41029      * @param {Field} field2 (optional)
41030      * @param {Field} etc. (optional)
41031      * @return {Form} this
41032      */
41033     add : function(){
41034         this.active.stack.push.apply(this.active.stack, arguments);
41035         this.allItems.push.apply(this.allItems,arguments);
41036         var r = [];
41037         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41038             if(a[i].isFormField){
41039                 r.push(a[i]);
41040             }
41041         }
41042         if(r.length > 0){
41043             Roo.form.Form.superclass.add.apply(this, r);
41044         }
41045         return this;
41046     },
41047     
41048
41049     
41050     
41051     
41052      /**
41053      * Find any element that has been added to a form, using it's ID or name
41054      * This can include framesets, columns etc. along with regular fields..
41055      * @param {String} id - id or name to find.
41056      
41057      * @return {Element} e - or false if nothing found.
41058      */
41059     findbyId : function(id)
41060     {
41061         var ret = false;
41062         if (!id) {
41063             return ret;
41064         }
41065         Ext.each(this.allItems, function(f){
41066             if (f.id == id || f.name == id ){
41067                 ret = f;
41068                 return false;
41069             }
41070         });
41071         return ret;
41072     },
41073
41074     
41075     
41076     /**
41077      * Render this form into the passed container. This should only be called once!
41078      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41079      * @return {Form} this
41080      */
41081     render : function(ct)
41082     {
41083         
41084         
41085         
41086         ct = Roo.get(ct);
41087         var o = this.autoCreate || {
41088             tag: 'form',
41089             method : this.method || 'POST',
41090             id : this.id || Roo.id()
41091         };
41092         this.initEl(ct.createChild(o));
41093
41094         this.root.render(this.el);
41095         
41096        
41097              
41098         this.items.each(function(f){
41099             f.render('x-form-el-'+f.id);
41100         });
41101
41102         if(this.buttons.length > 0){
41103             // tables are required to maintain order and for correct IE layout
41104             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41105                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41106                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41107             }}, null, true);
41108             var tr = tb.getElementsByTagName('tr')[0];
41109             for(var i = 0, len = this.buttons.length; i < len; i++) {
41110                 var b = this.buttons[i];
41111                 var td = document.createElement('td');
41112                 td.className = 'x-form-btn-td';
41113                 b.render(tr.appendChild(td));
41114             }
41115         }
41116         if(this.monitorValid){ // initialize after render
41117             this.startMonitoring();
41118         }
41119         this.fireEvent('rendered', this);
41120         return this;
41121     },
41122
41123     /**
41124      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41125      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41126      * object or a valid Roo.DomHelper element config
41127      * @param {Function} handler The function called when the button is clicked
41128      * @param {Object} scope (optional) The scope of the handler function
41129      * @return {Roo.Button}
41130      */
41131     addButton : function(config, handler, scope){
41132         var bc = {
41133             handler: handler,
41134             scope: scope,
41135             minWidth: this.minButtonWidth,
41136             hideParent:true
41137         };
41138         if(typeof config == "string"){
41139             bc.text = config;
41140         }else{
41141             Roo.apply(bc, config);
41142         }
41143         var btn = new Roo.Button(null, bc);
41144         this.buttons.push(btn);
41145         return btn;
41146     },
41147
41148      /**
41149      * Adds a series of form elements (using the xtype property as the factory method.
41150      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41151      * @param {Object} config 
41152      */
41153     
41154     addxtype : function()
41155     {
41156         var ar = Array.prototype.slice.call(arguments, 0);
41157         var ret = false;
41158         for(var i = 0; i < ar.length; i++) {
41159             if (!ar[i]) {
41160                 continue; // skip -- if this happends something invalid got sent, we 
41161                 // should ignore it, as basically that interface element will not show up
41162                 // and that should be pretty obvious!!
41163             }
41164             
41165             if (Roo.form[ar[i].xtype]) {
41166                 ar[i].form = this;
41167                 var fe = Roo.factory(ar[i], Roo.form);
41168                 if (!ret) {
41169                     ret = fe;
41170                 }
41171                 fe.form = this;
41172                 if (fe.store) {
41173                     fe.store.form = this;
41174                 }
41175                 if (fe.isLayout) {  
41176                          
41177                     this.start(fe);
41178                     this.allItems.push(fe);
41179                     if (fe.items && fe.addxtype) {
41180                         fe.addxtype.apply(fe, fe.items);
41181                         delete fe.items;
41182                     }
41183                      this.end();
41184                     continue;
41185                 }
41186                 
41187                 
41188                  
41189                 this.add(fe);
41190               //  console.log('adding ' + ar[i].xtype);
41191             }
41192             if (ar[i].xtype == 'Button') {  
41193                 //console.log('adding button');
41194                 //console.log(ar[i]);
41195                 this.addButton(ar[i]);
41196                 this.allItems.push(fe);
41197                 continue;
41198             }
41199             
41200             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41201                 alert('end is not supported on xtype any more, use items');
41202             //    this.end();
41203             //    //console.log('adding end');
41204             }
41205             
41206         }
41207         return ret;
41208     },
41209     
41210     /**
41211      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41212      * option "monitorValid"
41213      */
41214     startMonitoring : function(){
41215         if(!this.bound){
41216             this.bound = true;
41217             Roo.TaskMgr.start({
41218                 run : this.bindHandler,
41219                 interval : this.monitorPoll || 200,
41220                 scope: this
41221             });
41222         }
41223     },
41224
41225     /**
41226      * Stops monitoring of the valid state of this form
41227      */
41228     stopMonitoring : function(){
41229         this.bound = false;
41230     },
41231
41232     // private
41233     bindHandler : function(){
41234         if(!this.bound){
41235             return false; // stops binding
41236         }
41237         var valid = true;
41238         this.items.each(function(f){
41239             if(!f.isValid(true)){
41240                 valid = false;
41241                 return false;
41242             }
41243         });
41244         for(var i = 0, len = this.buttons.length; i < len; i++){
41245             var btn = this.buttons[i];
41246             if(btn.formBind === true && btn.disabled === valid){
41247                 btn.setDisabled(!valid);
41248             }
41249         }
41250         this.fireEvent('clientvalidation', this, valid);
41251     }
41252     
41253     
41254     
41255     
41256     
41257     
41258     
41259     
41260 });
41261
41262
41263 // back compat
41264 Roo.Form = Roo.form.Form;
41265 /*
41266  * Based on:
41267  * Ext JS Library 1.1.1
41268  * Copyright(c) 2006-2007, Ext JS, LLC.
41269  *
41270  * Originally Released Under LGPL - original licence link has changed is not relivant.
41271  *
41272  * Fork - LGPL
41273  * <script type="text/javascript">
41274  */
41275  
41276  /**
41277  * @class Roo.form.Action
41278  * Internal Class used to handle form actions
41279  * @constructor
41280  * @param {Roo.form.BasicForm} el The form element or its id
41281  * @param {Object} config Configuration options
41282  */
41283  
41284  
41285 // define the action interface
41286 Roo.form.Action = function(form, options){
41287     this.form = form;
41288     this.options = options || {};
41289 };
41290 /**
41291  * Client Validation Failed
41292  * @const 
41293  */
41294 Roo.form.Action.CLIENT_INVALID = 'client';
41295 /**
41296  * Server Validation Failed
41297  * @const 
41298  */
41299  Roo.form.Action.SERVER_INVALID = 'server';
41300  /**
41301  * Connect to Server Failed
41302  * @const 
41303  */
41304 Roo.form.Action.CONNECT_FAILURE = 'connect';
41305 /**
41306  * Reading Data from Server Failed
41307  * @const 
41308  */
41309 Roo.form.Action.LOAD_FAILURE = 'load';
41310
41311 Roo.form.Action.prototype = {
41312     type : 'default',
41313     failureType : undefined,
41314     response : undefined,
41315     result : undefined,
41316
41317     // interface method
41318     run : function(options){
41319
41320     },
41321
41322     // interface method
41323     success : function(response){
41324
41325     },
41326
41327     // interface method
41328     handleResponse : function(response){
41329
41330     },
41331
41332     // default connection failure
41333     failure : function(response){
41334         this.response = response;
41335         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41336         this.form.afterAction(this, false);
41337     },
41338
41339     processResponse : function(response){
41340         this.response = response;
41341         if(!response.responseText){
41342             return true;
41343         }
41344         this.result = this.handleResponse(response);
41345         return this.result;
41346     },
41347
41348     // utility functions used internally
41349     getUrl : function(appendParams){
41350         var url = this.options.url || this.form.url || this.form.el.dom.action;
41351         if(appendParams){
41352             var p = this.getParams();
41353             if(p){
41354                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41355             }
41356         }
41357         return url;
41358     },
41359
41360     getMethod : function(){
41361         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41362     },
41363
41364     getParams : function(){
41365         var bp = this.form.baseParams;
41366         var p = this.options.params;
41367         if(p){
41368             if(typeof p == "object"){
41369                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41370             }else if(typeof p == 'string' && bp){
41371                 p += '&' + Roo.urlEncode(bp);
41372             }
41373         }else if(bp){
41374             p = Roo.urlEncode(bp);
41375         }
41376         return p;
41377     },
41378
41379     createCallback : function(){
41380         return {
41381             success: this.success,
41382             failure: this.failure,
41383             scope: this,
41384             timeout: (this.form.timeout*1000),
41385             upload: this.form.fileUpload ? this.success : undefined
41386         };
41387     }
41388 };
41389
41390 Roo.form.Action.Submit = function(form, options){
41391     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41392 };
41393
41394 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41395     type : 'submit',
41396
41397     haveProgress : false,
41398     uploadComplete : false,
41399     
41400     // uploadProgress indicator.
41401     uploadProgress : function()
41402     {
41403         if (!this.form.progressUrl) {
41404             return;
41405         }
41406         
41407         if (!this.haveProgress) {
41408             Roo.MessageBox.progress("Uploading", "Uploading");
41409         }
41410         if (this.uploadComplete) {
41411            Roo.MessageBox.hide();
41412            return;
41413         }
41414         
41415         this.haveProgress = true;
41416    
41417         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41418         
41419         var c = new Roo.data.Connection();
41420         c.request({
41421             url : this.form.progressUrl,
41422             params: {
41423                 id : uid
41424             },
41425             method: 'GET',
41426             success : function(req){
41427                //console.log(data);
41428                 var rdata = false;
41429                 var edata;
41430                 try  {
41431                    rdata = Roo.decode(req.responseText)
41432                 } catch (e) {
41433                     Roo.log("Invalid data from server..");
41434                     Roo.log(edata);
41435                     return;
41436                 }
41437                 if (!rdata || !rdata.success) {
41438                     Roo.log(rdata);
41439                     return;
41440                 }
41441                 var data = rdata.data;
41442                 
41443                 if (this.uploadComplete) {
41444                    Roo.MessageBox.hide();
41445                    return;
41446                 }
41447                    
41448                 if (data){
41449                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41450                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41451                     );
41452                 }
41453                 this.uploadProgress.defer(2000,this);
41454             },
41455        
41456             failure: function(data) {
41457                 Roo.log('progress url failed ');
41458                 Roo.log(data);
41459             },
41460             scope : this
41461         });
41462            
41463     },
41464     
41465     
41466     run : function()
41467     {
41468         // run get Values on the form, so it syncs any secondary forms.
41469         this.form.getValues();
41470         
41471         var o = this.options;
41472         var method = this.getMethod();
41473         var isPost = method == 'POST';
41474         if(o.clientValidation === false || this.form.isValid()){
41475             
41476             if (this.form.progressUrl) {
41477                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41478                     (new Date() * 1) + '' + Math.random());
41479                     
41480             } 
41481             
41482             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41483                 form:this.form.el.dom,
41484                 url:this.getUrl(!isPost),
41485                 method: method,
41486                 params:isPost ? this.getParams() : null,
41487                 isUpload: this.form.fileUpload
41488             }));
41489             
41490             this.uploadProgress();
41491
41492         }else if (o.clientValidation !== false){ // client validation failed
41493             this.failureType = Roo.form.Action.CLIENT_INVALID;
41494             this.form.afterAction(this, false);
41495         }
41496     },
41497
41498     success : function(response)
41499     {
41500         this.uploadComplete= true;
41501         if (this.haveProgress) {
41502             Roo.MessageBox.hide();
41503         }
41504         
41505         var result = this.processResponse(response);
41506         if(result === true || result.success){
41507             this.form.afterAction(this, true);
41508             return;
41509         }
41510         if(result.errors){
41511             this.form.markInvalid(result.errors);
41512             this.failureType = Roo.form.Action.SERVER_INVALID;
41513         }
41514         this.form.afterAction(this, false);
41515     },
41516     failure : function(response)
41517     {
41518         this.uploadComplete= true;
41519         if (this.haveProgress) {
41520             Roo.MessageBox.hide();
41521         }
41522         
41523         this.response = response;
41524         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41525         this.form.afterAction(this, false);
41526     },
41527     
41528     handleResponse : function(response){
41529         if(this.form.errorReader){
41530             var rs = this.form.errorReader.read(response);
41531             var errors = [];
41532             if(rs.records){
41533                 for(var i = 0, len = rs.records.length; i < len; i++) {
41534                     var r = rs.records[i];
41535                     errors[i] = r.data;
41536                 }
41537             }
41538             if(errors.length < 1){
41539                 errors = null;
41540             }
41541             return {
41542                 success : rs.success,
41543                 errors : errors
41544             };
41545         }
41546         var ret = false;
41547         try {
41548             ret = Roo.decode(response.responseText);
41549         } catch (e) {
41550             ret = {
41551                 success: false,
41552                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41553                 errors : []
41554             };
41555         }
41556         return ret;
41557         
41558     }
41559 });
41560
41561
41562 Roo.form.Action.Load = function(form, options){
41563     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41564     this.reader = this.form.reader;
41565 };
41566
41567 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41568     type : 'load',
41569
41570     run : function(){
41571         Roo.Ajax.request(Roo.apply(
41572                 this.createCallback(), {
41573                     method:this.getMethod(),
41574                     url:this.getUrl(false),
41575                     params:this.getParams()
41576         }));
41577     },
41578
41579     success : function(response){
41580         var result = this.processResponse(response);
41581         if(result === true || !result.success || !result.data){
41582             this.failureType = Roo.form.Action.LOAD_FAILURE;
41583             this.form.afterAction(this, false);
41584             return;
41585         }
41586         this.form.clearInvalid();
41587         this.form.setValues(result.data);
41588         this.form.afterAction(this, true);
41589     },
41590
41591     handleResponse : function(response){
41592         if(this.form.reader){
41593             var rs = this.form.reader.read(response);
41594             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41595             return {
41596                 success : rs.success,
41597                 data : data
41598             };
41599         }
41600         return Roo.decode(response.responseText);
41601     }
41602 });
41603
41604 Roo.form.Action.ACTION_TYPES = {
41605     'load' : Roo.form.Action.Load,
41606     'submit' : Roo.form.Action.Submit
41607 };/*
41608  * Based on:
41609  * Ext JS Library 1.1.1
41610  * Copyright(c) 2006-2007, Ext JS, LLC.
41611  *
41612  * Originally Released Under LGPL - original licence link has changed is not relivant.
41613  *
41614  * Fork - LGPL
41615  * <script type="text/javascript">
41616  */
41617  
41618 /**
41619  * @class Roo.form.Layout
41620  * @extends Roo.Component
41621  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41622  * @constructor
41623  * @param {Object} config Configuration options
41624  */
41625 Roo.form.Layout = function(config){
41626     var xitems = [];
41627     if (config.items) {
41628         xitems = config.items;
41629         delete config.items;
41630     }
41631     Roo.form.Layout.superclass.constructor.call(this, config);
41632     this.stack = [];
41633     Roo.each(xitems, this.addxtype, this);
41634      
41635 };
41636
41637 Roo.extend(Roo.form.Layout, Roo.Component, {
41638     /**
41639      * @cfg {String/Object} autoCreate
41640      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41641      */
41642     /**
41643      * @cfg {String/Object/Function} style
41644      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41645      * a function which returns such a specification.
41646      */
41647     /**
41648      * @cfg {String} labelAlign
41649      * Valid values are "left," "top" and "right" (defaults to "left")
41650      */
41651     /**
41652      * @cfg {Number} labelWidth
41653      * Fixed width in pixels of all field labels (defaults to undefined)
41654      */
41655     /**
41656      * @cfg {Boolean} clear
41657      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41658      */
41659     clear : true,
41660     /**
41661      * @cfg {String} labelSeparator
41662      * The separator to use after field labels (defaults to ':')
41663      */
41664     labelSeparator : ':',
41665     /**
41666      * @cfg {Boolean} hideLabels
41667      * True to suppress the display of field labels in this layout (defaults to false)
41668      */
41669     hideLabels : false,
41670
41671     // private
41672     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41673     
41674     isLayout : true,
41675     
41676     // private
41677     onRender : function(ct, position){
41678         if(this.el){ // from markup
41679             this.el = Roo.get(this.el);
41680         }else {  // generate
41681             var cfg = this.getAutoCreate();
41682             this.el = ct.createChild(cfg, position);
41683         }
41684         if(this.style){
41685             this.el.applyStyles(this.style);
41686         }
41687         if(this.labelAlign){
41688             this.el.addClass('x-form-label-'+this.labelAlign);
41689         }
41690         if(this.hideLabels){
41691             this.labelStyle = "display:none";
41692             this.elementStyle = "padding-left:0;";
41693         }else{
41694             if(typeof this.labelWidth == 'number'){
41695                 this.labelStyle = "width:"+this.labelWidth+"px;";
41696                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41697             }
41698             if(this.labelAlign == 'top'){
41699                 this.labelStyle = "width:auto;";
41700                 this.elementStyle = "padding-left:0;";
41701             }
41702         }
41703         var stack = this.stack;
41704         var slen = stack.length;
41705         if(slen > 0){
41706             if(!this.fieldTpl){
41707                 var t = new Roo.Template(
41708                     '<div class="x-form-item {5}">',
41709                         '<label for="{0}" style="{2}">{1}{4}</label>',
41710                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41711                         '</div>',
41712                     '</div><div class="x-form-clear-left"></div>'
41713                 );
41714                 t.disableFormats = true;
41715                 t.compile();
41716                 Roo.form.Layout.prototype.fieldTpl = t;
41717             }
41718             for(var i = 0; i < slen; i++) {
41719                 if(stack[i].isFormField){
41720                     this.renderField(stack[i]);
41721                 }else{
41722                     this.renderComponent(stack[i]);
41723                 }
41724             }
41725         }
41726         if(this.clear){
41727             this.el.createChild({cls:'x-form-clear'});
41728         }
41729     },
41730
41731     // private
41732     renderField : function(f){
41733         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41734                f.id, //0
41735                f.fieldLabel, //1
41736                f.labelStyle||this.labelStyle||'', //2
41737                this.elementStyle||'', //3
41738                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41739                f.itemCls||this.itemCls||''  //5
41740        ], true).getPrevSibling());
41741     },
41742
41743     // private
41744     renderComponent : function(c){
41745         c.render(c.isLayout ? this.el : this.el.createChild());    
41746     },
41747     /**
41748      * Adds a object form elements (using the xtype property as the factory method.)
41749      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41750      * @param {Object} config 
41751      */
41752     addxtype : function(o)
41753     {
41754         // create the lement.
41755         o.form = this.form;
41756         var fe = Roo.factory(o, Roo.form);
41757         this.form.allItems.push(fe);
41758         this.stack.push(fe);
41759         
41760         if (fe.isFormField) {
41761             this.form.items.add(fe);
41762         }
41763          
41764         return fe;
41765     }
41766 });
41767
41768 /**
41769  * @class Roo.form.Column
41770  * @extends Roo.form.Layout
41771  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41772  * @constructor
41773  * @param {Object} config Configuration options
41774  */
41775 Roo.form.Column = function(config){
41776     Roo.form.Column.superclass.constructor.call(this, config);
41777 };
41778
41779 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41780     /**
41781      * @cfg {Number/String} width
41782      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41783      */
41784     /**
41785      * @cfg {String/Object} autoCreate
41786      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41787      */
41788
41789     // private
41790     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41791
41792     // private
41793     onRender : function(ct, position){
41794         Roo.form.Column.superclass.onRender.call(this, ct, position);
41795         if(this.width){
41796             this.el.setWidth(this.width);
41797         }
41798     }
41799 });
41800
41801
41802 /**
41803  * @class Roo.form.Row
41804  * @extends Roo.form.Layout
41805  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41806  * @constructor
41807  * @param {Object} config Configuration options
41808  */
41809
41810  
41811 Roo.form.Row = function(config){
41812     Roo.form.Row.superclass.constructor.call(this, config);
41813 };
41814  
41815 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41816       /**
41817      * @cfg {Number/String} width
41818      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41819      */
41820     /**
41821      * @cfg {Number/String} height
41822      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41823      */
41824     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41825     
41826     padWidth : 20,
41827     // private
41828     onRender : function(ct, position){
41829         //console.log('row render');
41830         if(!this.rowTpl){
41831             var t = new Roo.Template(
41832                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41833                     '<label for="{0}" style="{2}">{1}{4}</label>',
41834                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41835                     '</div>',
41836                 '</div>'
41837             );
41838             t.disableFormats = true;
41839             t.compile();
41840             Roo.form.Layout.prototype.rowTpl = t;
41841         }
41842         this.fieldTpl = this.rowTpl;
41843         
41844         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41845         var labelWidth = 100;
41846         
41847         if ((this.labelAlign != 'top')) {
41848             if (typeof this.labelWidth == 'number') {
41849                 labelWidth = this.labelWidth
41850             }
41851             this.padWidth =  20 + labelWidth;
41852             
41853         }
41854         
41855         Roo.form.Column.superclass.onRender.call(this, ct, position);
41856         if(this.width){
41857             this.el.setWidth(this.width);
41858         }
41859         if(this.height){
41860             this.el.setHeight(this.height);
41861         }
41862     },
41863     
41864     // private
41865     renderField : function(f){
41866         f.fieldEl = this.fieldTpl.append(this.el, [
41867                f.id, f.fieldLabel,
41868                f.labelStyle||this.labelStyle||'',
41869                this.elementStyle||'',
41870                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41871                f.itemCls||this.itemCls||'',
41872                f.width ? f.width + this.padWidth : 160 + this.padWidth
41873        ],true);
41874     }
41875 });
41876  
41877
41878 /**
41879  * @class Roo.form.FieldSet
41880  * @extends Roo.form.Layout
41881  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41882  * @constructor
41883  * @param {Object} config Configuration options
41884  */
41885 Roo.form.FieldSet = function(config){
41886     Roo.form.FieldSet.superclass.constructor.call(this, config);
41887 };
41888
41889 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41890     /**
41891      * @cfg {String} legend
41892      * The text to display as the legend for the FieldSet (defaults to '')
41893      */
41894     /**
41895      * @cfg {String/Object} autoCreate
41896      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41897      */
41898
41899     // private
41900     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41901
41902     // private
41903     onRender : function(ct, position){
41904         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41905         if(this.legend){
41906             this.setLegend(this.legend);
41907         }
41908     },
41909
41910     // private
41911     setLegend : function(text){
41912         if(this.rendered){
41913             this.el.child('legend').update(text);
41914         }
41915     }
41916 });/*
41917  * Based on:
41918  * Ext JS Library 1.1.1
41919  * Copyright(c) 2006-2007, Ext JS, LLC.
41920  *
41921  * Originally Released Under LGPL - original licence link has changed is not relivant.
41922  *
41923  * Fork - LGPL
41924  * <script type="text/javascript">
41925  */
41926 /**
41927  * @class Roo.form.VTypes
41928  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41929  * @singleton
41930  */
41931 Roo.form.VTypes = function(){
41932     // closure these in so they are only created once.
41933     var alpha = /^[a-zA-Z_]+$/;
41934     var alphanum = /^[a-zA-Z0-9_]+$/;
41935     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41936     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41937
41938     // All these messages and functions are configurable
41939     return {
41940         /**
41941          * The function used to validate email addresses
41942          * @param {String} value The email address
41943          */
41944         'email' : function(v){
41945             return email.test(v);
41946         },
41947         /**
41948          * The error text to display when the email validation function returns false
41949          * @type String
41950          */
41951         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41952         /**
41953          * The keystroke filter mask to be applied on email input
41954          * @type RegExp
41955          */
41956         'emailMask' : /[a-z0-9_\.\-@]/i,
41957
41958         /**
41959          * The function used to validate URLs
41960          * @param {String} value The URL
41961          */
41962         'url' : function(v){
41963             return url.test(v);
41964         },
41965         /**
41966          * The error text to display when the url validation function returns false
41967          * @type String
41968          */
41969         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41970         
41971         /**
41972          * The function used to validate alpha values
41973          * @param {String} value The value
41974          */
41975         'alpha' : function(v){
41976             return alpha.test(v);
41977         },
41978         /**
41979          * The error text to display when the alpha validation function returns false
41980          * @type String
41981          */
41982         'alphaText' : 'This field should only contain letters and _',
41983         /**
41984          * The keystroke filter mask to be applied on alpha input
41985          * @type RegExp
41986          */
41987         'alphaMask' : /[a-z_]/i,
41988
41989         /**
41990          * The function used to validate alphanumeric values
41991          * @param {String} value The value
41992          */
41993         'alphanum' : function(v){
41994             return alphanum.test(v);
41995         },
41996         /**
41997          * The error text to display when the alphanumeric validation function returns false
41998          * @type String
41999          */
42000         'alphanumText' : 'This field should only contain letters, numbers and _',
42001         /**
42002          * The keystroke filter mask to be applied on alphanumeric input
42003          * @type RegExp
42004          */
42005         'alphanumMask' : /[a-z0-9_]/i
42006     };
42007 }();//<script type="text/javascript">
42008
42009 /**
42010  * @class Roo.form.FCKeditor
42011  * @extends Roo.form.TextArea
42012  * Wrapper around the FCKEditor http://www.fckeditor.net
42013  * @constructor
42014  * Creates a new FCKeditor
42015  * @param {Object} config Configuration options
42016  */
42017 Roo.form.FCKeditor = function(config){
42018     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42019     this.addEvents({
42020          /**
42021          * @event editorinit
42022          * Fired when the editor is initialized - you can add extra handlers here..
42023          * @param {FCKeditor} this
42024          * @param {Object} the FCK object.
42025          */
42026         editorinit : true
42027     });
42028     
42029     
42030 };
42031 Roo.form.FCKeditor.editors = { };
42032 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42033 {
42034     //defaultAutoCreate : {
42035     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42036     //},
42037     // private
42038     /**
42039      * @cfg {Object} fck options - see fck manual for details.
42040      */
42041     fckconfig : false,
42042     
42043     /**
42044      * @cfg {Object} fck toolbar set (Basic or Default)
42045      */
42046     toolbarSet : 'Basic',
42047     /**
42048      * @cfg {Object} fck BasePath
42049      */ 
42050     basePath : '/fckeditor/',
42051     
42052     
42053     frame : false,
42054     
42055     value : '',
42056     
42057    
42058     onRender : function(ct, position)
42059     {
42060         if(!this.el){
42061             this.defaultAutoCreate = {
42062                 tag: "textarea",
42063                 style:"width:300px;height:60px;",
42064                 autocomplete: "off"
42065             };
42066         }
42067         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42068         /*
42069         if(this.grow){
42070             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42071             if(this.preventScrollbars){
42072                 this.el.setStyle("overflow", "hidden");
42073             }
42074             this.el.setHeight(this.growMin);
42075         }
42076         */
42077         //console.log('onrender' + this.getId() );
42078         Roo.form.FCKeditor.editors[this.getId()] = this;
42079          
42080
42081         this.replaceTextarea() ;
42082         
42083     },
42084     
42085     getEditor : function() {
42086         return this.fckEditor;
42087     },
42088     /**
42089      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42090      * @param {Mixed} value The value to set
42091      */
42092     
42093     
42094     setValue : function(value)
42095     {
42096         //console.log('setValue: ' + value);
42097         
42098         if(typeof(value) == 'undefined') { // not sure why this is happending...
42099             return;
42100         }
42101         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42102         
42103         //if(!this.el || !this.getEditor()) {
42104         //    this.value = value;
42105             //this.setValue.defer(100,this,[value]);    
42106         //    return;
42107         //} 
42108         
42109         if(!this.getEditor()) {
42110             return;
42111         }
42112         
42113         this.getEditor().SetData(value);
42114         
42115         //
42116
42117     },
42118
42119     /**
42120      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42121      * @return {Mixed} value The field value
42122      */
42123     getValue : function()
42124     {
42125         
42126         if (this.frame && this.frame.dom.style.display == 'none') {
42127             return Roo.form.FCKeditor.superclass.getValue.call(this);
42128         }
42129         
42130         if(!this.el || !this.getEditor()) {
42131            
42132            // this.getValue.defer(100,this); 
42133             return this.value;
42134         }
42135        
42136         
42137         var value=this.getEditor().GetData();
42138         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42139         return Roo.form.FCKeditor.superclass.getValue.call(this);
42140         
42141
42142     },
42143
42144     /**
42145      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42146      * @return {Mixed} value The field value
42147      */
42148     getRawValue : function()
42149     {
42150         if (this.frame && this.frame.dom.style.display == 'none') {
42151             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42152         }
42153         
42154         if(!this.el || !this.getEditor()) {
42155             //this.getRawValue.defer(100,this); 
42156             return this.value;
42157             return;
42158         }
42159         
42160         
42161         
42162         var value=this.getEditor().GetData();
42163         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42164         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42165          
42166     },
42167     
42168     setSize : function(w,h) {
42169         
42170         
42171         
42172         //if (this.frame && this.frame.dom.style.display == 'none') {
42173         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42174         //    return;
42175         //}
42176         //if(!this.el || !this.getEditor()) {
42177         //    this.setSize.defer(100,this, [w,h]); 
42178         //    return;
42179         //}
42180         
42181         
42182         
42183         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42184         
42185         this.frame.dom.setAttribute('width', w);
42186         this.frame.dom.setAttribute('height', h);
42187         this.frame.setSize(w,h);
42188         
42189     },
42190     
42191     toggleSourceEdit : function(value) {
42192         
42193       
42194          
42195         this.el.dom.style.display = value ? '' : 'none';
42196         this.frame.dom.style.display = value ?  'none' : '';
42197         
42198     },
42199     
42200     
42201     focus: function(tag)
42202     {
42203         if (this.frame.dom.style.display == 'none') {
42204             return Roo.form.FCKeditor.superclass.focus.call(this);
42205         }
42206         if(!this.el || !this.getEditor()) {
42207             this.focus.defer(100,this, [tag]); 
42208             return;
42209         }
42210         
42211         
42212         
42213         
42214         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42215         this.getEditor().Focus();
42216         if (tgs.length) {
42217             if (!this.getEditor().Selection.GetSelection()) {
42218                 this.focus.defer(100,this, [tag]); 
42219                 return;
42220             }
42221             
42222             
42223             var r = this.getEditor().EditorDocument.createRange();
42224             r.setStart(tgs[0],0);
42225             r.setEnd(tgs[0],0);
42226             this.getEditor().Selection.GetSelection().removeAllRanges();
42227             this.getEditor().Selection.GetSelection().addRange(r);
42228             this.getEditor().Focus();
42229         }
42230         
42231     },
42232     
42233     
42234     
42235     replaceTextarea : function()
42236     {
42237         if ( document.getElementById( this.getId() + '___Frame' ) )
42238             return ;
42239         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42240         //{
42241             // We must check the elements firstly using the Id and then the name.
42242         var oTextarea = document.getElementById( this.getId() );
42243         
42244         var colElementsByName = document.getElementsByName( this.getId() ) ;
42245          
42246         oTextarea.style.display = 'none' ;
42247
42248         if ( oTextarea.tabIndex ) {            
42249             this.TabIndex = oTextarea.tabIndex ;
42250         }
42251         
42252         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42253         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42254         this.frame = Roo.get(this.getId() + '___Frame')
42255     },
42256     
42257     _getConfigHtml : function()
42258     {
42259         var sConfig = '' ;
42260
42261         for ( var o in this.fckconfig ) {
42262             sConfig += sConfig.length > 0  ? '&amp;' : '';
42263             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42264         }
42265
42266         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42267     },
42268     
42269     
42270     _getIFrameHtml : function()
42271     {
42272         var sFile = 'fckeditor.html' ;
42273         /* no idea what this is about..
42274         try
42275         {
42276             if ( (/fcksource=true/i).test( window.top.location.search ) )
42277                 sFile = 'fckeditor.original.html' ;
42278         }
42279         catch (e) { 
42280         */
42281
42282         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42283         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42284         
42285         
42286         var html = '<iframe id="' + this.getId() +
42287             '___Frame" src="' + sLink +
42288             '" width="' + this.width +
42289             '" height="' + this.height + '"' +
42290             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42291             ' frameborder="0" scrolling="no"></iframe>' ;
42292
42293         return html ;
42294     },
42295     
42296     _insertHtmlBefore : function( html, element )
42297     {
42298         if ( element.insertAdjacentHTML )       {
42299             // IE
42300             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42301         } else { // Gecko
42302             var oRange = document.createRange() ;
42303             oRange.setStartBefore( element ) ;
42304             var oFragment = oRange.createContextualFragment( html );
42305             element.parentNode.insertBefore( oFragment, element ) ;
42306         }
42307     }
42308     
42309     
42310   
42311     
42312     
42313     
42314     
42315
42316 });
42317
42318 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42319
42320 function FCKeditor_OnComplete(editorInstance){
42321     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42322     f.fckEditor = editorInstance;
42323     //console.log("loaded");
42324     f.fireEvent('editorinit', f, editorInstance);
42325
42326   
42327
42328  
42329
42330
42331
42332
42333
42334
42335
42336
42337
42338
42339
42340
42341
42342
42343
42344 //<script type="text/javascript">
42345 /**
42346  * @class Roo.form.GridField
42347  * @extends Roo.form.Field
42348  * Embed a grid (or editable grid into a form)
42349  * STATUS ALPHA
42350  * 
42351  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42352  * it needs 
42353  * xgrid.store = Roo.data.Store
42354  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42355  * xgrid.store.reader = Roo.data.JsonReader 
42356  * 
42357  * 
42358  * @constructor
42359  * Creates a new GridField
42360  * @param {Object} config Configuration options
42361  */
42362 Roo.form.GridField = function(config){
42363     Roo.form.GridField.superclass.constructor.call(this, config);
42364      
42365 };
42366
42367 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42368     /**
42369      * @cfg {Number} width  - used to restrict width of grid..
42370      */
42371     width : 100,
42372     /**
42373      * @cfg {Number} height - used to restrict height of grid..
42374      */
42375     height : 50,
42376      /**
42377      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42378          * 
42379          *}
42380      */
42381     xgrid : false, 
42382     /**
42383      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42384      * {tag: "input", type: "checkbox", autocomplete: "off"})
42385      */
42386    // defaultAutoCreate : { tag: 'div' },
42387     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42388     /**
42389      * @cfg {String} addTitle Text to include for adding a title.
42390      */
42391     addTitle : false,
42392     //
42393     onResize : function(){
42394         Roo.form.Field.superclass.onResize.apply(this, arguments);
42395     },
42396
42397     initEvents : function(){
42398         // Roo.form.Checkbox.superclass.initEvents.call(this);
42399         // has no events...
42400        
42401     },
42402
42403
42404     getResizeEl : function(){
42405         return this.wrap;
42406     },
42407
42408     getPositionEl : function(){
42409         return this.wrap;
42410     },
42411
42412     // private
42413     onRender : function(ct, position){
42414         
42415         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42416         var style = this.style;
42417         delete this.style;
42418         
42419         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42420         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42421         this.viewEl = this.wrap.createChild({ tag: 'div' });
42422         if (style) {
42423             this.viewEl.applyStyles(style);
42424         }
42425         if (this.width) {
42426             this.viewEl.setWidth(this.width);
42427         }
42428         if (this.height) {
42429             this.viewEl.setHeight(this.height);
42430         }
42431         //if(this.inputValue !== undefined){
42432         //this.setValue(this.value);
42433         
42434         
42435         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42436         
42437         
42438         this.grid.render();
42439         this.grid.getDataSource().on('remove', this.refreshValue, this);
42440         this.grid.getDataSource().on('update', this.refreshValue, this);
42441         this.grid.on('afteredit', this.refreshValue, this);
42442  
42443     },
42444      
42445     
42446     /**
42447      * Sets the value of the item. 
42448      * @param {String} either an object  or a string..
42449      */
42450     setValue : function(v){
42451         //this.value = v;
42452         v = v || []; // empty set..
42453         // this does not seem smart - it really only affects memoryproxy grids..
42454         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42455             var ds = this.grid.getDataSource();
42456             // assumes a json reader..
42457             var data = {}
42458             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42459             ds.loadData( data);
42460         }
42461         Roo.form.GridField.superclass.setValue.call(this, v);
42462         this.refreshValue();
42463         // should load data in the grid really....
42464     },
42465     
42466     // private
42467     refreshValue: function() {
42468          var val = [];
42469         this.grid.getDataSource().each(function(r) {
42470             val.push(r.data);
42471         });
42472         this.el.dom.value = Roo.encode(val);
42473     }
42474     
42475      
42476     
42477     
42478 });/*
42479  * Based on:
42480  * Ext JS Library 1.1.1
42481  * Copyright(c) 2006-2007, Ext JS, LLC.
42482  *
42483  * Originally Released Under LGPL - original licence link has changed is not relivant.
42484  *
42485  * Fork - LGPL
42486  * <script type="text/javascript">
42487  */
42488 /**
42489  * @class Roo.form.DisplayField
42490  * @extends Roo.form.Field
42491  * A generic Field to display non-editable data.
42492  * @constructor
42493  * Creates a new Display Field item.
42494  * @param {Object} config Configuration options
42495  */
42496 Roo.form.DisplayField = function(config){
42497     Roo.form.DisplayField.superclass.constructor.call(this, config);
42498     
42499 };
42500
42501 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42502     inputType:      'hidden',
42503     allowBlank:     true,
42504     readOnly:         true,
42505     
42506  
42507     /**
42508      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42509      */
42510     focusClass : undefined,
42511     /**
42512      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42513      */
42514     fieldClass: 'x-form-field',
42515     
42516      /**
42517      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42518      */
42519     valueRenderer: undefined,
42520     
42521     width: 100,
42522     /**
42523      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42524      * {tag: "input", type: "checkbox", autocomplete: "off"})
42525      */
42526      
42527  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42528
42529     onResize : function(){
42530         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42531         
42532     },
42533
42534     initEvents : function(){
42535         // Roo.form.Checkbox.superclass.initEvents.call(this);
42536         // has no events...
42537        
42538     },
42539
42540
42541     getResizeEl : function(){
42542         return this.wrap;
42543     },
42544
42545     getPositionEl : function(){
42546         return this.wrap;
42547     },
42548
42549     // private
42550     onRender : function(ct, position){
42551         
42552         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42553         //if(this.inputValue !== undefined){
42554         this.wrap = this.el.wrap();
42555         
42556         this.viewEl = this.wrap.createChild({ tag: 'div'});
42557         
42558         if (this.bodyStyle) {
42559             this.viewEl.applyStyles(this.bodyStyle);
42560         }
42561         //this.viewEl.setStyle('padding', '2px');
42562         
42563         this.setValue(this.value);
42564         
42565     },
42566 /*
42567     // private
42568     initValue : Roo.emptyFn,
42569
42570   */
42571
42572         // private
42573     onClick : function(){
42574         
42575     },
42576
42577     /**
42578      * Sets the checked state of the checkbox.
42579      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42580      */
42581     setValue : function(v){
42582         this.value = v;
42583         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42584         // this might be called before we have a dom element..
42585         if (!this.viewEl) {
42586             return;
42587         }
42588         this.viewEl.dom.innerHTML = html;
42589         Roo.form.DisplayField.superclass.setValue.call(this, v);
42590
42591     }
42592 });//<script type="text/javasscript">
42593  
42594
42595 /**
42596  * @class Roo.DDView
42597  * A DnD enabled version of Roo.View.
42598  * @param {Element/String} container The Element in which to create the View.
42599  * @param {String} tpl The template string used to create the markup for each element of the View
42600  * @param {Object} config The configuration properties. These include all the config options of
42601  * {@link Roo.View} plus some specific to this class.<br>
42602  * <p>
42603  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42604  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42605  * <p>
42606  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42607 .x-view-drag-insert-above {
42608         border-top:1px dotted #3366cc;
42609 }
42610 .x-view-drag-insert-below {
42611         border-bottom:1px dotted #3366cc;
42612 }
42613 </code></pre>
42614  * 
42615  */
42616  
42617 Roo.DDView = function(container, tpl, config) {
42618     Roo.DDView.superclass.constructor.apply(this, arguments);
42619     this.getEl().setStyle("outline", "0px none");
42620     this.getEl().unselectable();
42621     if (this.dragGroup) {
42622                 this.setDraggable(this.dragGroup.split(","));
42623     }
42624     if (this.dropGroup) {
42625                 this.setDroppable(this.dropGroup.split(","));
42626     }
42627     if (this.deletable) {
42628         this.setDeletable();
42629     }
42630     this.isDirtyFlag = false;
42631         this.addEvents({
42632                 "drop" : true
42633         });
42634 };
42635
42636 Roo.extend(Roo.DDView, Roo.View, {
42637 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42638 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42639 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42640 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42641
42642         isFormField: true,
42643
42644         reset: Roo.emptyFn,
42645         
42646         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42647
42648         validate: function() {
42649                 return true;
42650         },
42651         
42652         destroy: function() {
42653                 this.purgeListeners();
42654                 this.getEl.removeAllListeners();
42655                 this.getEl().remove();
42656                 if (this.dragZone) {
42657                         if (this.dragZone.destroy) {
42658                                 this.dragZone.destroy();
42659                         }
42660                 }
42661                 if (this.dropZone) {
42662                         if (this.dropZone.destroy) {
42663                                 this.dropZone.destroy();
42664                         }
42665                 }
42666         },
42667
42668 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42669         getName: function() {
42670                 return this.name;
42671         },
42672
42673 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42674         setValue: function(v) {
42675                 if (!this.store) {
42676                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42677                 }
42678                 var data = {};
42679                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42680                 this.store.proxy = new Roo.data.MemoryProxy(data);
42681                 this.store.load();
42682         },
42683
42684 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42685         getValue: function() {
42686                 var result = '(';
42687                 this.store.each(function(rec) {
42688                         result += rec.id + ',';
42689                 });
42690                 return result.substr(0, result.length - 1) + ')';
42691         },
42692         
42693         getIds: function() {
42694                 var i = 0, result = new Array(this.store.getCount());
42695                 this.store.each(function(rec) {
42696                         result[i++] = rec.id;
42697                 });
42698                 return result;
42699         },
42700         
42701         isDirty: function() {
42702                 return this.isDirtyFlag;
42703         },
42704
42705 /**
42706  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42707  *      whole Element becomes the target, and this causes the drop gesture to append.
42708  */
42709     getTargetFromEvent : function(e) {
42710                 var target = e.getTarget();
42711                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42712                 target = target.parentNode;
42713                 }
42714                 if (!target) {
42715                         target = this.el.dom.lastChild || this.el.dom;
42716                 }
42717                 return target;
42718     },
42719
42720 /**
42721  *      Create the drag data which consists of an object which has the property "ddel" as
42722  *      the drag proxy element. 
42723  */
42724     getDragData : function(e) {
42725         var target = this.findItemFromChild(e.getTarget());
42726                 if(target) {
42727                         this.handleSelection(e);
42728                         var selNodes = this.getSelectedNodes();
42729             var dragData = {
42730                 source: this,
42731                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42732                 nodes: selNodes,
42733                 records: []
42734                         };
42735                         var selectedIndices = this.getSelectedIndexes();
42736                         for (var i = 0; i < selectedIndices.length; i++) {
42737                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42738                         }
42739                         if (selNodes.length == 1) {
42740                                 dragData.ddel = target.cloneNode(true); // the div element
42741                         } else {
42742                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42743                                 div.className = 'multi-proxy';
42744                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42745                                         div.appendChild(selNodes[i].cloneNode(true));
42746                                 }
42747                                 dragData.ddel = div;
42748                         }
42749             //console.log(dragData)
42750             //console.log(dragData.ddel.innerHTML)
42751                         return dragData;
42752                 }
42753         //console.log('nodragData')
42754                 return false;
42755     },
42756     
42757 /**     Specify to which ddGroup items in this DDView may be dragged. */
42758     setDraggable: function(ddGroup) {
42759         if (ddGroup instanceof Array) {
42760                 Roo.each(ddGroup, this.setDraggable, this);
42761                 return;
42762         }
42763         if (this.dragZone) {
42764                 this.dragZone.addToGroup(ddGroup);
42765         } else {
42766                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42767                                 containerScroll: true,
42768                                 ddGroup: ddGroup 
42769
42770                         });
42771 //                      Draggability implies selection. DragZone's mousedown selects the element.
42772                         if (!this.multiSelect) { this.singleSelect = true; }
42773
42774 //                      Wire the DragZone's handlers up to methods in *this*
42775                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42776                 }
42777     },
42778
42779 /**     Specify from which ddGroup this DDView accepts drops. */
42780     setDroppable: function(ddGroup) {
42781         if (ddGroup instanceof Array) {
42782                 Roo.each(ddGroup, this.setDroppable, this);
42783                 return;
42784         }
42785         if (this.dropZone) {
42786                 this.dropZone.addToGroup(ddGroup);
42787         } else {
42788                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42789                                 containerScroll: true,
42790                                 ddGroup: ddGroup
42791                         });
42792
42793 //                      Wire the DropZone's handlers up to methods in *this*
42794                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42795                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42796                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42797                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42798                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42799                 }
42800     },
42801
42802 /**     Decide whether to drop above or below a View node. */
42803     getDropPoint : function(e, n, dd){
42804         if (n == this.el.dom) { return "above"; }
42805                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42806                 var c = t + (b - t) / 2;
42807                 var y = Roo.lib.Event.getPageY(e);
42808                 if(y <= c) {
42809                         return "above";
42810                 }else{
42811                         return "below";
42812                 }
42813     },
42814
42815     onNodeEnter : function(n, dd, e, data){
42816                 return false;
42817     },
42818     
42819     onNodeOver : function(n, dd, e, data){
42820                 var pt = this.getDropPoint(e, n, dd);
42821                 // set the insert point style on the target node
42822                 var dragElClass = this.dropNotAllowed;
42823                 if (pt) {
42824                         var targetElClass;
42825                         if (pt == "above"){
42826                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42827                                 targetElClass = "x-view-drag-insert-above";
42828                         } else {
42829                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42830                                 targetElClass = "x-view-drag-insert-below";
42831                         }
42832                         if (this.lastInsertClass != targetElClass){
42833                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42834                                 this.lastInsertClass = targetElClass;
42835                         }
42836                 }
42837                 return dragElClass;
42838         },
42839
42840     onNodeOut : function(n, dd, e, data){
42841                 this.removeDropIndicators(n);
42842     },
42843
42844     onNodeDrop : function(n, dd, e, data){
42845         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42846                 return false;
42847         }
42848         var pt = this.getDropPoint(e, n, dd);
42849                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42850                 if (pt == "below") { insertAt++; }
42851                 for (var i = 0; i < data.records.length; i++) {
42852                         var r = data.records[i];
42853                         var dup = this.store.getById(r.id);
42854                         if (dup && (dd != this.dragZone)) {
42855                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42856                         } else {
42857                                 if (data.copy) {
42858                                         this.store.insert(insertAt++, r.copy());
42859                                 } else {
42860                                         data.source.isDirtyFlag = true;
42861                                         r.store.remove(r);
42862                                         this.store.insert(insertAt++, r);
42863                                 }
42864                                 this.isDirtyFlag = true;
42865                         }
42866                 }
42867                 this.dragZone.cachedTarget = null;
42868                 return true;
42869     },
42870
42871     removeDropIndicators : function(n){
42872                 if(n){
42873                         Roo.fly(n).removeClass([
42874                                 "x-view-drag-insert-above",
42875                                 "x-view-drag-insert-below"]);
42876                         this.lastInsertClass = "_noclass";
42877                 }
42878     },
42879
42880 /**
42881  *      Utility method. Add a delete option to the DDView's context menu.
42882  *      @param {String} imageUrl The URL of the "delete" icon image.
42883  */
42884         setDeletable: function(imageUrl) {
42885                 if (!this.singleSelect && !this.multiSelect) {
42886                         this.singleSelect = true;
42887                 }
42888                 var c = this.getContextMenu();
42889                 this.contextMenu.on("itemclick", function(item) {
42890                         switch (item.id) {
42891                                 case "delete":
42892                                         this.remove(this.getSelectedIndexes());
42893                                         break;
42894                         }
42895                 }, this);
42896                 this.contextMenu.add({
42897                         icon: imageUrl,
42898                         id: "delete",
42899                         text: 'Delete'
42900                 });
42901         },
42902         
42903 /**     Return the context menu for this DDView. */
42904         getContextMenu: function() {
42905                 if (!this.contextMenu) {
42906 //                      Create the View's context menu
42907                         this.contextMenu = new Roo.menu.Menu({
42908                                 id: this.id + "-contextmenu"
42909                         });
42910                         this.el.on("contextmenu", this.showContextMenu, this);
42911                 }
42912                 return this.contextMenu;
42913         },
42914         
42915         disableContextMenu: function() {
42916                 if (this.contextMenu) {
42917                         this.el.un("contextmenu", this.showContextMenu, this);
42918                 }
42919         },
42920
42921         showContextMenu: function(e, item) {
42922         item = this.findItemFromChild(e.getTarget());
42923                 if (item) {
42924                         e.stopEvent();
42925                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42926                         this.contextMenu.showAt(e.getXY());
42927             }
42928     },
42929
42930 /**
42931  *      Remove {@link Roo.data.Record}s at the specified indices.
42932  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42933  */
42934     remove: function(selectedIndices) {
42935                 selectedIndices = [].concat(selectedIndices);
42936                 for (var i = 0; i < selectedIndices.length; i++) {
42937                         var rec = this.store.getAt(selectedIndices[i]);
42938                         this.store.remove(rec);
42939                 }
42940     },
42941
42942 /**
42943  *      Double click fires the event, but also, if this is draggable, and there is only one other
42944  *      related DropZone, it transfers the selected node.
42945  */
42946     onDblClick : function(e){
42947         var item = this.findItemFromChild(e.getTarget());
42948         if(item){
42949             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42950                 return false;
42951             }
42952             if (this.dragGroup) {
42953                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42954                     while (targets.indexOf(this.dropZone) > -1) {
42955                             targets.remove(this.dropZone);
42956                                 }
42957                     if (targets.length == 1) {
42958                                         this.dragZone.cachedTarget = null;
42959                         var el = Roo.get(targets[0].getEl());
42960                         var box = el.getBox(true);
42961                         targets[0].onNodeDrop(el.dom, {
42962                                 target: el.dom,
42963                                 xy: [box.x, box.y + box.height - 1]
42964                         }, null, this.getDragData(e));
42965                     }
42966                 }
42967         }
42968     },
42969     
42970     handleSelection: function(e) {
42971                 this.dragZone.cachedTarget = null;
42972         var item = this.findItemFromChild(e.getTarget());
42973         if (!item) {
42974                 this.clearSelections(true);
42975                 return;
42976         }
42977                 if (item && (this.multiSelect || this.singleSelect)){
42978                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42979                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42980                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42981                                 this.unselect(item);
42982                         } else {
42983                                 this.select(item, this.multiSelect && e.ctrlKey);
42984                                 this.lastSelection = item;
42985                         }
42986                 }
42987     },
42988
42989     onItemClick : function(item, index, e){
42990                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42991                         return false;
42992                 }
42993                 return true;
42994     },
42995
42996     unselect : function(nodeInfo, suppressEvent){
42997                 var node = this.getNode(nodeInfo);
42998                 if(node && this.isSelected(node)){
42999                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43000                                 Roo.fly(node).removeClass(this.selectedClass);
43001                                 this.selections.remove(node);
43002                                 if(!suppressEvent){
43003                                         this.fireEvent("selectionchange", this, this.selections);
43004                                 }
43005                         }
43006                 }
43007     }
43008 });
43009 /*
43010  * Based on:
43011  * Ext JS Library 1.1.1
43012  * Copyright(c) 2006-2007, Ext JS, LLC.
43013  *
43014  * Originally Released Under LGPL - original licence link has changed is not relivant.
43015  *
43016  * Fork - LGPL
43017  * <script type="text/javascript">
43018  */
43019  
43020 /**
43021  * @class Roo.LayoutManager
43022  * @extends Roo.util.Observable
43023  * Base class for layout managers.
43024  */
43025 Roo.LayoutManager = function(container, config){
43026     Roo.LayoutManager.superclass.constructor.call(this);
43027     this.el = Roo.get(container);
43028     // ie scrollbar fix
43029     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43030         document.body.scroll = "no";
43031     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43032         this.el.position('relative');
43033     }
43034     this.id = this.el.id;
43035     this.el.addClass("x-layout-container");
43036     /** false to disable window resize monitoring @type Boolean */
43037     this.monitorWindowResize = true;
43038     this.regions = {};
43039     this.addEvents({
43040         /**
43041          * @event layout
43042          * Fires when a layout is performed. 
43043          * @param {Roo.LayoutManager} this
43044          */
43045         "layout" : true,
43046         /**
43047          * @event regionresized
43048          * Fires when the user resizes a region. 
43049          * @param {Roo.LayoutRegion} region The resized region
43050          * @param {Number} newSize The new size (width for east/west, height for north/south)
43051          */
43052         "regionresized" : true,
43053         /**
43054          * @event regioncollapsed
43055          * Fires when a region is collapsed. 
43056          * @param {Roo.LayoutRegion} region The collapsed region
43057          */
43058         "regioncollapsed" : true,
43059         /**
43060          * @event regionexpanded
43061          * Fires when a region is expanded.  
43062          * @param {Roo.LayoutRegion} region The expanded region
43063          */
43064         "regionexpanded" : true
43065     });
43066     this.updating = false;
43067     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43068 };
43069
43070 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43071     /**
43072      * Returns true if this layout is currently being updated
43073      * @return {Boolean}
43074      */
43075     isUpdating : function(){
43076         return this.updating; 
43077     },
43078     
43079     /**
43080      * Suspend the LayoutManager from doing auto-layouts while
43081      * making multiple add or remove calls
43082      */
43083     beginUpdate : function(){
43084         this.updating = true;    
43085     },
43086     
43087     /**
43088      * Restore auto-layouts and optionally disable the manager from performing a layout
43089      * @param {Boolean} noLayout true to disable a layout update 
43090      */
43091     endUpdate : function(noLayout){
43092         this.updating = false;
43093         if(!noLayout){
43094             this.layout();
43095         }    
43096     },
43097     
43098     layout: function(){
43099         
43100     },
43101     
43102     onRegionResized : function(region, newSize){
43103         this.fireEvent("regionresized", region, newSize);
43104         this.layout();
43105     },
43106     
43107     onRegionCollapsed : function(region){
43108         this.fireEvent("regioncollapsed", region);
43109     },
43110     
43111     onRegionExpanded : function(region){
43112         this.fireEvent("regionexpanded", region);
43113     },
43114         
43115     /**
43116      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43117      * performs box-model adjustments.
43118      * @return {Object} The size as an object {width: (the width), height: (the height)}
43119      */
43120     getViewSize : function(){
43121         var size;
43122         if(this.el.dom != document.body){
43123             size = this.el.getSize();
43124         }else{
43125             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43126         }
43127         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43128         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43129         return size;
43130     },
43131     
43132     /**
43133      * Returns the Element this layout is bound to.
43134      * @return {Roo.Element}
43135      */
43136     getEl : function(){
43137         return this.el;
43138     },
43139     
43140     /**
43141      * Returns the specified region.
43142      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43143      * @return {Roo.LayoutRegion}
43144      */
43145     getRegion : function(target){
43146         return this.regions[target.toLowerCase()];
43147     },
43148     
43149     onWindowResize : function(){
43150         if(this.monitorWindowResize){
43151             this.layout();
43152         }
43153     }
43154 });/*
43155  * Based on:
43156  * Ext JS Library 1.1.1
43157  * Copyright(c) 2006-2007, Ext JS, LLC.
43158  *
43159  * Originally Released Under LGPL - original licence link has changed is not relivant.
43160  *
43161  * Fork - LGPL
43162  * <script type="text/javascript">
43163  */
43164 /**
43165  * @class Roo.BorderLayout
43166  * @extends Roo.LayoutManager
43167  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43168  * please see: <br><br>
43169  * <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>
43170  * <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>
43171  * Example:
43172  <pre><code>
43173  var layout = new Roo.BorderLayout(document.body, {
43174     north: {
43175         initialSize: 25,
43176         titlebar: false
43177     },
43178     west: {
43179         split:true,
43180         initialSize: 200,
43181         minSize: 175,
43182         maxSize: 400,
43183         titlebar: true,
43184         collapsible: true
43185     },
43186     east: {
43187         split:true,
43188         initialSize: 202,
43189         minSize: 175,
43190         maxSize: 400,
43191         titlebar: true,
43192         collapsible: true
43193     },
43194     south: {
43195         split:true,
43196         initialSize: 100,
43197         minSize: 100,
43198         maxSize: 200,
43199         titlebar: true,
43200         collapsible: true
43201     },
43202     center: {
43203         titlebar: true,
43204         autoScroll:true,
43205         resizeTabs: true,
43206         minTabWidth: 50,
43207         preferredTabWidth: 150
43208     }
43209 });
43210
43211 // shorthand
43212 var CP = Roo.ContentPanel;
43213
43214 layout.beginUpdate();
43215 layout.add("north", new CP("north", "North"));
43216 layout.add("south", new CP("south", {title: "South", closable: true}));
43217 layout.add("west", new CP("west", {title: "West"}));
43218 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43219 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43220 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43221 layout.getRegion("center").showPanel("center1");
43222 layout.endUpdate();
43223 </code></pre>
43224
43225 <b>The container the layout is rendered into can be either the body element or any other element.
43226 If it is not the body element, the container needs to either be an absolute positioned element,
43227 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43228 the container size if it is not the body element.</b>
43229
43230 * @constructor
43231 * Create a new BorderLayout
43232 * @param {String/HTMLElement/Element} container The container this layout is bound to
43233 * @param {Object} config Configuration options
43234  */
43235 Roo.BorderLayout = function(container, config){
43236     config = config || {};
43237     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43238     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43239     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43240         var target = this.factory.validRegions[i];
43241         if(config[target]){
43242             this.addRegion(target, config[target]);
43243         }
43244     }
43245 };
43246
43247 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43248     /**
43249      * Creates and adds a new region if it doesn't already exist.
43250      * @param {String} target The target region key (north, south, east, west or center).
43251      * @param {Object} config The regions config object
43252      * @return {BorderLayoutRegion} The new region
43253      */
43254     addRegion : function(target, config){
43255         if(!this.regions[target]){
43256             var r = this.factory.create(target, this, config);
43257             this.bindRegion(target, r);
43258         }
43259         return this.regions[target];
43260     },
43261
43262     // private (kinda)
43263     bindRegion : function(name, r){
43264         this.regions[name] = r;
43265         r.on("visibilitychange", this.layout, this);
43266         r.on("paneladded", this.layout, this);
43267         r.on("panelremoved", this.layout, this);
43268         r.on("invalidated", this.layout, this);
43269         r.on("resized", this.onRegionResized, this);
43270         r.on("collapsed", this.onRegionCollapsed, this);
43271         r.on("expanded", this.onRegionExpanded, this);
43272     },
43273
43274     /**
43275      * Performs a layout update.
43276      */
43277     layout : function(){
43278         if(this.updating) return;
43279         var size = this.getViewSize();
43280         var w = size.width;
43281         var h = size.height;
43282         var centerW = w;
43283         var centerH = h;
43284         var centerY = 0;
43285         var centerX = 0;
43286         //var x = 0, y = 0;
43287
43288         var rs = this.regions;
43289         var north = rs["north"];
43290         var south = rs["south"]; 
43291         var west = rs["west"];
43292         var east = rs["east"];
43293         var center = rs["center"];
43294         //if(this.hideOnLayout){ // not supported anymore
43295             //c.el.setStyle("display", "none");
43296         //}
43297         if(north && north.isVisible()){
43298             var b = north.getBox();
43299             var m = north.getMargins();
43300             b.width = w - (m.left+m.right);
43301             b.x = m.left;
43302             b.y = m.top;
43303             centerY = b.height + b.y + m.bottom;
43304             centerH -= centerY;
43305             north.updateBox(this.safeBox(b));
43306         }
43307         if(south && south.isVisible()){
43308             var b = south.getBox();
43309             var m = south.getMargins();
43310             b.width = w - (m.left+m.right);
43311             b.x = m.left;
43312             var totalHeight = (b.height + m.top + m.bottom);
43313             b.y = h - totalHeight + m.top;
43314             centerH -= totalHeight;
43315             south.updateBox(this.safeBox(b));
43316         }
43317         if(west && west.isVisible()){
43318             var b = west.getBox();
43319             var m = west.getMargins();
43320             b.height = centerH - (m.top+m.bottom);
43321             b.x = m.left;
43322             b.y = centerY + m.top;
43323             var totalWidth = (b.width + m.left + m.right);
43324             centerX += totalWidth;
43325             centerW -= totalWidth;
43326             west.updateBox(this.safeBox(b));
43327         }
43328         if(east && east.isVisible()){
43329             var b = east.getBox();
43330             var m = east.getMargins();
43331             b.height = centerH - (m.top+m.bottom);
43332             var totalWidth = (b.width + m.left + m.right);
43333             b.x = w - totalWidth + m.left;
43334             b.y = centerY + m.top;
43335             centerW -= totalWidth;
43336             east.updateBox(this.safeBox(b));
43337         }
43338         if(center){
43339             var m = center.getMargins();
43340             var centerBox = {
43341                 x: centerX + m.left,
43342                 y: centerY + m.top,
43343                 width: centerW - (m.left+m.right),
43344                 height: centerH - (m.top+m.bottom)
43345             };
43346             //if(this.hideOnLayout){
43347                 //center.el.setStyle("display", "block");
43348             //}
43349             center.updateBox(this.safeBox(centerBox));
43350         }
43351         this.el.repaint();
43352         this.fireEvent("layout", this);
43353     },
43354
43355     // private
43356     safeBox : function(box){
43357         box.width = Math.max(0, box.width);
43358         box.height = Math.max(0, box.height);
43359         return box;
43360     },
43361
43362     /**
43363      * Adds a ContentPanel (or subclass) to this layout.
43364      * @param {String} target The target region key (north, south, east, west or center).
43365      * @param {Roo.ContentPanel} panel The panel to add
43366      * @return {Roo.ContentPanel} The added panel
43367      */
43368     add : function(target, panel){
43369          
43370         target = target.toLowerCase();
43371         return this.regions[target].add(panel);
43372     },
43373
43374     /**
43375      * Remove a ContentPanel (or subclass) to this layout.
43376      * @param {String} target The target region key (north, south, east, west or center).
43377      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43378      * @return {Roo.ContentPanel} The removed panel
43379      */
43380     remove : function(target, panel){
43381         target = target.toLowerCase();
43382         return this.regions[target].remove(panel);
43383     },
43384
43385     /**
43386      * Searches all regions for a panel with the specified id
43387      * @param {String} panelId
43388      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43389      */
43390     findPanel : function(panelId){
43391         var rs = this.regions;
43392         for(var target in rs){
43393             if(typeof rs[target] != "function"){
43394                 var p = rs[target].getPanel(panelId);
43395                 if(p){
43396                     return p;
43397                 }
43398             }
43399         }
43400         return null;
43401     },
43402
43403     /**
43404      * Searches all regions for a panel with the specified id and activates (shows) it.
43405      * @param {String/ContentPanel} panelId The panels id or the panel itself
43406      * @return {Roo.ContentPanel} The shown panel or null
43407      */
43408     showPanel : function(panelId) {
43409       var rs = this.regions;
43410       for(var target in rs){
43411          var r = rs[target];
43412          if(typeof r != "function"){
43413             if(r.hasPanel(panelId)){
43414                return r.showPanel(panelId);
43415             }
43416          }
43417       }
43418       return null;
43419    },
43420
43421    /**
43422      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43423      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43424      */
43425     restoreState : function(provider){
43426         if(!provider){
43427             provider = Roo.state.Manager;
43428         }
43429         var sm = new Roo.LayoutStateManager();
43430         sm.init(this, provider);
43431     },
43432
43433     /**
43434      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43435      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43436      * a valid ContentPanel config object.  Example:
43437      * <pre><code>
43438 // Create the main layout
43439 var layout = new Roo.BorderLayout('main-ct', {
43440     west: {
43441         split:true,
43442         minSize: 175,
43443         titlebar: true
43444     },
43445     center: {
43446         title:'Components'
43447     }
43448 }, 'main-ct');
43449
43450 // Create and add multiple ContentPanels at once via configs
43451 layout.batchAdd({
43452    west: {
43453        id: 'source-files',
43454        autoCreate:true,
43455        title:'Ext Source Files',
43456        autoScroll:true,
43457        fitToFrame:true
43458    },
43459    center : {
43460        el: cview,
43461        autoScroll:true,
43462        fitToFrame:true,
43463        toolbar: tb,
43464        resizeEl:'cbody'
43465    }
43466 });
43467 </code></pre>
43468      * @param {Object} regions An object containing ContentPanel configs by region name
43469      */
43470     batchAdd : function(regions){
43471         this.beginUpdate();
43472         for(var rname in regions){
43473             var lr = this.regions[rname];
43474             if(lr){
43475                 this.addTypedPanels(lr, regions[rname]);
43476             }
43477         }
43478         this.endUpdate();
43479     },
43480
43481     // private
43482     addTypedPanels : function(lr, ps){
43483         if(typeof ps == 'string'){
43484             lr.add(new Roo.ContentPanel(ps));
43485         }
43486         else if(ps instanceof Array){
43487             for(var i =0, len = ps.length; i < len; i++){
43488                 this.addTypedPanels(lr, ps[i]);
43489             }
43490         }
43491         else if(!ps.events){ // raw config?
43492             var el = ps.el;
43493             delete ps.el; // prevent conflict
43494             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43495         }
43496         else {  // panel object assumed!
43497             lr.add(ps);
43498         }
43499     },
43500     /**
43501      * Adds a xtype elements to the layout.
43502      * <pre><code>
43503
43504 layout.addxtype({
43505        xtype : 'ContentPanel',
43506        region: 'west',
43507        items: [ .... ]
43508    }
43509 );
43510
43511 layout.addxtype({
43512         xtype : 'NestedLayoutPanel',
43513         region: 'west',
43514         layout: {
43515            center: { },
43516            west: { }   
43517         },
43518         items : [ ... list of content panels or nested layout panels.. ]
43519    }
43520 );
43521 </code></pre>
43522      * @param {Object} cfg Xtype definition of item to add.
43523      */
43524     addxtype : function(cfg)
43525     {
43526         // basically accepts a pannel...
43527         // can accept a layout region..!?!?
43528        // console.log('BorderLayout add ' + cfg.xtype)
43529         
43530         if (!cfg.xtype.match(/Panel$/)) {
43531             return false;
43532         }
43533         var ret = false;
43534         var region = cfg.region;
43535         delete cfg.region;
43536         
43537           
43538         var xitems = [];
43539         if (cfg.items) {
43540             xitems = cfg.items;
43541             delete cfg.items;
43542         }
43543         
43544         
43545         switch(cfg.xtype) 
43546         {
43547             case 'ContentPanel':  // ContentPanel (el, cfg)
43548             case 'ScrollPanel':  // ContentPanel (el, cfg)
43549                 if(cfg.autoCreate) {
43550                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43551                 } else {
43552                     var el = this.el.createChild();
43553                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43554                 }
43555                 
43556                 this.add(region, ret);
43557                 break;
43558             
43559             
43560             case 'TreePanel': // our new panel!
43561                 cfg.el = this.el.createChild();
43562                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43563                 this.add(region, ret);
43564                 break;
43565             
43566             case 'NestedLayoutPanel': 
43567                 // create a new Layout (which is  a Border Layout...
43568                 var el = this.el.createChild();
43569                 var clayout = cfg.layout;
43570                 delete cfg.layout;
43571                 clayout.items   = clayout.items  || [];
43572                 // replace this exitems with the clayout ones..
43573                 xitems = clayout.items;
43574                  
43575                 
43576                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43577                     cfg.background = false;
43578                 }
43579                 var layout = new Roo.BorderLayout(el, clayout);
43580                 
43581                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43582                 //console.log('adding nested layout panel '  + cfg.toSource());
43583                 this.add(region, ret);
43584                 
43585                 break;
43586                 
43587             case 'GridPanel': 
43588             
43589                 // needs grid and region
43590                 
43591                 //var el = this.getRegion(region).el.createChild();
43592                 var el = this.el.createChild();
43593                 // create the grid first...
43594                 
43595                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43596                 delete cfg.grid;
43597                 if (region == 'center' && this.active ) {
43598                     cfg.background = false;
43599                 }
43600                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43601                 
43602                 this.add(region, ret);
43603                 if (cfg.background) {
43604                     ret.on('activate', function(gp) {
43605                         if (!gp.grid.rendered) {
43606                             gp.grid.render();
43607                         }
43608                     });
43609                 } else {
43610                     grid.render();
43611                 }
43612                 break;
43613            
43614                
43615                 
43616                 
43617             default: 
43618                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43619                 return;
43620              // GridPanel (grid, cfg)
43621             
43622         }
43623         this.beginUpdate();
43624         // add children..
43625         Roo.each(xitems, function(i)  {
43626             ret.addxtype(i);
43627         });
43628         this.endUpdate();
43629         return ret;
43630         
43631     }
43632 });
43633
43634 /**
43635  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43636  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43637  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43638  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43639  * <pre><code>
43640 // shorthand
43641 var CP = Roo.ContentPanel;
43642
43643 var layout = Roo.BorderLayout.create({
43644     north: {
43645         initialSize: 25,
43646         titlebar: false,
43647         panels: [new CP("north", "North")]
43648     },
43649     west: {
43650         split:true,
43651         initialSize: 200,
43652         minSize: 175,
43653         maxSize: 400,
43654         titlebar: true,
43655         collapsible: true,
43656         panels: [new CP("west", {title: "West"})]
43657     },
43658     east: {
43659         split:true,
43660         initialSize: 202,
43661         minSize: 175,
43662         maxSize: 400,
43663         titlebar: true,
43664         collapsible: true,
43665         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43666     },
43667     south: {
43668         split:true,
43669         initialSize: 100,
43670         minSize: 100,
43671         maxSize: 200,
43672         titlebar: true,
43673         collapsible: true,
43674         panels: [new CP("south", {title: "South", closable: true})]
43675     },
43676     center: {
43677         titlebar: true,
43678         autoScroll:true,
43679         resizeTabs: true,
43680         minTabWidth: 50,
43681         preferredTabWidth: 150,
43682         panels: [
43683             new CP("center1", {title: "Close Me", closable: true}),
43684             new CP("center2", {title: "Center Panel", closable: false})
43685         ]
43686     }
43687 }, document.body);
43688
43689 layout.getRegion("center").showPanel("center1");
43690 </code></pre>
43691  * @param config
43692  * @param targetEl
43693  */
43694 Roo.BorderLayout.create = function(config, targetEl){
43695     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43696     layout.beginUpdate();
43697     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43698     for(var j = 0, jlen = regions.length; j < jlen; j++){
43699         var lr = regions[j];
43700         if(layout.regions[lr] && config[lr].panels){
43701             var r = layout.regions[lr];
43702             var ps = config[lr].panels;
43703             layout.addTypedPanels(r, ps);
43704         }
43705     }
43706     layout.endUpdate();
43707     return layout;
43708 };
43709
43710 // private
43711 Roo.BorderLayout.RegionFactory = {
43712     // private
43713     validRegions : ["north","south","east","west","center"],
43714
43715     // private
43716     create : function(target, mgr, config){
43717         target = target.toLowerCase();
43718         if(config.lightweight || config.basic){
43719             return new Roo.BasicLayoutRegion(mgr, config, target);
43720         }
43721         switch(target){
43722             case "north":
43723                 return new Roo.NorthLayoutRegion(mgr, config);
43724             case "south":
43725                 return new Roo.SouthLayoutRegion(mgr, config);
43726             case "east":
43727                 return new Roo.EastLayoutRegion(mgr, config);
43728             case "west":
43729                 return new Roo.WestLayoutRegion(mgr, config);
43730             case "center":
43731                 return new Roo.CenterLayoutRegion(mgr, config);
43732         }
43733         throw 'Layout region "'+target+'" not supported.';
43734     }
43735 };/*
43736  * Based on:
43737  * Ext JS Library 1.1.1
43738  * Copyright(c) 2006-2007, Ext JS, LLC.
43739  *
43740  * Originally Released Under LGPL - original licence link has changed is not relivant.
43741  *
43742  * Fork - LGPL
43743  * <script type="text/javascript">
43744  */
43745  
43746 /**
43747  * @class Roo.BasicLayoutRegion
43748  * @extends Roo.util.Observable
43749  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43750  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43751  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43752  */
43753 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43754     this.mgr = mgr;
43755     this.position  = pos;
43756     this.events = {
43757         /**
43758          * @scope Roo.BasicLayoutRegion
43759          */
43760         
43761         /**
43762          * @event beforeremove
43763          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43764          * @param {Roo.LayoutRegion} this
43765          * @param {Roo.ContentPanel} panel The panel
43766          * @param {Object} e The cancel event object
43767          */
43768         "beforeremove" : true,
43769         /**
43770          * @event invalidated
43771          * Fires when the layout for this region is changed.
43772          * @param {Roo.LayoutRegion} this
43773          */
43774         "invalidated" : true,
43775         /**
43776          * @event visibilitychange
43777          * Fires when this region is shown or hidden 
43778          * @param {Roo.LayoutRegion} this
43779          * @param {Boolean} visibility true or false
43780          */
43781         "visibilitychange" : true,
43782         /**
43783          * @event paneladded
43784          * Fires when a panel is added. 
43785          * @param {Roo.LayoutRegion} this
43786          * @param {Roo.ContentPanel} panel The panel
43787          */
43788         "paneladded" : true,
43789         /**
43790          * @event panelremoved
43791          * Fires when a panel is removed. 
43792          * @param {Roo.LayoutRegion} this
43793          * @param {Roo.ContentPanel} panel The panel
43794          */
43795         "panelremoved" : true,
43796         /**
43797          * @event collapsed
43798          * Fires when this region is collapsed.
43799          * @param {Roo.LayoutRegion} this
43800          */
43801         "collapsed" : true,
43802         /**
43803          * @event expanded
43804          * Fires when this region is expanded.
43805          * @param {Roo.LayoutRegion} this
43806          */
43807         "expanded" : true,
43808         /**
43809          * @event slideshow
43810          * Fires when this region is slid into view.
43811          * @param {Roo.LayoutRegion} this
43812          */
43813         "slideshow" : true,
43814         /**
43815          * @event slidehide
43816          * Fires when this region slides out of view. 
43817          * @param {Roo.LayoutRegion} this
43818          */
43819         "slidehide" : true,
43820         /**
43821          * @event panelactivated
43822          * Fires when a panel is activated. 
43823          * @param {Roo.LayoutRegion} this
43824          * @param {Roo.ContentPanel} panel The activated panel
43825          */
43826         "panelactivated" : true,
43827         /**
43828          * @event resized
43829          * Fires when the user resizes this region. 
43830          * @param {Roo.LayoutRegion} this
43831          * @param {Number} newSize The new size (width for east/west, height for north/south)
43832          */
43833         "resized" : true
43834     };
43835     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43836     this.panels = new Roo.util.MixedCollection();
43837     this.panels.getKey = this.getPanelId.createDelegate(this);
43838     this.box = null;
43839     this.activePanel = null;
43840     // ensure listeners are added...
43841     
43842     if (config.listeners || config.events) {
43843         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43844             listeners : config.listeners || {},
43845             events : config.events || {}
43846         });
43847     }
43848     
43849     if(skipConfig !== true){
43850         this.applyConfig(config);
43851     }
43852 };
43853
43854 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43855     getPanelId : function(p){
43856         return p.getId();
43857     },
43858     
43859     applyConfig : function(config){
43860         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43861         this.config = config;
43862         
43863     },
43864     
43865     /**
43866      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43867      * the width, for horizontal (north, south) the height.
43868      * @param {Number} newSize The new width or height
43869      */
43870     resizeTo : function(newSize){
43871         var el = this.el ? this.el :
43872                  (this.activePanel ? this.activePanel.getEl() : null);
43873         if(el){
43874             switch(this.position){
43875                 case "east":
43876                 case "west":
43877                     el.setWidth(newSize);
43878                     this.fireEvent("resized", this, newSize);
43879                 break;
43880                 case "north":
43881                 case "south":
43882                     el.setHeight(newSize);
43883                     this.fireEvent("resized", this, newSize);
43884                 break;                
43885             }
43886         }
43887     },
43888     
43889     getBox : function(){
43890         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43891     },
43892     
43893     getMargins : function(){
43894         return this.margins;
43895     },
43896     
43897     updateBox : function(box){
43898         this.box = box;
43899         var el = this.activePanel.getEl();
43900         el.dom.style.left = box.x + "px";
43901         el.dom.style.top = box.y + "px";
43902         this.activePanel.setSize(box.width, box.height);
43903     },
43904     
43905     /**
43906      * Returns the container element for this region.
43907      * @return {Roo.Element}
43908      */
43909     getEl : function(){
43910         return this.activePanel;
43911     },
43912     
43913     /**
43914      * Returns true if this region is currently visible.
43915      * @return {Boolean}
43916      */
43917     isVisible : function(){
43918         return this.activePanel ? true : false;
43919     },
43920     
43921     setActivePanel : function(panel){
43922         panel = this.getPanel(panel);
43923         if(this.activePanel && this.activePanel != panel){
43924             this.activePanel.setActiveState(false);
43925             this.activePanel.getEl().setLeftTop(-10000,-10000);
43926         }
43927         this.activePanel = panel;
43928         panel.setActiveState(true);
43929         if(this.box){
43930             panel.setSize(this.box.width, this.box.height);
43931         }
43932         this.fireEvent("panelactivated", this, panel);
43933         this.fireEvent("invalidated");
43934     },
43935     
43936     /**
43937      * Show the specified panel.
43938      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43939      * @return {Roo.ContentPanel} The shown panel or null
43940      */
43941     showPanel : function(panel){
43942         if(panel = this.getPanel(panel)){
43943             this.setActivePanel(panel);
43944         }
43945         return panel;
43946     },
43947     
43948     /**
43949      * Get the active panel for this region.
43950      * @return {Roo.ContentPanel} The active panel or null
43951      */
43952     getActivePanel : function(){
43953         return this.activePanel;
43954     },
43955     
43956     /**
43957      * Add the passed ContentPanel(s)
43958      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43959      * @return {Roo.ContentPanel} The panel added (if only one was added)
43960      */
43961     add : function(panel){
43962         if(arguments.length > 1){
43963             for(var i = 0, len = arguments.length; i < len; i++) {
43964                 this.add(arguments[i]);
43965             }
43966             return null;
43967         }
43968         if(this.hasPanel(panel)){
43969             this.showPanel(panel);
43970             return panel;
43971         }
43972         var el = panel.getEl();
43973         if(el.dom.parentNode != this.mgr.el.dom){
43974             this.mgr.el.dom.appendChild(el.dom);
43975         }
43976         if(panel.setRegion){
43977             panel.setRegion(this);
43978         }
43979         this.panels.add(panel);
43980         el.setStyle("position", "absolute");
43981         if(!panel.background){
43982             this.setActivePanel(panel);
43983             if(this.config.initialSize && this.panels.getCount()==1){
43984                 this.resizeTo(this.config.initialSize);
43985             }
43986         }
43987         this.fireEvent("paneladded", this, panel);
43988         return panel;
43989     },
43990     
43991     /**
43992      * Returns true if the panel is in this region.
43993      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43994      * @return {Boolean}
43995      */
43996     hasPanel : function(panel){
43997         if(typeof panel == "object"){ // must be panel obj
43998             panel = panel.getId();
43999         }
44000         return this.getPanel(panel) ? true : false;
44001     },
44002     
44003     /**
44004      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44005      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44006      * @param {Boolean} preservePanel Overrides the config preservePanel option
44007      * @return {Roo.ContentPanel} The panel that was removed
44008      */
44009     remove : function(panel, preservePanel){
44010         panel = this.getPanel(panel);
44011         if(!panel){
44012             return null;
44013         }
44014         var e = {};
44015         this.fireEvent("beforeremove", this, panel, e);
44016         if(e.cancel === true){
44017             return null;
44018         }
44019         var panelId = panel.getId();
44020         this.panels.removeKey(panelId);
44021         return panel;
44022     },
44023     
44024     /**
44025      * Returns the panel specified or null if it's not in this region.
44026      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44027      * @return {Roo.ContentPanel}
44028      */
44029     getPanel : function(id){
44030         if(typeof id == "object"){ // must be panel obj
44031             return id;
44032         }
44033         return this.panels.get(id);
44034     },
44035     
44036     /**
44037      * Returns this regions position (north/south/east/west/center).
44038      * @return {String} 
44039      */
44040     getPosition: function(){
44041         return this.position;    
44042     }
44043 });/*
44044  * Based on:
44045  * Ext JS Library 1.1.1
44046  * Copyright(c) 2006-2007, Ext JS, LLC.
44047  *
44048  * Originally Released Under LGPL - original licence link has changed is not relivant.
44049  *
44050  * Fork - LGPL
44051  * <script type="text/javascript">
44052  */
44053  
44054 /**
44055  * @class Roo.LayoutRegion
44056  * @extends Roo.BasicLayoutRegion
44057  * This class represents a region in a layout manager.
44058  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44059  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44060  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44061  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44062  * @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})
44063  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44064  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44065  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44066  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44067  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44068  * @cfg {String} title The title for the region (overrides panel titles)
44069  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44070  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44071  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44072  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44073  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44074  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44075  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44076  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44077  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44078  * @cfg {Boolean} showPin True to show a pin button
44079 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44080 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44081 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44082 * @cfg {Number} width  For East/West panels
44083 * @cfg {Number} height For North/South panels
44084 * @cfg {Boolean} split To show the splitter
44085  */
44086 Roo.LayoutRegion = function(mgr, config, pos){
44087     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44088     var dh = Roo.DomHelper;
44089     /** This region's container element 
44090     * @type Roo.Element */
44091     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44092     /** This region's title element 
44093     * @type Roo.Element */
44094
44095     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44096         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44097         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44098     ]}, true);
44099     this.titleEl.enableDisplayMode();
44100     /** This region's title text element 
44101     * @type HTMLElement */
44102     this.titleTextEl = this.titleEl.dom.firstChild;
44103     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44104     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44105     this.closeBtn.enableDisplayMode();
44106     this.closeBtn.on("click", this.closeClicked, this);
44107     this.closeBtn.hide();
44108
44109     this.createBody(config);
44110     this.visible = true;
44111     this.collapsed = false;
44112
44113     if(config.hideWhenEmpty){
44114         this.hide();
44115         this.on("paneladded", this.validateVisibility, this);
44116         this.on("panelremoved", this.validateVisibility, this);
44117     }
44118     this.applyConfig(config);
44119 };
44120
44121 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44122
44123     createBody : function(){
44124         /** This region's body element 
44125         * @type Roo.Element */
44126         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44127     },
44128
44129     applyConfig : function(c){
44130         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44131             var dh = Roo.DomHelper;
44132             if(c.titlebar !== false){
44133                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44134                 this.collapseBtn.on("click", this.collapse, this);
44135                 this.collapseBtn.enableDisplayMode();
44136
44137                 if(c.showPin === true || this.showPin){
44138                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44139                     this.stickBtn.enableDisplayMode();
44140                     this.stickBtn.on("click", this.expand, this);
44141                     this.stickBtn.hide();
44142                 }
44143             }
44144             /** This region's collapsed element
44145             * @type Roo.Element */
44146             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44147                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44148             ]}, true);
44149             if(c.floatable !== false){
44150                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44151                this.collapsedEl.on("click", this.collapseClick, this);
44152             }
44153
44154             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44155                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44156                    id: "message", unselectable: "on", style:{"float":"left"}});
44157                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44158              }
44159             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44160             this.expandBtn.on("click", this.expand, this);
44161         }
44162         if(this.collapseBtn){
44163             this.collapseBtn.setVisible(c.collapsible == true);
44164         }
44165         this.cmargins = c.cmargins || this.cmargins ||
44166                          (this.position == "west" || this.position == "east" ?
44167                              {top: 0, left: 2, right:2, bottom: 0} :
44168                              {top: 2, left: 0, right:0, bottom: 2});
44169         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44170         this.bottomTabs = c.tabPosition != "top";
44171         this.autoScroll = c.autoScroll || false;
44172         if(this.autoScroll){
44173             this.bodyEl.setStyle("overflow", "auto");
44174         }else{
44175             this.bodyEl.setStyle("overflow", "hidden");
44176         }
44177         //if(c.titlebar !== false){
44178             if((!c.titlebar && !c.title) || c.titlebar === false){
44179                 this.titleEl.hide();
44180             }else{
44181                 this.titleEl.show();
44182                 if(c.title){
44183                     this.titleTextEl.innerHTML = c.title;
44184                 }
44185             }
44186         //}
44187         this.duration = c.duration || .30;
44188         this.slideDuration = c.slideDuration || .45;
44189         this.config = c;
44190         if(c.collapsed){
44191             this.collapse(true);
44192         }
44193         if(c.hidden){
44194             this.hide();
44195         }
44196     },
44197     /**
44198      * Returns true if this region is currently visible.
44199      * @return {Boolean}
44200      */
44201     isVisible : function(){
44202         return this.visible;
44203     },
44204
44205     /**
44206      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44207      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44208      */
44209     setCollapsedTitle : function(title){
44210         title = title || "&#160;";
44211         if(this.collapsedTitleTextEl){
44212             this.collapsedTitleTextEl.innerHTML = title;
44213         }
44214     },
44215
44216     getBox : function(){
44217         var b;
44218         if(!this.collapsed){
44219             b = this.el.getBox(false, true);
44220         }else{
44221             b = this.collapsedEl.getBox(false, true);
44222         }
44223         return b;
44224     },
44225
44226     getMargins : function(){
44227         return this.collapsed ? this.cmargins : this.margins;
44228     },
44229
44230     highlight : function(){
44231         this.el.addClass("x-layout-panel-dragover");
44232     },
44233
44234     unhighlight : function(){
44235         this.el.removeClass("x-layout-panel-dragover");
44236     },
44237
44238     updateBox : function(box){
44239         this.box = box;
44240         if(!this.collapsed){
44241             this.el.dom.style.left = box.x + "px";
44242             this.el.dom.style.top = box.y + "px";
44243             this.updateBody(box.width, box.height);
44244         }else{
44245             this.collapsedEl.dom.style.left = box.x + "px";
44246             this.collapsedEl.dom.style.top = box.y + "px";
44247             this.collapsedEl.setSize(box.width, box.height);
44248         }
44249         if(this.tabs){
44250             this.tabs.autoSizeTabs();
44251         }
44252     },
44253
44254     updateBody : function(w, h){
44255         if(w !== null){
44256             this.el.setWidth(w);
44257             w -= this.el.getBorderWidth("rl");
44258             if(this.config.adjustments){
44259                 w += this.config.adjustments[0];
44260             }
44261         }
44262         if(h !== null){
44263             this.el.setHeight(h);
44264             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44265             h -= this.el.getBorderWidth("tb");
44266             if(this.config.adjustments){
44267                 h += this.config.adjustments[1];
44268             }
44269             this.bodyEl.setHeight(h);
44270             if(this.tabs){
44271                 h = this.tabs.syncHeight(h);
44272             }
44273         }
44274         if(this.panelSize){
44275             w = w !== null ? w : this.panelSize.width;
44276             h = h !== null ? h : this.panelSize.height;
44277         }
44278         if(this.activePanel){
44279             var el = this.activePanel.getEl();
44280             w = w !== null ? w : el.getWidth();
44281             h = h !== null ? h : el.getHeight();
44282             this.panelSize = {width: w, height: h};
44283             this.activePanel.setSize(w, h);
44284         }
44285         if(Roo.isIE && this.tabs){
44286             this.tabs.el.repaint();
44287         }
44288     },
44289
44290     /**
44291      * Returns the container element for this region.
44292      * @return {Roo.Element}
44293      */
44294     getEl : function(){
44295         return this.el;
44296     },
44297
44298     /**
44299      * Hides this region.
44300      */
44301     hide : function(){
44302         if(!this.collapsed){
44303             this.el.dom.style.left = "-2000px";
44304             this.el.hide();
44305         }else{
44306             this.collapsedEl.dom.style.left = "-2000px";
44307             this.collapsedEl.hide();
44308         }
44309         this.visible = false;
44310         this.fireEvent("visibilitychange", this, false);
44311     },
44312
44313     /**
44314      * Shows this region if it was previously hidden.
44315      */
44316     show : function(){
44317         if(!this.collapsed){
44318             this.el.show();
44319         }else{
44320             this.collapsedEl.show();
44321         }
44322         this.visible = true;
44323         this.fireEvent("visibilitychange", this, true);
44324     },
44325
44326     closeClicked : function(){
44327         if(this.activePanel){
44328             this.remove(this.activePanel);
44329         }
44330     },
44331
44332     collapseClick : function(e){
44333         if(this.isSlid){
44334            e.stopPropagation();
44335            this.slideIn();
44336         }else{
44337            e.stopPropagation();
44338            this.slideOut();
44339         }
44340     },
44341
44342     /**
44343      * Collapses this region.
44344      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44345      */
44346     collapse : function(skipAnim){
44347         if(this.collapsed) return;
44348         this.collapsed = true;
44349         if(this.split){
44350             this.split.el.hide();
44351         }
44352         if(this.config.animate && skipAnim !== true){
44353             this.fireEvent("invalidated", this);
44354             this.animateCollapse();
44355         }else{
44356             this.el.setLocation(-20000,-20000);
44357             this.el.hide();
44358             this.collapsedEl.show();
44359             this.fireEvent("collapsed", this);
44360             this.fireEvent("invalidated", this);
44361         }
44362     },
44363
44364     animateCollapse : function(){
44365         // overridden
44366     },
44367
44368     /**
44369      * Expands this region if it was previously collapsed.
44370      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44371      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44372      */
44373     expand : function(e, skipAnim){
44374         if(e) e.stopPropagation();
44375         if(!this.collapsed || this.el.hasActiveFx()) return;
44376         if(this.isSlid){
44377             this.afterSlideIn();
44378             skipAnim = true;
44379         }
44380         this.collapsed = false;
44381         if(this.config.animate && skipAnim !== true){
44382             this.animateExpand();
44383         }else{
44384             this.el.show();
44385             if(this.split){
44386                 this.split.el.show();
44387             }
44388             this.collapsedEl.setLocation(-2000,-2000);
44389             this.collapsedEl.hide();
44390             this.fireEvent("invalidated", this);
44391             this.fireEvent("expanded", this);
44392         }
44393     },
44394
44395     animateExpand : function(){
44396         // overridden
44397     },
44398
44399     initTabs : function(){
44400         this.bodyEl.setStyle("overflow", "hidden");
44401         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44402             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44403             disableTooltips: this.config.disableTabTips
44404         });
44405         if(this.config.hideTabs){
44406             ts.stripWrap.setDisplayed(false);
44407         }
44408         this.tabs = ts;
44409         ts.resizeTabs = this.config.resizeTabs === true;
44410         ts.minTabWidth = this.config.minTabWidth || 40;
44411         ts.maxTabWidth = this.config.maxTabWidth || 250;
44412         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44413         ts.monitorResize = false;
44414         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44415         ts.bodyEl.addClass('x-layout-tabs-body');
44416         this.panels.each(this.initPanelAsTab, this);
44417     },
44418
44419     initPanelAsTab : function(panel){
44420         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44421                     this.config.closeOnTab && panel.isClosable());
44422         if(panel.tabTip !== undefined){
44423             ti.setTooltip(panel.tabTip);
44424         }
44425         ti.on("activate", function(){
44426               this.setActivePanel(panel);
44427         }, this);
44428         if(this.config.closeOnTab){
44429             ti.on("beforeclose", function(t, e){
44430                 e.cancel = true;
44431                 this.remove(panel);
44432             }, this);
44433         }
44434         return ti;
44435     },
44436
44437     updatePanelTitle : function(panel, title){
44438         if(this.activePanel == panel){
44439             this.updateTitle(title);
44440         }
44441         if(this.tabs){
44442             var ti = this.tabs.getTab(panel.getEl().id);
44443             ti.setText(title);
44444             if(panel.tabTip !== undefined){
44445                 ti.setTooltip(panel.tabTip);
44446             }
44447         }
44448     },
44449
44450     updateTitle : function(title){
44451         if(this.titleTextEl && !this.config.title){
44452             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44453         }
44454     },
44455
44456     setActivePanel : function(panel){
44457         panel = this.getPanel(panel);
44458         if(this.activePanel && this.activePanel != panel){
44459             this.activePanel.setActiveState(false);
44460         }
44461         this.activePanel = panel;
44462         panel.setActiveState(true);
44463         if(this.panelSize){
44464             panel.setSize(this.panelSize.width, this.panelSize.height);
44465         }
44466         if(this.closeBtn){
44467             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44468         }
44469         this.updateTitle(panel.getTitle());
44470         if(this.tabs){
44471             this.fireEvent("invalidated", this);
44472         }
44473         this.fireEvent("panelactivated", this, panel);
44474     },
44475
44476     /**
44477      * Shows the specified panel.
44478      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44479      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44480      */
44481     showPanel : function(panel){
44482         if(panel = this.getPanel(panel)){
44483             if(this.tabs){
44484                 var tab = this.tabs.getTab(panel.getEl().id);
44485                 if(tab.isHidden()){
44486                     this.tabs.unhideTab(tab.id);
44487                 }
44488                 tab.activate();
44489             }else{
44490                 this.setActivePanel(panel);
44491             }
44492         }
44493         return panel;
44494     },
44495
44496     /**
44497      * Get the active panel for this region.
44498      * @return {Roo.ContentPanel} The active panel or null
44499      */
44500     getActivePanel : function(){
44501         return this.activePanel;
44502     },
44503
44504     validateVisibility : function(){
44505         if(this.panels.getCount() < 1){
44506             this.updateTitle("&#160;");
44507             this.closeBtn.hide();
44508             this.hide();
44509         }else{
44510             if(!this.isVisible()){
44511                 this.show();
44512             }
44513         }
44514     },
44515
44516     /**
44517      * Adds the passed ContentPanel(s) to this region.
44518      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44519      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44520      */
44521     add : function(panel){
44522         if(arguments.length > 1){
44523             for(var i = 0, len = arguments.length; i < len; i++) {
44524                 this.add(arguments[i]);
44525             }
44526             return null;
44527         }
44528         if(this.hasPanel(panel)){
44529             this.showPanel(panel);
44530             return panel;
44531         }
44532         panel.setRegion(this);
44533         this.panels.add(panel);
44534         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44535             this.bodyEl.dom.appendChild(panel.getEl().dom);
44536             if(panel.background !== true){
44537                 this.setActivePanel(panel);
44538             }
44539             this.fireEvent("paneladded", this, panel);
44540             return panel;
44541         }
44542         if(!this.tabs){
44543             this.initTabs();
44544         }else{
44545             this.initPanelAsTab(panel);
44546         }
44547         if(panel.background !== true){
44548             this.tabs.activate(panel.getEl().id);
44549         }
44550         this.fireEvent("paneladded", this, panel);
44551         return panel;
44552     },
44553
44554     /**
44555      * Hides the tab for the specified panel.
44556      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44557      */
44558     hidePanel : function(panel){
44559         if(this.tabs && (panel = this.getPanel(panel))){
44560             this.tabs.hideTab(panel.getEl().id);
44561         }
44562     },
44563
44564     /**
44565      * Unhides the tab for a previously hidden panel.
44566      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44567      */
44568     unhidePanel : function(panel){
44569         if(this.tabs && (panel = this.getPanel(panel))){
44570             this.tabs.unhideTab(panel.getEl().id);
44571         }
44572     },
44573
44574     clearPanels : function(){
44575         while(this.panels.getCount() > 0){
44576              this.remove(this.panels.first());
44577         }
44578     },
44579
44580     /**
44581      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44582      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44583      * @param {Boolean} preservePanel Overrides the config preservePanel option
44584      * @return {Roo.ContentPanel} The panel that was removed
44585      */
44586     remove : function(panel, preservePanel){
44587         panel = this.getPanel(panel);
44588         if(!panel){
44589             return null;
44590         }
44591         var e = {};
44592         this.fireEvent("beforeremove", this, panel, e);
44593         if(e.cancel === true){
44594             return null;
44595         }
44596         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44597         var panelId = panel.getId();
44598         this.panels.removeKey(panelId);
44599         if(preservePanel){
44600             document.body.appendChild(panel.getEl().dom);
44601         }
44602         if(this.tabs){
44603             this.tabs.removeTab(panel.getEl().id);
44604         }else if (!preservePanel){
44605             this.bodyEl.dom.removeChild(panel.getEl().dom);
44606         }
44607         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44608             var p = this.panels.first();
44609             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44610             tempEl.appendChild(p.getEl().dom);
44611             this.bodyEl.update("");
44612             this.bodyEl.dom.appendChild(p.getEl().dom);
44613             tempEl = null;
44614             this.updateTitle(p.getTitle());
44615             this.tabs = null;
44616             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44617             this.setActivePanel(p);
44618         }
44619         panel.setRegion(null);
44620         if(this.activePanel == panel){
44621             this.activePanel = null;
44622         }
44623         if(this.config.autoDestroy !== false && preservePanel !== true){
44624             try{panel.destroy();}catch(e){}
44625         }
44626         this.fireEvent("panelremoved", this, panel);
44627         return panel;
44628     },
44629
44630     /**
44631      * Returns the TabPanel component used by this region
44632      * @return {Roo.TabPanel}
44633      */
44634     getTabs : function(){
44635         return this.tabs;
44636     },
44637
44638     createTool : function(parentEl, className){
44639         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44640             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44641         btn.addClassOnOver("x-layout-tools-button-over");
44642         return btn;
44643     }
44644 });/*
44645  * Based on:
44646  * Ext JS Library 1.1.1
44647  * Copyright(c) 2006-2007, Ext JS, LLC.
44648  *
44649  * Originally Released Under LGPL - original licence link has changed is not relivant.
44650  *
44651  * Fork - LGPL
44652  * <script type="text/javascript">
44653  */
44654  
44655
44656
44657 /**
44658  * @class Roo.SplitLayoutRegion
44659  * @extends Roo.LayoutRegion
44660  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44661  */
44662 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44663     this.cursor = cursor;
44664     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44665 };
44666
44667 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44668     splitTip : "Drag to resize.",
44669     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44670     useSplitTips : false,
44671
44672     applyConfig : function(config){
44673         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44674         if(config.split){
44675             if(!this.split){
44676                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44677                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44678                 /** The SplitBar for this region 
44679                 * @type Roo.SplitBar */
44680                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44681                 this.split.on("moved", this.onSplitMove, this);
44682                 this.split.useShim = config.useShim === true;
44683                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44684                 if(this.useSplitTips){
44685                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44686                 }
44687                 if(config.collapsible){
44688                     this.split.el.on("dblclick", this.collapse,  this);
44689                 }
44690             }
44691             if(typeof config.minSize != "undefined"){
44692                 this.split.minSize = config.minSize;
44693             }
44694             if(typeof config.maxSize != "undefined"){
44695                 this.split.maxSize = config.maxSize;
44696             }
44697             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44698                 this.hideSplitter();
44699             }
44700         }
44701     },
44702
44703     getHMaxSize : function(){
44704          var cmax = this.config.maxSize || 10000;
44705          var center = this.mgr.getRegion("center");
44706          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44707     },
44708
44709     getVMaxSize : function(){
44710          var cmax = this.config.maxSize || 10000;
44711          var center = this.mgr.getRegion("center");
44712          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44713     },
44714
44715     onSplitMove : function(split, newSize){
44716         this.fireEvent("resized", this, newSize);
44717     },
44718     
44719     /** 
44720      * Returns the {@link Roo.SplitBar} for this region.
44721      * @return {Roo.SplitBar}
44722      */
44723     getSplitBar : function(){
44724         return this.split;
44725     },
44726     
44727     hide : function(){
44728         this.hideSplitter();
44729         Roo.SplitLayoutRegion.superclass.hide.call(this);
44730     },
44731
44732     hideSplitter : function(){
44733         if(this.split){
44734             this.split.el.setLocation(-2000,-2000);
44735             this.split.el.hide();
44736         }
44737     },
44738
44739     show : function(){
44740         if(this.split){
44741             this.split.el.show();
44742         }
44743         Roo.SplitLayoutRegion.superclass.show.call(this);
44744     },
44745     
44746     beforeSlide: function(){
44747         if(Roo.isGecko){// firefox overflow auto bug workaround
44748             this.bodyEl.clip();
44749             if(this.tabs) this.tabs.bodyEl.clip();
44750             if(this.activePanel){
44751                 this.activePanel.getEl().clip();
44752                 
44753                 if(this.activePanel.beforeSlide){
44754                     this.activePanel.beforeSlide();
44755                 }
44756             }
44757         }
44758     },
44759     
44760     afterSlide : function(){
44761         if(Roo.isGecko){// firefox overflow auto bug workaround
44762             this.bodyEl.unclip();
44763             if(this.tabs) this.tabs.bodyEl.unclip();
44764             if(this.activePanel){
44765                 this.activePanel.getEl().unclip();
44766                 if(this.activePanel.afterSlide){
44767                     this.activePanel.afterSlide();
44768                 }
44769             }
44770         }
44771     },
44772
44773     initAutoHide : function(){
44774         if(this.autoHide !== false){
44775             if(!this.autoHideHd){
44776                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44777                 this.autoHideHd = {
44778                     "mouseout": function(e){
44779                         if(!e.within(this.el, true)){
44780                             st.delay(500);
44781                         }
44782                     },
44783                     "mouseover" : function(e){
44784                         st.cancel();
44785                     },
44786                     scope : this
44787                 };
44788             }
44789             this.el.on(this.autoHideHd);
44790         }
44791     },
44792
44793     clearAutoHide : function(){
44794         if(this.autoHide !== false){
44795             this.el.un("mouseout", this.autoHideHd.mouseout);
44796             this.el.un("mouseover", this.autoHideHd.mouseover);
44797         }
44798     },
44799
44800     clearMonitor : function(){
44801         Roo.get(document).un("click", this.slideInIf, this);
44802     },
44803
44804     // these names are backwards but not changed for compat
44805     slideOut : function(){
44806         if(this.isSlid || this.el.hasActiveFx()){
44807             return;
44808         }
44809         this.isSlid = true;
44810         if(this.collapseBtn){
44811             this.collapseBtn.hide();
44812         }
44813         this.closeBtnState = this.closeBtn.getStyle('display');
44814         this.closeBtn.hide();
44815         if(this.stickBtn){
44816             this.stickBtn.show();
44817         }
44818         this.el.show();
44819         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44820         this.beforeSlide();
44821         this.el.setStyle("z-index", 10001);
44822         this.el.slideIn(this.getSlideAnchor(), {
44823             callback: function(){
44824                 this.afterSlide();
44825                 this.initAutoHide();
44826                 Roo.get(document).on("click", this.slideInIf, this);
44827                 this.fireEvent("slideshow", this);
44828             },
44829             scope: this,
44830             block: true
44831         });
44832     },
44833
44834     afterSlideIn : function(){
44835         this.clearAutoHide();
44836         this.isSlid = false;
44837         this.clearMonitor();
44838         this.el.setStyle("z-index", "");
44839         if(this.collapseBtn){
44840             this.collapseBtn.show();
44841         }
44842         this.closeBtn.setStyle('display', this.closeBtnState);
44843         if(this.stickBtn){
44844             this.stickBtn.hide();
44845         }
44846         this.fireEvent("slidehide", this);
44847     },
44848
44849     slideIn : function(cb){
44850         if(!this.isSlid || this.el.hasActiveFx()){
44851             Roo.callback(cb);
44852             return;
44853         }
44854         this.isSlid = false;
44855         this.beforeSlide();
44856         this.el.slideOut(this.getSlideAnchor(), {
44857             callback: function(){
44858                 this.el.setLeftTop(-10000, -10000);
44859                 this.afterSlide();
44860                 this.afterSlideIn();
44861                 Roo.callback(cb);
44862             },
44863             scope: this,
44864             block: true
44865         });
44866     },
44867     
44868     slideInIf : function(e){
44869         if(!e.within(this.el)){
44870             this.slideIn();
44871         }
44872     },
44873
44874     animateCollapse : function(){
44875         this.beforeSlide();
44876         this.el.setStyle("z-index", 20000);
44877         var anchor = this.getSlideAnchor();
44878         this.el.slideOut(anchor, {
44879             callback : function(){
44880                 this.el.setStyle("z-index", "");
44881                 this.collapsedEl.slideIn(anchor, {duration:.3});
44882                 this.afterSlide();
44883                 this.el.setLocation(-10000,-10000);
44884                 this.el.hide();
44885                 this.fireEvent("collapsed", this);
44886             },
44887             scope: this,
44888             block: true
44889         });
44890     },
44891
44892     animateExpand : function(){
44893         this.beforeSlide();
44894         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44895         this.el.setStyle("z-index", 20000);
44896         this.collapsedEl.hide({
44897             duration:.1
44898         });
44899         this.el.slideIn(this.getSlideAnchor(), {
44900             callback : function(){
44901                 this.el.setStyle("z-index", "");
44902                 this.afterSlide();
44903                 if(this.split){
44904                     this.split.el.show();
44905                 }
44906                 this.fireEvent("invalidated", this);
44907                 this.fireEvent("expanded", this);
44908             },
44909             scope: this,
44910             block: true
44911         });
44912     },
44913
44914     anchors : {
44915         "west" : "left",
44916         "east" : "right",
44917         "north" : "top",
44918         "south" : "bottom"
44919     },
44920
44921     sanchors : {
44922         "west" : "l",
44923         "east" : "r",
44924         "north" : "t",
44925         "south" : "b"
44926     },
44927
44928     canchors : {
44929         "west" : "tl-tr",
44930         "east" : "tr-tl",
44931         "north" : "tl-bl",
44932         "south" : "bl-tl"
44933     },
44934
44935     getAnchor : function(){
44936         return this.anchors[this.position];
44937     },
44938
44939     getCollapseAnchor : function(){
44940         return this.canchors[this.position];
44941     },
44942
44943     getSlideAnchor : function(){
44944         return this.sanchors[this.position];
44945     },
44946
44947     getAlignAdj : function(){
44948         var cm = this.cmargins;
44949         switch(this.position){
44950             case "west":
44951                 return [0, 0];
44952             break;
44953             case "east":
44954                 return [0, 0];
44955             break;
44956             case "north":
44957                 return [0, 0];
44958             break;
44959             case "south":
44960                 return [0, 0];
44961             break;
44962         }
44963     },
44964
44965     getExpandAdj : function(){
44966         var c = this.collapsedEl, cm = this.cmargins;
44967         switch(this.position){
44968             case "west":
44969                 return [-(cm.right+c.getWidth()+cm.left), 0];
44970             break;
44971             case "east":
44972                 return [cm.right+c.getWidth()+cm.left, 0];
44973             break;
44974             case "north":
44975                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44976             break;
44977             case "south":
44978                 return [0, cm.top+cm.bottom+c.getHeight()];
44979             break;
44980         }
44981     }
44982 });/*
44983  * Based on:
44984  * Ext JS Library 1.1.1
44985  * Copyright(c) 2006-2007, Ext JS, LLC.
44986  *
44987  * Originally Released Under LGPL - original licence link has changed is not relivant.
44988  *
44989  * Fork - LGPL
44990  * <script type="text/javascript">
44991  */
44992 /*
44993  * These classes are private internal classes
44994  */
44995 Roo.CenterLayoutRegion = function(mgr, config){
44996     Roo.LayoutRegion.call(this, mgr, config, "center");
44997     this.visible = true;
44998     this.minWidth = config.minWidth || 20;
44999     this.minHeight = config.minHeight || 20;
45000 };
45001
45002 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45003     hide : function(){
45004         // center panel can't be hidden
45005     },
45006     
45007     show : function(){
45008         // center panel can't be hidden
45009     },
45010     
45011     getMinWidth: function(){
45012         return this.minWidth;
45013     },
45014     
45015     getMinHeight: function(){
45016         return this.minHeight;
45017     }
45018 });
45019
45020
45021 Roo.NorthLayoutRegion = function(mgr, config){
45022     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45023     if(this.split){
45024         this.split.placement = Roo.SplitBar.TOP;
45025         this.split.orientation = Roo.SplitBar.VERTICAL;
45026         this.split.el.addClass("x-layout-split-v");
45027     }
45028     var size = config.initialSize || config.height;
45029     if(typeof size != "undefined"){
45030         this.el.setHeight(size);
45031     }
45032 };
45033 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45034     orientation: Roo.SplitBar.VERTICAL,
45035     getBox : function(){
45036         if(this.collapsed){
45037             return this.collapsedEl.getBox();
45038         }
45039         var box = this.el.getBox();
45040         if(this.split){
45041             box.height += this.split.el.getHeight();
45042         }
45043         return box;
45044     },
45045     
45046     updateBox : function(box){
45047         if(this.split && !this.collapsed){
45048             box.height -= this.split.el.getHeight();
45049             this.split.el.setLeft(box.x);
45050             this.split.el.setTop(box.y+box.height);
45051             this.split.el.setWidth(box.width);
45052         }
45053         if(this.collapsed){
45054             this.updateBody(box.width, null);
45055         }
45056         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45057     }
45058 });
45059
45060 Roo.SouthLayoutRegion = function(mgr, config){
45061     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45062     if(this.split){
45063         this.split.placement = Roo.SplitBar.BOTTOM;
45064         this.split.orientation = Roo.SplitBar.VERTICAL;
45065         this.split.el.addClass("x-layout-split-v");
45066     }
45067     var size = config.initialSize || config.height;
45068     if(typeof size != "undefined"){
45069         this.el.setHeight(size);
45070     }
45071 };
45072 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45073     orientation: Roo.SplitBar.VERTICAL,
45074     getBox : function(){
45075         if(this.collapsed){
45076             return this.collapsedEl.getBox();
45077         }
45078         var box = this.el.getBox();
45079         if(this.split){
45080             var sh = this.split.el.getHeight();
45081             box.height += sh;
45082             box.y -= sh;
45083         }
45084         return box;
45085     },
45086     
45087     updateBox : function(box){
45088         if(this.split && !this.collapsed){
45089             var sh = this.split.el.getHeight();
45090             box.height -= sh;
45091             box.y += sh;
45092             this.split.el.setLeft(box.x);
45093             this.split.el.setTop(box.y-sh);
45094             this.split.el.setWidth(box.width);
45095         }
45096         if(this.collapsed){
45097             this.updateBody(box.width, null);
45098         }
45099         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45100     }
45101 });
45102
45103 Roo.EastLayoutRegion = function(mgr, config){
45104     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45105     if(this.split){
45106         this.split.placement = Roo.SplitBar.RIGHT;
45107         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45108         this.split.el.addClass("x-layout-split-h");
45109     }
45110     var size = config.initialSize || config.width;
45111     if(typeof size != "undefined"){
45112         this.el.setWidth(size);
45113     }
45114 };
45115 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45116     orientation: Roo.SplitBar.HORIZONTAL,
45117     getBox : function(){
45118         if(this.collapsed){
45119             return this.collapsedEl.getBox();
45120         }
45121         var box = this.el.getBox();
45122         if(this.split){
45123             var sw = this.split.el.getWidth();
45124             box.width += sw;
45125             box.x -= sw;
45126         }
45127         return box;
45128     },
45129
45130     updateBox : function(box){
45131         if(this.split && !this.collapsed){
45132             var sw = this.split.el.getWidth();
45133             box.width -= sw;
45134             this.split.el.setLeft(box.x);
45135             this.split.el.setTop(box.y);
45136             this.split.el.setHeight(box.height);
45137             box.x += sw;
45138         }
45139         if(this.collapsed){
45140             this.updateBody(null, box.height);
45141         }
45142         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45143     }
45144 });
45145
45146 Roo.WestLayoutRegion = function(mgr, config){
45147     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45148     if(this.split){
45149         this.split.placement = Roo.SplitBar.LEFT;
45150         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45151         this.split.el.addClass("x-layout-split-h");
45152     }
45153     var size = config.initialSize || config.width;
45154     if(typeof size != "undefined"){
45155         this.el.setWidth(size);
45156     }
45157 };
45158 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45159     orientation: Roo.SplitBar.HORIZONTAL,
45160     getBox : function(){
45161         if(this.collapsed){
45162             return this.collapsedEl.getBox();
45163         }
45164         var box = this.el.getBox();
45165         if(this.split){
45166             box.width += this.split.el.getWidth();
45167         }
45168         return box;
45169     },
45170     
45171     updateBox : function(box){
45172         if(this.split && !this.collapsed){
45173             var sw = this.split.el.getWidth();
45174             box.width -= sw;
45175             this.split.el.setLeft(box.x+box.width);
45176             this.split.el.setTop(box.y);
45177             this.split.el.setHeight(box.height);
45178         }
45179         if(this.collapsed){
45180             this.updateBody(null, box.height);
45181         }
45182         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45183     }
45184 });
45185 /*
45186  * Based on:
45187  * Ext JS Library 1.1.1
45188  * Copyright(c) 2006-2007, Ext JS, LLC.
45189  *
45190  * Originally Released Under LGPL - original licence link has changed is not relivant.
45191  *
45192  * Fork - LGPL
45193  * <script type="text/javascript">
45194  */
45195  
45196  
45197 /*
45198  * Private internal class for reading and applying state
45199  */
45200 Roo.LayoutStateManager = function(layout){
45201      // default empty state
45202      this.state = {
45203         north: {},
45204         south: {},
45205         east: {},
45206         west: {}       
45207     };
45208 };
45209
45210 Roo.LayoutStateManager.prototype = {
45211     init : function(layout, provider){
45212         this.provider = provider;
45213         var state = provider.get(layout.id+"-layout-state");
45214         if(state){
45215             var wasUpdating = layout.isUpdating();
45216             if(!wasUpdating){
45217                 layout.beginUpdate();
45218             }
45219             for(var key in state){
45220                 if(typeof state[key] != "function"){
45221                     var rstate = state[key];
45222                     var r = layout.getRegion(key);
45223                     if(r && rstate){
45224                         if(rstate.size){
45225                             r.resizeTo(rstate.size);
45226                         }
45227                         if(rstate.collapsed == true){
45228                             r.collapse(true);
45229                         }else{
45230                             r.expand(null, true);
45231                         }
45232                     }
45233                 }
45234             }
45235             if(!wasUpdating){
45236                 layout.endUpdate();
45237             }
45238             this.state = state; 
45239         }
45240         this.layout = layout;
45241         layout.on("regionresized", this.onRegionResized, this);
45242         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45243         layout.on("regionexpanded", this.onRegionExpanded, this);
45244     },
45245     
45246     storeState : function(){
45247         this.provider.set(this.layout.id+"-layout-state", this.state);
45248     },
45249     
45250     onRegionResized : function(region, newSize){
45251         this.state[region.getPosition()].size = newSize;
45252         this.storeState();
45253     },
45254     
45255     onRegionCollapsed : function(region){
45256         this.state[region.getPosition()].collapsed = true;
45257         this.storeState();
45258     },
45259     
45260     onRegionExpanded : function(region){
45261         this.state[region.getPosition()].collapsed = false;
45262         this.storeState();
45263     }
45264 };/*
45265  * Based on:
45266  * Ext JS Library 1.1.1
45267  * Copyright(c) 2006-2007, Ext JS, LLC.
45268  *
45269  * Originally Released Under LGPL - original licence link has changed is not relivant.
45270  *
45271  * Fork - LGPL
45272  * <script type="text/javascript">
45273  */
45274 /**
45275  * @class Roo.ContentPanel
45276  * @extends Roo.util.Observable
45277  * A basic ContentPanel element.
45278  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45279  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45280  * @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
45281  * @cfg {Boolean} closable True if the panel can be closed/removed
45282  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45283  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45284  * @cfg {Toolbar} toolbar A toolbar for this panel
45285  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45286  * @cfg {String} title The title for this panel
45287  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45288  * @cfg {String} url Calls {@link #setUrl} with this value
45289  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45290  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45291  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45292  * @constructor
45293  * Create a new ContentPanel.
45294  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45295  * @param {String/Object} config A string to set only the title or a config object
45296  * @param {String} content (optional) Set the HTML content for this panel
45297  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45298  */
45299 Roo.ContentPanel = function(el, config, content){
45300     
45301      
45302     /*
45303     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45304         config = el;
45305         el = Roo.id();
45306     }
45307     if (config && config.parentLayout) { 
45308         el = config.parentLayout.el.createChild(); 
45309     }
45310     */
45311     if(el.autoCreate){ // xtype is available if this is called from factory
45312         config = el;
45313         el = Roo.id();
45314     }
45315     this.el = Roo.get(el);
45316     if(!this.el && config && config.autoCreate){
45317         if(typeof config.autoCreate == "object"){
45318             if(!config.autoCreate.id){
45319                 config.autoCreate.id = config.id||el;
45320             }
45321             this.el = Roo.DomHelper.append(document.body,
45322                         config.autoCreate, true);
45323         }else{
45324             this.el = Roo.DomHelper.append(document.body,
45325                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45326         }
45327     }
45328     this.closable = false;
45329     this.loaded = false;
45330     this.active = false;
45331     if(typeof config == "string"){
45332         this.title = config;
45333     }else{
45334         Roo.apply(this, config);
45335     }
45336     
45337     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45338         this.wrapEl = this.el.wrap();    
45339         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45340         
45341     }
45342     
45343     
45344     
45345     if(this.resizeEl){
45346         this.resizeEl = Roo.get(this.resizeEl, true);
45347     }else{
45348         this.resizeEl = this.el;
45349     }
45350     this.addEvents({
45351         /**
45352          * @event activate
45353          * Fires when this panel is activated. 
45354          * @param {Roo.ContentPanel} this
45355          */
45356         "activate" : true,
45357         /**
45358          * @event deactivate
45359          * Fires when this panel is activated. 
45360          * @param {Roo.ContentPanel} this
45361          */
45362         "deactivate" : true,
45363
45364         /**
45365          * @event resize
45366          * Fires when this panel is resized if fitToFrame is true.
45367          * @param {Roo.ContentPanel} this
45368          * @param {Number} width The width after any component adjustments
45369          * @param {Number} height The height after any component adjustments
45370          */
45371         "resize" : true
45372     });
45373     if(this.autoScroll){
45374         this.resizeEl.setStyle("overflow", "auto");
45375     } else {
45376         // fix randome scrolling
45377         this.el.on('scroll', function() {
45378             this.scrollTo('top',0); 
45379         });
45380     }
45381     content = content || this.content;
45382     if(content){
45383         this.setContent(content);
45384     }
45385     if(config && config.url){
45386         this.setUrl(this.url, this.params, this.loadOnce);
45387     }
45388     
45389     
45390     
45391     Roo.ContentPanel.superclass.constructor.call(this);
45392 };
45393
45394 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45395     tabTip:'',
45396     setRegion : function(region){
45397         this.region = region;
45398         if(region){
45399            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45400         }else{
45401            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45402         } 
45403     },
45404     
45405     /**
45406      * Returns the toolbar for this Panel if one was configured. 
45407      * @return {Roo.Toolbar} 
45408      */
45409     getToolbar : function(){
45410         return this.toolbar;
45411     },
45412     
45413     setActiveState : function(active){
45414         this.active = active;
45415         if(!active){
45416             this.fireEvent("deactivate", this);
45417         }else{
45418             this.fireEvent("activate", this);
45419         }
45420     },
45421     /**
45422      * Updates this panel's element
45423      * @param {String} content The new content
45424      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45425     */
45426     setContent : function(content, loadScripts){
45427         this.el.update(content, loadScripts);
45428     },
45429
45430     ignoreResize : function(w, h){
45431         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45432             return true;
45433         }else{
45434             this.lastSize = {width: w, height: h};
45435             return false;
45436         }
45437     },
45438     /**
45439      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45440      * @return {Roo.UpdateManager} The UpdateManager
45441      */
45442     getUpdateManager : function(){
45443         return this.el.getUpdateManager();
45444     },
45445      /**
45446      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45447      * @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:
45448 <pre><code>
45449 panel.load({
45450     url: "your-url.php",
45451     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45452     callback: yourFunction,
45453     scope: yourObject, //(optional scope)
45454     discardUrl: false,
45455     nocache: false,
45456     text: "Loading...",
45457     timeout: 30,
45458     scripts: false
45459 });
45460 </code></pre>
45461      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45462      * 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.
45463      * @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}
45464      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45465      * @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.
45466      * @return {Roo.ContentPanel} this
45467      */
45468     load : function(){
45469         var um = this.el.getUpdateManager();
45470         um.update.apply(um, arguments);
45471         return this;
45472     },
45473
45474
45475     /**
45476      * 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.
45477      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45478      * @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)
45479      * @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)
45480      * @return {Roo.UpdateManager} The UpdateManager
45481      */
45482     setUrl : function(url, params, loadOnce){
45483         if(this.refreshDelegate){
45484             this.removeListener("activate", this.refreshDelegate);
45485         }
45486         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45487         this.on("activate", this.refreshDelegate);
45488         return this.el.getUpdateManager();
45489     },
45490     
45491     _handleRefresh : function(url, params, loadOnce){
45492         if(!loadOnce || !this.loaded){
45493             var updater = this.el.getUpdateManager();
45494             updater.update(url, params, this._setLoaded.createDelegate(this));
45495         }
45496     },
45497     
45498     _setLoaded : function(){
45499         this.loaded = true;
45500     }, 
45501     
45502     /**
45503      * Returns this panel's id
45504      * @return {String} 
45505      */
45506     getId : function(){
45507         return this.el.id;
45508     },
45509     
45510     /** 
45511      * Returns this panel's element - used by regiosn to add.
45512      * @return {Roo.Element} 
45513      */
45514     getEl : function(){
45515         return this.wrapEl || this.el;
45516     },
45517     
45518     adjustForComponents : function(width, height){
45519         if(this.resizeEl != this.el){
45520             width -= this.el.getFrameWidth('lr');
45521             height -= this.el.getFrameWidth('tb');
45522         }
45523         if(this.toolbar){
45524             var te = this.toolbar.getEl();
45525             height -= te.getHeight();
45526             te.setWidth(width);
45527         }
45528         if(this.adjustments){
45529             width += this.adjustments[0];
45530             height += this.adjustments[1];
45531         }
45532         return {"width": width, "height": height};
45533     },
45534     
45535     setSize : function(width, height){
45536         if(this.fitToFrame && !this.ignoreResize(width, height)){
45537             if(this.fitContainer && this.resizeEl != this.el){
45538                 this.el.setSize(width, height);
45539             }
45540             var size = this.adjustForComponents(width, height);
45541             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45542             this.fireEvent('resize', this, size.width, size.height);
45543         }
45544     },
45545     
45546     /**
45547      * Returns this panel's title
45548      * @return {String} 
45549      */
45550     getTitle : function(){
45551         return this.title;
45552     },
45553     
45554     /**
45555      * Set this panel's title
45556      * @param {String} title
45557      */
45558     setTitle : function(title){
45559         this.title = title;
45560         if(this.region){
45561             this.region.updatePanelTitle(this, title);
45562         }
45563     },
45564     
45565     /**
45566      * Returns true is this panel was configured to be closable
45567      * @return {Boolean} 
45568      */
45569     isClosable : function(){
45570         return this.closable;
45571     },
45572     
45573     beforeSlide : function(){
45574         this.el.clip();
45575         this.resizeEl.clip();
45576     },
45577     
45578     afterSlide : function(){
45579         this.el.unclip();
45580         this.resizeEl.unclip();
45581     },
45582     
45583     /**
45584      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45585      *   Will fail silently if the {@link #setUrl} method has not been called.
45586      *   This does not activate the panel, just updates its content.
45587      */
45588     refresh : function(){
45589         if(this.refreshDelegate){
45590            this.loaded = false;
45591            this.refreshDelegate();
45592         }
45593     },
45594     
45595     /**
45596      * Destroys this panel
45597      */
45598     destroy : function(){
45599         this.el.removeAllListeners();
45600         var tempEl = document.createElement("span");
45601         tempEl.appendChild(this.el.dom);
45602         tempEl.innerHTML = "";
45603         this.el.remove();
45604         this.el = null;
45605     },
45606     
45607       /**
45608      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45609      * <pre><code>
45610
45611 layout.addxtype({
45612        xtype : 'Form',
45613        items: [ .... ]
45614    }
45615 );
45616
45617 </code></pre>
45618      * @param {Object} cfg Xtype definition of item to add.
45619      */
45620     
45621     addxtype : function(cfg) {
45622         // add form..
45623         if (cfg.xtype.match(/^Form$/)) {
45624             var el = this.el.createChild();
45625
45626             this.form = new  Roo.form.Form(cfg);
45627             
45628             
45629             if ( this.form.allItems.length) this.form.render(el.dom);
45630             return this.form;
45631         }
45632         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45633             // views..
45634             cfg.el = this.el.appendChild(document.createElement("div"));
45635             // factory?
45636             var ret = new Roo[cfg.xtype](cfg);
45637             ret.render(false, ''); // render blank..
45638             return ret;
45639             
45640         }
45641         return false;
45642         
45643     }
45644 });
45645
45646 /**
45647  * @class Roo.GridPanel
45648  * @extends Roo.ContentPanel
45649  * @constructor
45650  * Create a new GridPanel.
45651  * @param {Roo.grid.Grid} grid The grid for this panel
45652  * @param {String/Object} config A string to set only the panel's title, or a config object
45653  */
45654 Roo.GridPanel = function(grid, config){
45655     
45656   
45657     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45658         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45659         
45660     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45661     
45662     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45663     
45664     if(this.toolbar){
45665         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45666     }
45667     // xtype created footer. - not sure if will work as we normally have to render first..
45668     if (this.footer && !this.footer.el && this.footer.xtype) {
45669         
45670         this.footer.container = this.grid.getView().getFooterPanel(true);
45671         this.footer.dataSource = this.grid.dataSource;
45672         this.footer = Roo.factory(this.footer, Roo);
45673         
45674     }
45675     
45676     grid.monitorWindowResize = false; // turn off autosizing
45677     grid.autoHeight = false;
45678     grid.autoWidth = false;
45679     this.grid = grid;
45680     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45681 };
45682
45683 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45684     getId : function(){
45685         return this.grid.id;
45686     },
45687     
45688     /**
45689      * Returns the grid for this panel
45690      * @return {Roo.grid.Grid} 
45691      */
45692     getGrid : function(){
45693         return this.grid;    
45694     },
45695     
45696     setSize : function(width, height){
45697         if(!this.ignoreResize(width, height)){
45698             var grid = this.grid;
45699             var size = this.adjustForComponents(width, height);
45700             grid.getGridEl().setSize(size.width, size.height);
45701             grid.autoSize();
45702         }
45703     },
45704     
45705     beforeSlide : function(){
45706         this.grid.getView().scroller.clip();
45707     },
45708     
45709     afterSlide : function(){
45710         this.grid.getView().scroller.unclip();
45711     },
45712     
45713     destroy : function(){
45714         this.grid.destroy();
45715         delete this.grid;
45716         Roo.GridPanel.superclass.destroy.call(this); 
45717     }
45718 });
45719
45720
45721 /**
45722  * @class Roo.NestedLayoutPanel
45723  * @extends Roo.ContentPanel
45724  * @constructor
45725  * Create a new NestedLayoutPanel.
45726  * 
45727  * 
45728  * @param {Roo.BorderLayout} layout The layout for this panel
45729  * @param {String/Object} config A string to set only the title or a config object
45730  */
45731 Roo.NestedLayoutPanel = function(layout, config)
45732 {
45733     // construct with only one argument..
45734     /* FIXME - implement nicer consturctors
45735     if (layout.layout) {
45736         config = layout;
45737         layout = config.layout;
45738         delete config.layout;
45739     }
45740     if (layout.xtype && !layout.getEl) {
45741         // then layout needs constructing..
45742         layout = Roo.factory(layout, Roo);
45743     }
45744     */
45745     
45746     
45747     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45748     
45749     layout.monitorWindowResize = false; // turn off autosizing
45750     this.layout = layout;
45751     this.layout.getEl().addClass("x-layout-nested-layout");
45752     
45753     
45754     
45755     
45756 };
45757
45758 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45759
45760     setSize : function(width, height){
45761         if(!this.ignoreResize(width, height)){
45762             var size = this.adjustForComponents(width, height);
45763             var el = this.layout.getEl();
45764             el.setSize(size.width, size.height);
45765             var touch = el.dom.offsetWidth;
45766             this.layout.layout();
45767             // ie requires a double layout on the first pass
45768             if(Roo.isIE && !this.initialized){
45769                 this.initialized = true;
45770                 this.layout.layout();
45771             }
45772         }
45773     },
45774     
45775     // activate all subpanels if not currently active..
45776     
45777     setActiveState : function(active){
45778         this.active = active;
45779         if(!active){
45780             this.fireEvent("deactivate", this);
45781             return;
45782         }
45783         
45784         this.fireEvent("activate", this);
45785         // not sure if this should happen before or after..
45786         if (!this.layout) {
45787             return; // should not happen..
45788         }
45789         var reg = false;
45790         for (var r in this.layout.regions) {
45791             reg = this.layout.getRegion(r);
45792             if (reg.getActivePanel()) {
45793                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45794                 reg.setActivePanel(reg.getActivePanel());
45795                 continue;
45796             }
45797             if (!reg.panels.length) {
45798                 continue;
45799             }
45800             reg.showPanel(reg.getPanel(0));
45801         }
45802         
45803         
45804         
45805         
45806     },
45807     
45808     /**
45809      * Returns the nested BorderLayout for this panel
45810      * @return {Roo.BorderLayout} 
45811      */
45812     getLayout : function(){
45813         return this.layout;
45814     },
45815     
45816      /**
45817      * Adds a xtype elements to the layout of the nested panel
45818      * <pre><code>
45819
45820 panel.addxtype({
45821        xtype : 'ContentPanel',
45822        region: 'west',
45823        items: [ .... ]
45824    }
45825 );
45826
45827 panel.addxtype({
45828         xtype : 'NestedLayoutPanel',
45829         region: 'west',
45830         layout: {
45831            center: { },
45832            west: { }   
45833         },
45834         items : [ ... list of content panels or nested layout panels.. ]
45835    }
45836 );
45837 </code></pre>
45838      * @param {Object} cfg Xtype definition of item to add.
45839      */
45840     addxtype : function(cfg) {
45841         return this.layout.addxtype(cfg);
45842     
45843     }
45844 });
45845
45846 Roo.ScrollPanel = function(el, config, content){
45847     config = config || {};
45848     config.fitToFrame = true;
45849     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45850     
45851     this.el.dom.style.overflow = "hidden";
45852     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45853     this.el.removeClass("x-layout-inactive-content");
45854     this.el.on("mousewheel", this.onWheel, this);
45855
45856     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45857     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45858     up.unselectable(); down.unselectable();
45859     up.on("click", this.scrollUp, this);
45860     down.on("click", this.scrollDown, this);
45861     up.addClassOnOver("x-scroller-btn-over");
45862     down.addClassOnOver("x-scroller-btn-over");
45863     up.addClassOnClick("x-scroller-btn-click");
45864     down.addClassOnClick("x-scroller-btn-click");
45865     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45866
45867     this.resizeEl = this.el;
45868     this.el = wrap; this.up = up; this.down = down;
45869 };
45870
45871 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45872     increment : 100,
45873     wheelIncrement : 5,
45874     scrollUp : function(){
45875         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45876     },
45877
45878     scrollDown : function(){
45879         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45880     },
45881
45882     afterScroll : function(){
45883         var el = this.resizeEl;
45884         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45885         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45886         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45887     },
45888
45889     setSize : function(){
45890         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45891         this.afterScroll();
45892     },
45893
45894     onWheel : function(e){
45895         var d = e.getWheelDelta();
45896         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45897         this.afterScroll();
45898         e.stopEvent();
45899     },
45900
45901     setContent : function(content, loadScripts){
45902         this.resizeEl.update(content, loadScripts);
45903     }
45904
45905 });
45906
45907
45908
45909
45910
45911
45912
45913
45914
45915 /**
45916  * @class Roo.TreePanel
45917  * @extends Roo.ContentPanel
45918  * @constructor
45919  * Create a new TreePanel. - defaults to fit/scoll contents.
45920  * @param {String/Object} config A string to set only the panel's title, or a config object
45921  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45922  */
45923 Roo.TreePanel = function(config){
45924     var el = config.el;
45925     var tree = config.tree;
45926     delete config.tree; 
45927     delete config.el; // hopefull!
45928     
45929     // wrapper for IE7 strict & safari scroll issue
45930     
45931     var treeEl = el.createChild();
45932     config.resizeEl = treeEl;
45933     
45934     
45935     
45936     Roo.TreePanel.superclass.constructor.call(this, el, config);
45937  
45938  
45939     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45940     //console.log(tree);
45941     this.on('activate', function()
45942     {
45943         if (this.tree.rendered) {
45944             return;
45945         }
45946         //console.log('render tree');
45947         this.tree.render();
45948     });
45949     
45950     this.on('resize',  function (cp, w, h) {
45951             this.tree.innerCt.setWidth(w);
45952             this.tree.innerCt.setHeight(h);
45953             this.tree.innerCt.setStyle('overflow-y', 'auto');
45954     });
45955
45956         
45957     
45958 };
45959
45960 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
45961     fitToFrame : true,
45962     autoScroll : true
45963 });
45964
45965
45966
45967
45968
45969
45970
45971
45972
45973
45974
45975 /*
45976  * Based on:
45977  * Ext JS Library 1.1.1
45978  * Copyright(c) 2006-2007, Ext JS, LLC.
45979  *
45980  * Originally Released Under LGPL - original licence link has changed is not relivant.
45981  *
45982  * Fork - LGPL
45983  * <script type="text/javascript">
45984  */
45985  
45986
45987 /**
45988  * @class Roo.ReaderLayout
45989  * @extends Roo.BorderLayout
45990  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45991  * center region containing two nested regions (a top one for a list view and one for item preview below),
45992  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45993  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45994  * expedites the setup of the overall layout and regions for this common application style.
45995  * Example:
45996  <pre><code>
45997 var reader = new Roo.ReaderLayout();
45998 var CP = Roo.ContentPanel;  // shortcut for adding
45999
46000 reader.beginUpdate();
46001 reader.add("north", new CP("north", "North"));
46002 reader.add("west", new CP("west", {title: "West"}));
46003 reader.add("east", new CP("east", {title: "East"}));
46004
46005 reader.regions.listView.add(new CP("listView", "List"));
46006 reader.regions.preview.add(new CP("preview", "Preview"));
46007 reader.endUpdate();
46008 </code></pre>
46009 * @constructor
46010 * Create a new ReaderLayout
46011 * @param {Object} config Configuration options
46012 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46013 * document.body if omitted)
46014 */
46015 Roo.ReaderLayout = function(config, renderTo){
46016     var c = config || {size:{}};
46017     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46018         north: c.north !== false ? Roo.apply({
46019             split:false,
46020             initialSize: 32,
46021             titlebar: false
46022         }, c.north) : false,
46023         west: c.west !== false ? Roo.apply({
46024             split:true,
46025             initialSize: 200,
46026             minSize: 175,
46027             maxSize: 400,
46028             titlebar: true,
46029             collapsible: true,
46030             animate: true,
46031             margins:{left:5,right:0,bottom:5,top:5},
46032             cmargins:{left:5,right:5,bottom:5,top:5}
46033         }, c.west) : false,
46034         east: c.east !== false ? Roo.apply({
46035             split:true,
46036             initialSize: 200,
46037             minSize: 175,
46038             maxSize: 400,
46039             titlebar: true,
46040             collapsible: true,
46041             animate: true,
46042             margins:{left:0,right:5,bottom:5,top:5},
46043             cmargins:{left:5,right:5,bottom:5,top:5}
46044         }, c.east) : false,
46045         center: Roo.apply({
46046             tabPosition: 'top',
46047             autoScroll:false,
46048             closeOnTab: true,
46049             titlebar:false,
46050             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46051         }, c.center)
46052     });
46053
46054     this.el.addClass('x-reader');
46055
46056     this.beginUpdate();
46057
46058     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46059         south: c.preview !== false ? Roo.apply({
46060             split:true,
46061             initialSize: 200,
46062             minSize: 100,
46063             autoScroll:true,
46064             collapsible:true,
46065             titlebar: true,
46066             cmargins:{top:5,left:0, right:0, bottom:0}
46067         }, c.preview) : false,
46068         center: Roo.apply({
46069             autoScroll:false,
46070             titlebar:false,
46071             minHeight:200
46072         }, c.listView)
46073     });
46074     this.add('center', new Roo.NestedLayoutPanel(inner,
46075             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46076
46077     this.endUpdate();
46078
46079     this.regions.preview = inner.getRegion('south');
46080     this.regions.listView = inner.getRegion('center');
46081 };
46082
46083 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46084  * Based on:
46085  * Ext JS Library 1.1.1
46086  * Copyright(c) 2006-2007, Ext JS, LLC.
46087  *
46088  * Originally Released Under LGPL - original licence link has changed is not relivant.
46089  *
46090  * Fork - LGPL
46091  * <script type="text/javascript">
46092  */
46093  
46094 /**
46095  * @class Roo.grid.Grid
46096  * @extends Roo.util.Observable
46097  * This class represents the primary interface of a component based grid control.
46098  * <br><br>Usage:<pre><code>
46099  var grid = new Roo.grid.Grid("my-container-id", {
46100      ds: myDataStore,
46101      cm: myColModel,
46102      selModel: mySelectionModel,
46103      autoSizeColumns: true,
46104      monitorWindowResize: false,
46105      trackMouseOver: true
46106  });
46107  // set any options
46108  grid.render();
46109  * </code></pre>
46110  * <b>Common Problems:</b><br/>
46111  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46112  * element will correct this<br/>
46113  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46114  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46115  * are unpredictable.<br/>
46116  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46117  * grid to calculate dimensions/offsets.<br/>
46118   * @constructor
46119  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46120  * The container MUST have some type of size defined for the grid to fill. The container will be
46121  * automatically set to position relative if it isn't already.
46122  * @param {Object} config A config object that sets properties on this grid.
46123  */
46124 Roo.grid.Grid = function(container, config){
46125         // initialize the container
46126         this.container = Roo.get(container);
46127         this.container.update("");
46128         this.container.setStyle("overflow", "hidden");
46129     this.container.addClass('x-grid-container');
46130
46131     this.id = this.container.id;
46132
46133     Roo.apply(this, config);
46134     // check and correct shorthanded configs
46135     if(this.ds){
46136         this.dataSource = this.ds;
46137         delete this.ds;
46138     }
46139     if(this.cm){
46140         this.colModel = this.cm;
46141         delete this.cm;
46142     }
46143     if(this.sm){
46144         this.selModel = this.sm;
46145         delete this.sm;
46146     }
46147
46148     if (this.selModel) {
46149         this.selModel = Roo.factory(this.selModel, Roo.grid);
46150         this.sm = this.selModel;
46151         this.sm.xmodule = this.xmodule || false;
46152     }
46153     if (typeof(this.colModel.config) == 'undefined') {
46154         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46155         this.cm = this.colModel;
46156         this.cm.xmodule = this.xmodule || false;
46157     }
46158     if (this.dataSource) {
46159         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46160         this.ds = this.dataSource;
46161         this.ds.xmodule = this.xmodule || false;
46162         
46163     }
46164     
46165     
46166     
46167     if(this.width){
46168         this.container.setWidth(this.width);
46169     }
46170
46171     if(this.height){
46172         this.container.setHeight(this.height);
46173     }
46174     /** @private */
46175         this.addEvents({
46176             // raw events
46177             /**
46178              * @event click
46179              * The raw click event for the entire grid.
46180              * @param {Roo.EventObject} e
46181              */
46182             "click" : true,
46183             /**
46184              * @event dblclick
46185              * The raw dblclick event for the entire grid.
46186              * @param {Roo.EventObject} e
46187              */
46188             "dblclick" : true,
46189             /**
46190              * @event contextmenu
46191              * The raw contextmenu event for the entire grid.
46192              * @param {Roo.EventObject} e
46193              */
46194             "contextmenu" : true,
46195             /**
46196              * @event mousedown
46197              * The raw mousedown event for the entire grid.
46198              * @param {Roo.EventObject} e
46199              */
46200             "mousedown" : true,
46201             /**
46202              * @event mouseup
46203              * The raw mouseup event for the entire grid.
46204              * @param {Roo.EventObject} e
46205              */
46206             "mouseup" : true,
46207             /**
46208              * @event mouseover
46209              * The raw mouseover event for the entire grid.
46210              * @param {Roo.EventObject} e
46211              */
46212             "mouseover" : true,
46213             /**
46214              * @event mouseout
46215              * The raw mouseout event for the entire grid.
46216              * @param {Roo.EventObject} e
46217              */
46218             "mouseout" : true,
46219             /**
46220              * @event keypress
46221              * The raw keypress event for the entire grid.
46222              * @param {Roo.EventObject} e
46223              */
46224             "keypress" : true,
46225             /**
46226              * @event keydown
46227              * The raw keydown event for the entire grid.
46228              * @param {Roo.EventObject} e
46229              */
46230             "keydown" : true,
46231
46232             // custom events
46233
46234             /**
46235              * @event cellclick
46236              * Fires when a cell is clicked
46237              * @param {Grid} this
46238              * @param {Number} rowIndex
46239              * @param {Number} columnIndex
46240              * @param {Roo.EventObject} e
46241              */
46242             "cellclick" : true,
46243             /**
46244              * @event celldblclick
46245              * Fires when a cell is double clicked
46246              * @param {Grid} this
46247              * @param {Number} rowIndex
46248              * @param {Number} columnIndex
46249              * @param {Roo.EventObject} e
46250              */
46251             "celldblclick" : true,
46252             /**
46253              * @event rowclick
46254              * Fires when a row is clicked
46255              * @param {Grid} this
46256              * @param {Number} rowIndex
46257              * @param {Roo.EventObject} e
46258              */
46259             "rowclick" : true,
46260             /**
46261              * @event rowdblclick
46262              * Fires when a row is double clicked
46263              * @param {Grid} this
46264              * @param {Number} rowIndex
46265              * @param {Roo.EventObject} e
46266              */
46267             "rowdblclick" : true,
46268             /**
46269              * @event headerclick
46270              * Fires when a header is clicked
46271              * @param {Grid} this
46272              * @param {Number} columnIndex
46273              * @param {Roo.EventObject} e
46274              */
46275             "headerclick" : true,
46276             /**
46277              * @event headerdblclick
46278              * Fires when a header cell is double clicked
46279              * @param {Grid} this
46280              * @param {Number} columnIndex
46281              * @param {Roo.EventObject} e
46282              */
46283             "headerdblclick" : true,
46284             /**
46285              * @event rowcontextmenu
46286              * Fires when a row is right clicked
46287              * @param {Grid} this
46288              * @param {Number} rowIndex
46289              * @param {Roo.EventObject} e
46290              */
46291             "rowcontextmenu" : true,
46292             /**
46293          * @event cellcontextmenu
46294          * Fires when a cell is right clicked
46295          * @param {Grid} this
46296          * @param {Number} rowIndex
46297          * @param {Number} cellIndex
46298          * @param {Roo.EventObject} e
46299          */
46300          "cellcontextmenu" : true,
46301             /**
46302              * @event headercontextmenu
46303              * Fires when a header is right clicked
46304              * @param {Grid} this
46305              * @param {Number} columnIndex
46306              * @param {Roo.EventObject} e
46307              */
46308             "headercontextmenu" : true,
46309             /**
46310              * @event bodyscroll
46311              * Fires when the body element is scrolled
46312              * @param {Number} scrollLeft
46313              * @param {Number} scrollTop
46314              */
46315             "bodyscroll" : true,
46316             /**
46317              * @event columnresize
46318              * Fires when the user resizes a column
46319              * @param {Number} columnIndex
46320              * @param {Number} newSize
46321              */
46322             "columnresize" : true,
46323             /**
46324              * @event columnmove
46325              * Fires when the user moves a column
46326              * @param {Number} oldIndex
46327              * @param {Number} newIndex
46328              */
46329             "columnmove" : true,
46330             /**
46331              * @event startdrag
46332              * Fires when row(s) start being dragged
46333              * @param {Grid} this
46334              * @param {Roo.GridDD} dd The drag drop object
46335              * @param {event} e The raw browser event
46336              */
46337             "startdrag" : true,
46338             /**
46339              * @event enddrag
46340              * Fires when a drag operation is complete
46341              * @param {Grid} this
46342              * @param {Roo.GridDD} dd The drag drop object
46343              * @param {event} e The raw browser event
46344              */
46345             "enddrag" : true,
46346             /**
46347              * @event dragdrop
46348              * Fires when dragged row(s) are dropped on a valid DD target
46349              * @param {Grid} this
46350              * @param {Roo.GridDD} dd The drag drop object
46351              * @param {String} targetId The target drag drop object
46352              * @param {event} e The raw browser event
46353              */
46354             "dragdrop" : true,
46355             /**
46356              * @event dragover
46357              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46358              * @param {Grid} this
46359              * @param {Roo.GridDD} dd The drag drop object
46360              * @param {String} targetId The target drag drop object
46361              * @param {event} e The raw browser event
46362              */
46363             "dragover" : true,
46364             /**
46365              * @event dragenter
46366              *  Fires when the dragged row(s) first cross another DD target while being dragged
46367              * @param {Grid} this
46368              * @param {Roo.GridDD} dd The drag drop object
46369              * @param {String} targetId The target drag drop object
46370              * @param {event} e The raw browser event
46371              */
46372             "dragenter" : true,
46373             /**
46374              * @event dragout
46375              * Fires when the dragged row(s) leave another DD target while being dragged
46376              * @param {Grid} this
46377              * @param {Roo.GridDD} dd The drag drop object
46378              * @param {String} targetId The target drag drop object
46379              * @param {event} e The raw browser event
46380              */
46381             "dragout" : true,
46382         /**
46383          * @event render
46384          * Fires when the grid is rendered
46385          * @param {Grid} grid
46386          */
46387         render : true
46388     });
46389
46390     Roo.grid.Grid.superclass.constructor.call(this);
46391 };
46392 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46393     
46394     /**
46395      * @cfg {String} ddGroup - drag drop group.
46396          */
46397     
46398     /**
46399      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46400          */
46401         minColumnWidth : 25,
46402
46403     /**
46404          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46405          * <b>on initial render.</b> It is more efficient to explicitly size the columns
46406          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46407          */
46408         autoSizeColumns : false,
46409
46410         /**
46411          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46412          */
46413         autoSizeHeaders : true,
46414
46415         /**
46416          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46417          */
46418         monitorWindowResize : true,
46419
46420         /**
46421          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46422          * rows measured to get a columns size. Default is 0 (all rows).
46423          */
46424         maxRowsToMeasure : 0,
46425
46426         /**
46427          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46428          */
46429         trackMouseOver : true,
46430
46431     /**
46432          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46433          */
46434     
46435         /**
46436          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46437          */
46438         enableDragDrop : false,
46439
46440         /**
46441          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46442          */
46443         enableColumnMove : true,
46444
46445         /**
46446          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46447          */
46448         enableColumnHide : true,
46449
46450         /**
46451          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46452          */
46453         enableRowHeightSync : false,
46454
46455         /**
46456          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46457          */
46458         stripeRows : true,
46459
46460         /**
46461          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46462          */
46463         autoHeight : false,
46464
46465     /**
46466      * @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.
46467      */
46468     autoExpandColumn : false,
46469
46470     /**
46471     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46472     * Default is 50.
46473     */
46474     autoExpandMin : 50,
46475
46476     /**
46477     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46478     */
46479     autoExpandMax : 1000,
46480
46481     /**
46482          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46483          */
46484         view : null,
46485
46486         /**
46487      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46488          */
46489         loadMask : false,
46490     /**
46491      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46492          */
46493         dropTarget: false,
46494     // private
46495     rendered : false,
46496
46497     /**
46498     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46499     * of a fixed width. Default is false.
46500     */
46501     /**
46502     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46503     */
46504     /**
46505      * Called once after all setup has been completed and the grid is ready to be rendered.
46506      * @return {Roo.grid.Grid} this
46507      */
46508     render : function(){
46509         var c = this.container;
46510         // try to detect autoHeight/width mode
46511         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46512             this.autoHeight = true;
46513         }
46514         var view = this.getView();
46515         view.init(this);
46516
46517         c.on("click", this.onClick, this);
46518         c.on("dblclick", this.onDblClick, this);
46519         c.on("contextmenu", this.onContextMenu, this);
46520         c.on("keydown", this.onKeyDown, this);
46521
46522         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46523
46524         this.getSelectionModel().init(this);
46525
46526         view.render();
46527
46528         if(this.loadMask){
46529             this.loadMask = new Roo.LoadMask(this.container,
46530                     Roo.apply({store:this.dataSource}, this.loadMask));
46531         }
46532         
46533         
46534         if (this.toolbar && this.toolbar.xtype) {
46535             this.toolbar.container = this.getView().getHeaderPanel(true);
46536             this.toolbar = new Ext.Toolbar(this.toolbar);
46537         }
46538         if (this.footer && this.footer.xtype) {
46539             this.footer.dataSource = this.getDataSource();
46540             this.footer.container = this.getView().getFooterPanel(true);
46541             this.footer = Roo.factory(this.footer, Roo);
46542         }
46543         if (this.dropTarget && this.dropTarget.xtype) {
46544             delete this.dropTarget.xtype;
46545             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46546         }
46547         
46548         
46549         this.rendered = true;
46550         this.fireEvent('render', this);
46551         return this;
46552     },
46553
46554         /**
46555          * Reconfigures the grid to use a different Store and Column Model.
46556          * The View will be bound to the new objects and refreshed.
46557          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46558          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46559          */
46560     reconfigure : function(dataSource, colModel){
46561         if(this.loadMask){
46562             this.loadMask.destroy();
46563             this.loadMask = new Roo.LoadMask(this.container,
46564                     Roo.apply({store:dataSource}, this.loadMask));
46565         }
46566         this.view.bind(dataSource, colModel);
46567         this.dataSource = dataSource;
46568         this.colModel = colModel;
46569         this.view.refresh(true);
46570     },
46571
46572     // private
46573     onKeyDown : function(e){
46574         this.fireEvent("keydown", e);
46575     },
46576
46577     /**
46578      * Destroy this grid.
46579      * @param {Boolean} removeEl True to remove the element
46580      */
46581     destroy : function(removeEl, keepListeners){
46582         if(this.loadMask){
46583             this.loadMask.destroy();
46584         }
46585         var c = this.container;
46586         c.removeAllListeners();
46587         this.view.destroy();
46588         this.colModel.purgeListeners();
46589         if(!keepListeners){
46590             this.purgeListeners();
46591         }
46592         c.update("");
46593         if(removeEl === true){
46594             c.remove();
46595         }
46596     },
46597
46598     // private
46599     processEvent : function(name, e){
46600         this.fireEvent(name, e);
46601         var t = e.getTarget();
46602         var v = this.view;
46603         var header = v.findHeaderIndex(t);
46604         if(header !== false){
46605             this.fireEvent("header" + name, this, header, e);
46606         }else{
46607             var row = v.findRowIndex(t);
46608             var cell = v.findCellIndex(t);
46609             if(row !== false){
46610                 this.fireEvent("row" + name, this, row, e);
46611                 if(cell !== false){
46612                     this.fireEvent("cell" + name, this, row, cell, e);
46613                 }
46614             }
46615         }
46616     },
46617
46618     // private
46619     onClick : function(e){
46620         this.processEvent("click", e);
46621     },
46622
46623     // private
46624     onContextMenu : function(e, t){
46625         this.processEvent("contextmenu", e);
46626     },
46627
46628     // private
46629     onDblClick : function(e){
46630         this.processEvent("dblclick", e);
46631     },
46632
46633     // private
46634     walkCells : function(row, col, step, fn, scope){
46635         var cm = this.colModel, clen = cm.getColumnCount();
46636         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46637         if(step < 0){
46638             if(col < 0){
46639                 row--;
46640                 first = false;
46641             }
46642             while(row >= 0){
46643                 if(!first){
46644                     col = clen-1;
46645                 }
46646                 first = false;
46647                 while(col >= 0){
46648                     if(fn.call(scope || this, row, col, cm) === true){
46649                         return [row, col];
46650                     }
46651                     col--;
46652                 }
46653                 row--;
46654             }
46655         } else {
46656             if(col >= clen){
46657                 row++;
46658                 first = false;
46659             }
46660             while(row < rlen){
46661                 if(!first){
46662                     col = 0;
46663                 }
46664                 first = false;
46665                 while(col < clen){
46666                     if(fn.call(scope || this, row, col, cm) === true){
46667                         return [row, col];
46668                     }
46669                     col++;
46670                 }
46671                 row++;
46672             }
46673         }
46674         return null;
46675     },
46676
46677     // private
46678     getSelections : function(){
46679         return this.selModel.getSelections();
46680     },
46681
46682     /**
46683      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46684      * but if manual update is required this method will initiate it.
46685      */
46686     autoSize : function(){
46687         if(this.rendered){
46688             this.view.layout();
46689             if(this.view.adjustForScroll){
46690                 this.view.adjustForScroll();
46691             }
46692         }
46693     },
46694
46695     /**
46696      * Returns the grid's underlying element.
46697      * @return {Element} The element
46698      */
46699     getGridEl : function(){
46700         return this.container;
46701     },
46702
46703     // private for compatibility, overridden by editor grid
46704     stopEditing : function(){},
46705
46706     /**
46707      * Returns the grid's SelectionModel.
46708      * @return {SelectionModel}
46709      */
46710     getSelectionModel : function(){
46711         if(!this.selModel){
46712             this.selModel = new Roo.grid.RowSelectionModel();
46713         }
46714         return this.selModel;
46715     },
46716
46717     /**
46718      * Returns the grid's DataSource.
46719      * @return {DataSource}
46720      */
46721     getDataSource : function(){
46722         return this.dataSource;
46723     },
46724
46725     /**
46726      * Returns the grid's ColumnModel.
46727      * @return {ColumnModel}
46728      */
46729     getColumnModel : function(){
46730         return this.colModel;
46731     },
46732
46733     /**
46734      * Returns the grid's GridView object.
46735      * @return {GridView}
46736      */
46737     getView : function(){
46738         if(!this.view){
46739             this.view = new Roo.grid.GridView(this.viewConfig);
46740         }
46741         return this.view;
46742     },
46743     /**
46744      * Called to get grid's drag proxy text, by default returns this.ddText.
46745      * @return {String}
46746      */
46747     getDragDropText : function(){
46748         var count = this.selModel.getCount();
46749         return String.format(this.ddText, count, count == 1 ? '' : 's');
46750     }
46751 });
46752 /**
46753  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46754  * %0 is replaced with the number of selected rows.
46755  * @type String
46756  */
46757 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46758  * Based on:
46759  * Ext JS Library 1.1.1
46760  * Copyright(c) 2006-2007, Ext JS, LLC.
46761  *
46762  * Originally Released Under LGPL - original licence link has changed is not relivant.
46763  *
46764  * Fork - LGPL
46765  * <script type="text/javascript">
46766  */
46767  
46768 Roo.grid.AbstractGridView = function(){
46769         this.grid = null;
46770         
46771         this.events = {
46772             "beforerowremoved" : true,
46773             "beforerowsinserted" : true,
46774             "beforerefresh" : true,
46775             "rowremoved" : true,
46776             "rowsinserted" : true,
46777             "rowupdated" : true,
46778             "refresh" : true
46779         };
46780     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46781 };
46782
46783 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46784     rowClass : "x-grid-row",
46785     cellClass : "x-grid-cell",
46786     tdClass : "x-grid-td",
46787     hdClass : "x-grid-hd",
46788     splitClass : "x-grid-hd-split",
46789     
46790         init: function(grid){
46791         this.grid = grid;
46792                 var cid = this.grid.getGridEl().id;
46793         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46794         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46795         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46796         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46797         },
46798         
46799         getColumnRenderers : function(){
46800         var renderers = [];
46801         var cm = this.grid.colModel;
46802         var colCount = cm.getColumnCount();
46803         for(var i = 0; i < colCount; i++){
46804             renderers[i] = cm.getRenderer(i);
46805         }
46806         return renderers;
46807     },
46808     
46809     getColumnIds : function(){
46810         var ids = [];
46811         var cm = this.grid.colModel;
46812         var colCount = cm.getColumnCount();
46813         for(var i = 0; i < colCount; i++){
46814             ids[i] = cm.getColumnId(i);
46815         }
46816         return ids;
46817     },
46818     
46819     getDataIndexes : function(){
46820         if(!this.indexMap){
46821             this.indexMap = this.buildIndexMap();
46822         }
46823         return this.indexMap.colToData;
46824     },
46825     
46826     getColumnIndexByDataIndex : function(dataIndex){
46827         if(!this.indexMap){
46828             this.indexMap = this.buildIndexMap();
46829         }
46830         return this.indexMap.dataToCol[dataIndex];
46831     },
46832     
46833     /**
46834      * Set a css style for a column dynamically. 
46835      * @param {Number} colIndex The index of the column
46836      * @param {String} name The css property name
46837      * @param {String} value The css value
46838      */
46839     setCSSStyle : function(colIndex, name, value){
46840         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46841         Roo.util.CSS.updateRule(selector, name, value);
46842     },
46843     
46844     generateRules : function(cm){
46845         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46846         Roo.util.CSS.removeStyleSheet(rulesId);
46847         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46848             var cid = cm.getColumnId(i);
46849             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46850                          this.tdSelector, cid, " {\n}\n",
46851                          this.hdSelector, cid, " {\n}\n",
46852                          this.splitSelector, cid, " {\n}\n");
46853         }
46854         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46855     }
46856 });/*
46857  * Based on:
46858  * Ext JS Library 1.1.1
46859  * Copyright(c) 2006-2007, Ext JS, LLC.
46860  *
46861  * Originally Released Under LGPL - original licence link has changed is not relivant.
46862  *
46863  * Fork - LGPL
46864  * <script type="text/javascript">
46865  */
46866
46867 // private
46868 // This is a support class used internally by the Grid components
46869 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46870     this.grid = grid;
46871     this.view = grid.getView();
46872     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46873     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46874     if(hd2){
46875         this.setHandleElId(Roo.id(hd));
46876         this.setOuterHandleElId(Roo.id(hd2));
46877     }
46878     this.scroll = false;
46879 };
46880 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46881     maxDragWidth: 120,
46882     getDragData : function(e){
46883         var t = Roo.lib.Event.getTarget(e);
46884         var h = this.view.findHeaderCell(t);
46885         if(h){
46886             return {ddel: h.firstChild, header:h};
46887         }
46888         return false;
46889     },
46890
46891     onInitDrag : function(e){
46892         this.view.headersDisabled = true;
46893         var clone = this.dragData.ddel.cloneNode(true);
46894         clone.id = Roo.id();
46895         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46896         this.proxy.update(clone);
46897         return true;
46898     },
46899
46900     afterValidDrop : function(){
46901         var v = this.view;
46902         setTimeout(function(){
46903             v.headersDisabled = false;
46904         }, 50);
46905     },
46906
46907     afterInvalidDrop : function(){
46908         var v = this.view;
46909         setTimeout(function(){
46910             v.headersDisabled = false;
46911         }, 50);
46912     }
46913 });
46914 /*
46915  * Based on:
46916  * Ext JS Library 1.1.1
46917  * Copyright(c) 2006-2007, Ext JS, LLC.
46918  *
46919  * Originally Released Under LGPL - original licence link has changed is not relivant.
46920  *
46921  * Fork - LGPL
46922  * <script type="text/javascript">
46923  */
46924 // private
46925 // This is a support class used internally by the Grid components
46926 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46927     this.grid = grid;
46928     this.view = grid.getView();
46929     // split the proxies so they don't interfere with mouse events
46930     this.proxyTop = Roo.DomHelper.append(document.body, {
46931         cls:"col-move-top", html:"&#160;"
46932     }, true);
46933     this.proxyBottom = Roo.DomHelper.append(document.body, {
46934         cls:"col-move-bottom", html:"&#160;"
46935     }, true);
46936     this.proxyTop.hide = this.proxyBottom.hide = function(){
46937         this.setLeftTop(-100,-100);
46938         this.setStyle("visibility", "hidden");
46939     };
46940     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46941     // temporarily disabled
46942     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46943     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46944 };
46945 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46946     proxyOffsets : [-4, -9],
46947     fly: Roo.Element.fly,
46948
46949     getTargetFromEvent : function(e){
46950         var t = Roo.lib.Event.getTarget(e);
46951         var cindex = this.view.findCellIndex(t);
46952         if(cindex !== false){
46953             return this.view.getHeaderCell(cindex);
46954         }
46955     },
46956
46957     nextVisible : function(h){
46958         var v = this.view, cm = this.grid.colModel;
46959         h = h.nextSibling;
46960         while(h){
46961             if(!cm.isHidden(v.getCellIndex(h))){
46962                 return h;
46963             }
46964             h = h.nextSibling;
46965         }
46966         return null;
46967     },
46968
46969     prevVisible : function(h){
46970         var v = this.view, cm = this.grid.colModel;
46971         h = h.prevSibling;
46972         while(h){
46973             if(!cm.isHidden(v.getCellIndex(h))){
46974                 return h;
46975             }
46976             h = h.prevSibling;
46977         }
46978         return null;
46979     },
46980
46981     positionIndicator : function(h, n, e){
46982         var x = Roo.lib.Event.getPageX(e);
46983         var r = Roo.lib.Dom.getRegion(n.firstChild);
46984         var px, pt, py = r.top + this.proxyOffsets[1];
46985         if((r.right - x) <= (r.right-r.left)/2){
46986             px = r.right+this.view.borderWidth;
46987             pt = "after";
46988         }else{
46989             px = r.left;
46990             pt = "before";
46991         }
46992         var oldIndex = this.view.getCellIndex(h);
46993         var newIndex = this.view.getCellIndex(n);
46994
46995         if(this.grid.colModel.isFixed(newIndex)){
46996             return false;
46997         }
46998
46999         var locked = this.grid.colModel.isLocked(newIndex);
47000
47001         if(pt == "after"){
47002             newIndex++;
47003         }
47004         if(oldIndex < newIndex){
47005             newIndex--;
47006         }
47007         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47008             return false;
47009         }
47010         px +=  this.proxyOffsets[0];
47011         this.proxyTop.setLeftTop(px, py);
47012         this.proxyTop.show();
47013         if(!this.bottomOffset){
47014             this.bottomOffset = this.view.mainHd.getHeight();
47015         }
47016         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47017         this.proxyBottom.show();
47018         return pt;
47019     },
47020
47021     onNodeEnter : function(n, dd, e, data){
47022         if(data.header != n){
47023             this.positionIndicator(data.header, n, e);
47024         }
47025     },
47026
47027     onNodeOver : function(n, dd, e, data){
47028         var result = false;
47029         if(data.header != n){
47030             result = this.positionIndicator(data.header, n, e);
47031         }
47032         if(!result){
47033             this.proxyTop.hide();
47034             this.proxyBottom.hide();
47035         }
47036         return result ? this.dropAllowed : this.dropNotAllowed;
47037     },
47038
47039     onNodeOut : function(n, dd, e, data){
47040         this.proxyTop.hide();
47041         this.proxyBottom.hide();
47042     },
47043
47044     onNodeDrop : function(n, dd, e, data){
47045         var h = data.header;
47046         if(h != n){
47047             var cm = this.grid.colModel;
47048             var x = Roo.lib.Event.getPageX(e);
47049             var r = Roo.lib.Dom.getRegion(n.firstChild);
47050             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47051             var oldIndex = this.view.getCellIndex(h);
47052             var newIndex = this.view.getCellIndex(n);
47053             var locked = cm.isLocked(newIndex);
47054             if(pt == "after"){
47055                 newIndex++;
47056             }
47057             if(oldIndex < newIndex){
47058                 newIndex--;
47059             }
47060             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47061                 return false;
47062             }
47063             cm.setLocked(oldIndex, locked, true);
47064             cm.moveColumn(oldIndex, newIndex);
47065             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47066             return true;
47067         }
47068         return false;
47069     }
47070 });
47071 /*
47072  * Based on:
47073  * Ext JS Library 1.1.1
47074  * Copyright(c) 2006-2007, Ext JS, LLC.
47075  *
47076  * Originally Released Under LGPL - original licence link has changed is not relivant.
47077  *
47078  * Fork - LGPL
47079  * <script type="text/javascript">
47080  */
47081   
47082 /**
47083  * @class Roo.grid.GridView
47084  * @extends Roo.util.Observable
47085  *
47086  * @constructor
47087  * @param {Object} config
47088  */
47089 Roo.grid.GridView = function(config){
47090     Roo.grid.GridView.superclass.constructor.call(this);
47091     this.el = null;
47092
47093     Roo.apply(this, config);
47094 };
47095
47096 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47097
47098     /**
47099      * Override this function to apply custom css classes to rows during rendering
47100      * @param {Record} record The record
47101      * @param {Number} index
47102      * @method getRowClass
47103      */
47104     rowClass : "x-grid-row",
47105
47106     cellClass : "x-grid-col",
47107
47108     tdClass : "x-grid-td",
47109
47110     hdClass : "x-grid-hd",
47111
47112     splitClass : "x-grid-split",
47113
47114     sortClasses : ["sort-asc", "sort-desc"],
47115
47116     enableMoveAnim : false,
47117
47118     hlColor: "C3DAF9",
47119
47120     dh : Roo.DomHelper,
47121
47122     fly : Roo.Element.fly,
47123
47124     css : Roo.util.CSS,
47125
47126     borderWidth: 1,
47127
47128     splitOffset: 3,
47129
47130     scrollIncrement : 22,
47131
47132     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47133
47134     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47135
47136     bind : function(ds, cm){
47137         if(this.ds){
47138             this.ds.un("load", this.onLoad, this);
47139             this.ds.un("datachanged", this.onDataChange, this);
47140             this.ds.un("add", this.onAdd, this);
47141             this.ds.un("remove", this.onRemove, this);
47142             this.ds.un("update", this.onUpdate, this);
47143             this.ds.un("clear", this.onClear, this);
47144         }
47145         if(ds){
47146             ds.on("load", this.onLoad, this);
47147             ds.on("datachanged", this.onDataChange, this);
47148             ds.on("add", this.onAdd, this);
47149             ds.on("remove", this.onRemove, this);
47150             ds.on("update", this.onUpdate, this);
47151             ds.on("clear", this.onClear, this);
47152         }
47153         this.ds = ds;
47154
47155         if(this.cm){
47156             this.cm.un("widthchange", this.onColWidthChange, this);
47157             this.cm.un("headerchange", this.onHeaderChange, this);
47158             this.cm.un("hiddenchange", this.onHiddenChange, this);
47159             this.cm.un("columnmoved", this.onColumnMove, this);
47160             this.cm.un("columnlockchange", this.onColumnLock, this);
47161         }
47162         if(cm){
47163             this.generateRules(cm);
47164             cm.on("widthchange", this.onColWidthChange, this);
47165             cm.on("headerchange", this.onHeaderChange, this);
47166             cm.on("hiddenchange", this.onHiddenChange, this);
47167             cm.on("columnmoved", this.onColumnMove, this);
47168             cm.on("columnlockchange", this.onColumnLock, this);
47169         }
47170         this.cm = cm;
47171     },
47172
47173     init: function(grid){
47174                 Roo.grid.GridView.superclass.init.call(this, grid);
47175
47176                 this.bind(grid.dataSource, grid.colModel);
47177
47178             grid.on("headerclick", this.handleHeaderClick, this);
47179
47180         if(grid.trackMouseOver){
47181             grid.on("mouseover", this.onRowOver, this);
47182                 grid.on("mouseout", this.onRowOut, this);
47183             }
47184             grid.cancelTextSelection = function(){};
47185                 this.gridId = grid.id;
47186
47187                 var tpls = this.templates || {};
47188
47189                 if(!tpls.master){
47190                     tpls.master = new Roo.Template(
47191                        '<div class="x-grid" hidefocus="true">',
47192                           '<div class="x-grid-topbar"></div>',
47193                           '<div class="x-grid-scroller"><div></div></div>',
47194                           '<div class="x-grid-locked">',
47195                               '<div class="x-grid-header">{lockedHeader}</div>',
47196                               '<div class="x-grid-body">{lockedBody}</div>',
47197                           "</div>",
47198                           '<div class="x-grid-viewport">',
47199                               '<div class="x-grid-header">{header}</div>',
47200                               '<div class="x-grid-body">{body}</div>',
47201                           "</div>",
47202                           '<div class="x-grid-bottombar"></div>',
47203                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47204                           '<div class="x-grid-resize-proxy">&#160;</div>',
47205                        "</div>"
47206                     );
47207                     tpls.master.disableformats = true;
47208                 }
47209
47210                 if(!tpls.header){
47211                     tpls.header = new Roo.Template(
47212                        '<table border="0" cellspacing="0" cellpadding="0">',
47213                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47214                        "</table>{splits}"
47215                     );
47216                     tpls.header.disableformats = true;
47217                 }
47218                 tpls.header.compile();
47219
47220                 if(!tpls.hcell){
47221                     tpls.hcell = new Roo.Template(
47222                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47223                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47224                         "</div></td>"
47225                      );
47226                      tpls.hcell.disableFormats = true;
47227                 }
47228                 tpls.hcell.compile();
47229
47230                 if(!tpls.hsplit){
47231                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47232                     tpls.hsplit.disableFormats = true;
47233                 }
47234                 tpls.hsplit.compile();
47235
47236                 if(!tpls.body){
47237                     tpls.body = new Roo.Template(
47238                        '<table border="0" cellspacing="0" cellpadding="0">',
47239                        "<tbody>{rows}</tbody>",
47240                        "</table>"
47241                     );
47242                     tpls.body.disableFormats = true;
47243                 }
47244                 tpls.body.compile();
47245
47246                 if(!tpls.row){
47247                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47248                     tpls.row.disableFormats = true;
47249                 }
47250                 tpls.row.compile();
47251
47252                 if(!tpls.cell){
47253                     tpls.cell = new Roo.Template(
47254                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47255                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47256                         "</td>"
47257                     );
47258             tpls.cell.disableFormats = true;
47259         }
47260                 tpls.cell.compile();
47261
47262                 this.templates = tpls;
47263         },
47264
47265         // remap these for backwards compat
47266     onColWidthChange : function(){
47267         this.updateColumns.apply(this, arguments);
47268     },
47269     onHeaderChange : function(){
47270         this.updateHeaders.apply(this, arguments);
47271     }, 
47272     onHiddenChange : function(){
47273         this.handleHiddenChange.apply(this, arguments);
47274     },
47275     onColumnMove : function(){
47276         this.handleColumnMove.apply(this, arguments);
47277     },
47278     onColumnLock : function(){
47279         this.handleLockChange.apply(this, arguments);
47280     },
47281
47282     onDataChange : function(){
47283         this.refresh();
47284         this.updateHeaderSortState();
47285     },
47286
47287         onClear : function(){
47288         this.refresh();
47289     },
47290
47291         onUpdate : function(ds, record){
47292         this.refreshRow(record);
47293     },
47294
47295     refreshRow : function(record){
47296         var ds = this.ds, index;
47297         if(typeof record == 'number'){
47298             index = record;
47299             record = ds.getAt(index);
47300         }else{
47301             index = ds.indexOf(record);
47302         }
47303         this.insertRows(ds, index, index, true);
47304         this.onRemove(ds, record, index+1, true);
47305         this.syncRowHeights(index, index);
47306         this.layout();
47307         this.fireEvent("rowupdated", this, index, record);
47308     },
47309
47310     onAdd : function(ds, records, index){
47311         this.insertRows(ds, index, index + (records.length-1));
47312     },
47313
47314     onRemove : function(ds, record, index, isUpdate){
47315         if(isUpdate !== true){
47316             this.fireEvent("beforerowremoved", this, index, record);
47317         }
47318         var bt = this.getBodyTable(), lt = this.getLockedTable();
47319         if(bt.rows[index]){
47320             bt.firstChild.removeChild(bt.rows[index]);
47321         }
47322         if(lt.rows[index]){
47323             lt.firstChild.removeChild(lt.rows[index]);
47324         }
47325         if(isUpdate !== true){
47326             this.stripeRows(index);
47327             this.syncRowHeights(index, index);
47328             this.layout();
47329             this.fireEvent("rowremoved", this, index, record);
47330         }
47331     },
47332
47333     onLoad : function(){
47334         this.scrollToTop();
47335     },
47336
47337     /**
47338      * Scrolls the grid to the top
47339      */
47340     scrollToTop : function(){
47341         if(this.scroller){
47342             this.scroller.dom.scrollTop = 0;
47343             this.syncScroll();
47344         }
47345     },
47346
47347     /**
47348      * Gets a panel in the header of the grid that can be used for toolbars etc.
47349      * After modifying the contents of this panel a call to grid.autoSize() may be
47350      * required to register any changes in size.
47351      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47352      * @return Roo.Element
47353      */
47354     getHeaderPanel : function(doShow){
47355         if(doShow){
47356             this.headerPanel.show();
47357         }
47358         return this.headerPanel;
47359         },
47360
47361         /**
47362      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47363      * After modifying the contents of this panel a call to grid.autoSize() may be
47364      * required to register any changes in size.
47365      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47366      * @return Roo.Element
47367      */
47368     getFooterPanel : function(doShow){
47369         if(doShow){
47370             this.footerPanel.show();
47371         }
47372         return this.footerPanel;
47373         },
47374
47375         initElements : function(){
47376             var E = Roo.Element;
47377             var el = this.grid.getGridEl().dom.firstChild;
47378             var cs = el.childNodes;
47379
47380             this.el = new E(el);
47381             this.headerPanel = new E(el.firstChild);
47382             this.headerPanel.enableDisplayMode("block");
47383
47384         this.scroller = new E(cs[1]);
47385             this.scrollSizer = new E(this.scroller.dom.firstChild);
47386
47387             this.lockedWrap = new E(cs[2]);
47388             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47389             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47390
47391             this.mainWrap = new E(cs[3]);
47392             this.mainHd = new E(this.mainWrap.dom.firstChild);
47393             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47394
47395             this.footerPanel = new E(cs[4]);
47396             this.footerPanel.enableDisplayMode("block");
47397
47398         this.focusEl = new E(cs[5]);
47399         this.focusEl.swallowEvent("click", true);
47400         this.resizeProxy = new E(cs[6]);
47401
47402             this.headerSelector = String.format(
47403                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47404                this.lockedHd.id, this.mainHd.id
47405             );
47406
47407             this.splitterSelector = String.format(
47408                '#{0} div.x-grid-split, #{1} div.x-grid-split',
47409                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47410             );
47411     },
47412     idToCssName : function(s)
47413     {
47414         return s.replace(/[^a-z0-9]+/ig, '-');
47415     },
47416
47417         getHeaderCell : function(index){
47418             return Roo.DomQuery.select(this.headerSelector)[index];
47419         },
47420
47421         getHeaderCellMeasure : function(index){
47422             return this.getHeaderCell(index).firstChild;
47423         },
47424
47425         getHeaderCellText : function(index){
47426             return this.getHeaderCell(index).firstChild.firstChild;
47427         },
47428
47429         getLockedTable : function(){
47430             return this.lockedBody.dom.firstChild;
47431         },
47432
47433         getBodyTable : function(){
47434             return this.mainBody.dom.firstChild;
47435         },
47436
47437         getLockedRow : function(index){
47438             return this.getLockedTable().rows[index];
47439         },
47440
47441         getRow : function(index){
47442             return this.getBodyTable().rows[index];
47443         },
47444
47445         getRowComposite : function(index){
47446             if(!this.rowEl){
47447                 this.rowEl = new Roo.CompositeElementLite();
47448             }
47449         var els = [], lrow, mrow;
47450         if(lrow = this.getLockedRow(index)){
47451             els.push(lrow);
47452         }
47453         if(mrow = this.getRow(index)){
47454             els.push(mrow);
47455         }
47456         this.rowEl.elements = els;
47457             return this.rowEl;
47458         },
47459
47460         getCell : function(rowIndex, colIndex){
47461             var locked = this.cm.getLockedCount();
47462             var source;
47463             if(colIndex < locked){
47464                 source = this.lockedBody.dom.firstChild;
47465             }else{
47466                 source = this.mainBody.dom.firstChild;
47467                 colIndex -= locked;
47468             }
47469         return source.rows[rowIndex].childNodes[colIndex];
47470         },
47471
47472         getCellText : function(rowIndex, colIndex){
47473             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47474         },
47475
47476         getCellBox : function(cell){
47477             var b = this.fly(cell).getBox();
47478         if(Roo.isOpera){ // opera fails to report the Y
47479             b.y = cell.offsetTop + this.mainBody.getY();
47480         }
47481         return b;
47482     },
47483
47484     getCellIndex : function(cell){
47485         var id = String(cell.className).match(this.cellRE);
47486         if(id){
47487             return parseInt(id[1], 10);
47488         }
47489         return 0;
47490     },
47491
47492     findHeaderIndex : function(n){
47493         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47494         return r ? this.getCellIndex(r) : false;
47495     },
47496
47497     findHeaderCell : function(n){
47498         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47499         return r ? r : false;
47500     },
47501
47502     findRowIndex : function(n){
47503         if(!n){
47504             return false;
47505         }
47506         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47507         return r ? r.rowIndex : false;
47508     },
47509
47510     findCellIndex : function(node){
47511         var stop = this.el.dom;
47512         while(node && node != stop){
47513             if(this.findRE.test(node.className)){
47514                 return this.getCellIndex(node);
47515             }
47516             node = node.parentNode;
47517         }
47518         return false;
47519     },
47520
47521     getColumnId : function(index){
47522             return this.cm.getColumnId(index);
47523         },
47524
47525         getSplitters : function(){
47526             if(this.splitterSelector){
47527                return Roo.DomQuery.select(this.splitterSelector);
47528             }else{
47529                 return null;
47530             }
47531         },
47532
47533         getSplitter : function(index){
47534             return this.getSplitters()[index];
47535         },
47536
47537     onRowOver : function(e, t){
47538         var row;
47539         if((row = this.findRowIndex(t)) !== false){
47540             this.getRowComposite(row).addClass("x-grid-row-over");
47541         }
47542     },
47543
47544     onRowOut : function(e, t){
47545         var row;
47546         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47547             this.getRowComposite(row).removeClass("x-grid-row-over");
47548         }
47549     },
47550
47551     renderHeaders : function(){
47552             var cm = this.cm;
47553         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47554         var cb = [], lb = [], sb = [], lsb = [], p = {};
47555         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47556             p.cellId = "x-grid-hd-0-" + i;
47557             p.splitId = "x-grid-csplit-0-" + i;
47558             p.id = cm.getColumnId(i);
47559             p.title = cm.getColumnTooltip(i) || "";
47560             p.value = cm.getColumnHeader(i) || "";
47561             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47562             if(!cm.isLocked(i)){
47563                 cb[cb.length] = ct.apply(p);
47564                 sb[sb.length] = st.apply(p);
47565             }else{
47566                 lb[lb.length] = ct.apply(p);
47567                 lsb[lsb.length] = st.apply(p);
47568             }
47569         }
47570         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47571                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47572         },
47573
47574         updateHeaders : function(){
47575         var html = this.renderHeaders();
47576         this.lockedHd.update(html[0]);
47577         this.mainHd.update(html[1]);
47578     },
47579
47580     /**
47581      * Focuses the specified row.
47582      * @param {Number} row The row index
47583      */
47584     focusRow : function(row){
47585         var x = this.scroller.dom.scrollLeft;
47586         this.focusCell(row, 0, false);
47587         this.scroller.dom.scrollLeft = x;
47588     },
47589
47590     /**
47591      * Focuses the specified cell.
47592      * @param {Number} row The row index
47593      * @param {Number} col The column index
47594      * @param {Boolean} hscroll false to disable horizontal scrolling
47595      */
47596     focusCell : function(row, col, hscroll){
47597         var el = this.ensureVisible(row, col, hscroll);
47598         this.focusEl.alignTo(el, "tl-tl");
47599         if(Roo.isGecko){
47600             this.focusEl.focus();
47601         }else{
47602             this.focusEl.focus.defer(1, this.focusEl);
47603         }
47604     },
47605
47606     /**
47607      * Scrolls the specified cell into view
47608      * @param {Number} row The row index
47609      * @param {Number} col The column index
47610      * @param {Boolean} hscroll false to disable horizontal scrolling
47611      */
47612     ensureVisible : function(row, col, hscroll){
47613         if(typeof row != "number"){
47614             row = row.rowIndex;
47615         }
47616         if(row < 0 && row >= this.ds.getCount()){
47617             return;
47618         }
47619         col = (col !== undefined ? col : 0);
47620         var cm = this.grid.colModel;
47621         while(cm.isHidden(col)){
47622             col++;
47623         }
47624
47625         var el = this.getCell(row, col);
47626         if(!el){
47627             return;
47628         }
47629         var c = this.scroller.dom;
47630
47631         var ctop = parseInt(el.offsetTop, 10);
47632         var cleft = parseInt(el.offsetLeft, 10);
47633         var cbot = ctop + el.offsetHeight;
47634         var cright = cleft + el.offsetWidth;
47635
47636         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47637         var stop = parseInt(c.scrollTop, 10);
47638         var sleft = parseInt(c.scrollLeft, 10);
47639         var sbot = stop + ch;
47640         var sright = sleft + c.clientWidth;
47641
47642         if(ctop < stop){
47643                 c.scrollTop = ctop;
47644         }else if(cbot > sbot){
47645             c.scrollTop = cbot-ch;
47646         }
47647
47648         if(hscroll !== false){
47649             if(cleft < sleft){
47650                 c.scrollLeft = cleft;
47651             }else if(cright > sright){
47652                 c.scrollLeft = cright-c.clientWidth;
47653             }
47654         }
47655         return el;
47656     },
47657
47658     updateColumns : function(){
47659         this.grid.stopEditing();
47660         var cm = this.grid.colModel, colIds = this.getColumnIds();
47661         //var totalWidth = cm.getTotalWidth();
47662         var pos = 0;
47663         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47664             //if(cm.isHidden(i)) continue;
47665             var w = cm.getColumnWidth(i);
47666             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47667             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47668         }
47669         this.updateSplitters();
47670     },
47671
47672     generateRules : function(cm){
47673         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47674         Roo.util.CSS.removeStyleSheet(rulesId);
47675         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47676             var cid = cm.getColumnId(i);
47677             var align = '';
47678             if(cm.config[i].align){
47679                 align = 'text-align:'+cm.config[i].align+';';
47680             }
47681             var hidden = '';
47682             if(cm.isHidden(i)){
47683                 hidden = 'display:none;';
47684             }
47685             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47686             ruleBuf.push(
47687                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47688                     this.hdSelector, cid, " {\n", align, width, "}\n",
47689                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47690                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47691         }
47692         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47693     },
47694
47695     updateSplitters : function(){
47696         var cm = this.cm, s = this.getSplitters();
47697         if(s){ // splitters not created yet
47698             var pos = 0, locked = true;
47699             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47700                 if(cm.isHidden(i)) continue;
47701                 var w = cm.getColumnWidth(i);
47702                 if(!cm.isLocked(i) && locked){
47703                     pos = 0;
47704                     locked = false;
47705                 }
47706                 pos += w;
47707                 s[i].style.left = (pos-this.splitOffset) + "px";
47708             }
47709         }
47710     },
47711
47712     handleHiddenChange : function(colModel, colIndex, hidden){
47713         if(hidden){
47714             this.hideColumn(colIndex);
47715         }else{
47716             this.unhideColumn(colIndex);
47717         }
47718     },
47719
47720     hideColumn : function(colIndex){
47721         var cid = this.getColumnId(colIndex);
47722         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47723         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47724         if(Roo.isSafari){
47725             this.updateHeaders();
47726         }
47727         this.updateSplitters();
47728         this.layout();
47729     },
47730
47731     unhideColumn : function(colIndex){
47732         var cid = this.getColumnId(colIndex);
47733         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47734         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47735
47736         if(Roo.isSafari){
47737             this.updateHeaders();
47738         }
47739         this.updateSplitters();
47740         this.layout();
47741     },
47742
47743     insertRows : function(dm, firstRow, lastRow, isUpdate){
47744         if(firstRow == 0 && lastRow == dm.getCount()-1){
47745             this.refresh();
47746         }else{
47747             if(!isUpdate){
47748                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47749             }
47750             var s = this.getScrollState();
47751             var markup = this.renderRows(firstRow, lastRow);
47752             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47753             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47754             this.restoreScroll(s);
47755             if(!isUpdate){
47756                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47757                 this.syncRowHeights(firstRow, lastRow);
47758                 this.stripeRows(firstRow);
47759                 this.layout();
47760             }
47761         }
47762     },
47763
47764     bufferRows : function(markup, target, index){
47765         var before = null, trows = target.rows, tbody = target.tBodies[0];
47766         if(index < trows.length){
47767             before = trows[index];
47768         }
47769         var b = document.createElement("div");
47770         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47771         var rows = b.firstChild.rows;
47772         for(var i = 0, len = rows.length; i < len; i++){
47773             if(before){
47774                 tbody.insertBefore(rows[0], before);
47775             }else{
47776                 tbody.appendChild(rows[0]);
47777             }
47778         }
47779         b.innerHTML = "";
47780         b = null;
47781     },
47782
47783     deleteRows : function(dm, firstRow, lastRow){
47784         if(dm.getRowCount()<1){
47785             this.fireEvent("beforerefresh", this);
47786             this.mainBody.update("");
47787             this.lockedBody.update("");
47788             this.fireEvent("refresh", this);
47789         }else{
47790             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47791             var bt = this.getBodyTable();
47792             var tbody = bt.firstChild;
47793             var rows = bt.rows;
47794             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47795                 tbody.removeChild(rows[firstRow]);
47796             }
47797             this.stripeRows(firstRow);
47798             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47799         }
47800     },
47801
47802     updateRows : function(dataSource, firstRow, lastRow){
47803         var s = this.getScrollState();
47804         this.refresh();
47805         this.restoreScroll(s);
47806     },
47807
47808     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47809         if(!noRefresh){
47810            this.refresh();
47811         }
47812         this.updateHeaderSortState();
47813     },
47814
47815     getScrollState : function(){
47816         var sb = this.scroller.dom;
47817         return {left: sb.scrollLeft, top: sb.scrollTop};
47818     },
47819
47820     stripeRows : function(startRow){
47821         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47822             return;
47823         }
47824         startRow = startRow || 0;
47825         var rows = this.getBodyTable().rows;
47826         var lrows = this.getLockedTable().rows;
47827         var cls = ' x-grid-row-alt ';
47828         for(var i = startRow, len = rows.length; i < len; i++){
47829             var row = rows[i], lrow = lrows[i];
47830             var isAlt = ((i+1) % 2 == 0);
47831             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47832             if(isAlt == hasAlt){
47833                 continue;
47834             }
47835             if(isAlt){
47836                 row.className += " x-grid-row-alt";
47837             }else{
47838                 row.className = row.className.replace("x-grid-row-alt", "");
47839             }
47840             if(lrow){
47841                 lrow.className = row.className;
47842             }
47843         }
47844     },
47845
47846     restoreScroll : function(state){
47847         var sb = this.scroller.dom;
47848         sb.scrollLeft = state.left;
47849         sb.scrollTop = state.top;
47850         this.syncScroll();
47851     },
47852
47853     syncScroll : function(){
47854         var sb = this.scroller.dom;
47855         var sh = this.mainHd.dom;
47856         var bs = this.mainBody.dom;
47857         var lv = this.lockedBody.dom;
47858         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47859         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47860     },
47861
47862     handleScroll : function(e){
47863         this.syncScroll();
47864         var sb = this.scroller.dom;
47865         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47866         e.stopEvent();
47867     },
47868
47869     handleWheel : function(e){
47870         var d = e.getWheelDelta();
47871         this.scroller.dom.scrollTop -= d*22;
47872         // set this here to prevent jumpy scrolling on large tables
47873         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47874         e.stopEvent();
47875     },
47876
47877     renderRows : function(startRow, endRow){
47878         // pull in all the crap needed to render rows
47879         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47880         var colCount = cm.getColumnCount();
47881
47882         if(ds.getCount() < 1){
47883             return ["", ""];
47884         }
47885
47886         // build a map for all the columns
47887         var cs = [];
47888         for(var i = 0; i < colCount; i++){
47889             var name = cm.getDataIndex(i);
47890             cs[i] = {
47891                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47892                 renderer : cm.getRenderer(i),
47893                 id : cm.getColumnId(i),
47894                 locked : cm.isLocked(i)
47895             };
47896         }
47897
47898         startRow = startRow || 0;
47899         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47900
47901         // records to render
47902         var rs = ds.getRange(startRow, endRow);
47903
47904         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47905     },
47906
47907     // As much as I hate to duplicate code, this was branched because FireFox really hates
47908     // [].join("") on strings. The performance difference was substantial enough to
47909     // branch this function
47910     doRender : Roo.isGecko ?
47911             function(cs, rs, ds, startRow, colCount, stripe){
47912                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47913                 // buffers
47914                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47915                 for(var j = 0, len = rs.length; j < len; j++){
47916                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47917                     for(var i = 0; i < colCount; i++){
47918                         c = cs[i];
47919                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47920                         p.id = c.id;
47921                         p.css = p.attr = "";
47922                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47923                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47924                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47925                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47926                         }
47927                         var markup = ct.apply(p);
47928                         if(!c.locked){
47929                             cb+= markup;
47930                         }else{
47931                             lcb+= markup;
47932                         }
47933                     }
47934                     var alt = [];
47935                     if(stripe && ((rowIndex+1) % 2 == 0)){
47936                         alt[0] = "x-grid-row-alt";
47937                     }
47938                     if(r.dirty){
47939                         alt[1] = " x-grid-dirty-row";
47940                     }
47941                     rp.cells = lcb;
47942                     if(this.getRowClass){
47943                         alt[2] = this.getRowClass(r, rowIndex);
47944                     }
47945                     rp.alt = alt.join(" ");
47946                     lbuf+= rt.apply(rp);
47947                     rp.cells = cb;
47948                     buf+=  rt.apply(rp);
47949                 }
47950                 return [lbuf, buf];
47951             } :
47952             function(cs, rs, ds, startRow, colCount, stripe){
47953                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47954                 // buffers
47955                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47956                 for(var j = 0, len = rs.length; j < len; j++){
47957                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47958                     for(var i = 0; i < colCount; i++){
47959                         c = cs[i];
47960                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47961                         p.id = c.id;
47962                         p.css = p.attr = "";
47963                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47964                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47965                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47966                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47967                         }
47968                         var markup = ct.apply(p);
47969                         if(!c.locked){
47970                             cb[cb.length] = markup;
47971                         }else{
47972                             lcb[lcb.length] = markup;
47973                         }
47974                     }
47975                     var alt = [];
47976                     if(stripe && ((rowIndex+1) % 2 == 0)){
47977                         alt[0] = "x-grid-row-alt";
47978                     }
47979                     if(r.dirty){
47980                         alt[1] = " x-grid-dirty-row";
47981                     }
47982                     rp.cells = lcb;
47983                     if(this.getRowClass){
47984                         alt[2] = this.getRowClass(r, rowIndex);
47985                     }
47986                     rp.alt = alt.join(" ");
47987                     rp.cells = lcb.join("");
47988                     lbuf[lbuf.length] = rt.apply(rp);
47989                     rp.cells = cb.join("");
47990                     buf[buf.length] =  rt.apply(rp);
47991                 }
47992                 return [lbuf.join(""), buf.join("")];
47993             },
47994
47995     renderBody : function(){
47996         var markup = this.renderRows();
47997         var bt = this.templates.body;
47998         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47999     },
48000
48001     /**
48002      * Refreshes the grid
48003      * @param {Boolean} headersToo
48004      */
48005     refresh : function(headersToo){
48006         this.fireEvent("beforerefresh", this);
48007         this.grid.stopEditing();
48008         var result = this.renderBody();
48009         this.lockedBody.update(result[0]);
48010         this.mainBody.update(result[1]);
48011         if(headersToo === true){
48012             this.updateHeaders();
48013             this.updateColumns();
48014             this.updateSplitters();
48015             this.updateHeaderSortState();
48016         }
48017         this.syncRowHeights();
48018         this.layout();
48019         this.fireEvent("refresh", this);
48020     },
48021
48022     handleColumnMove : function(cm, oldIndex, newIndex){
48023         this.indexMap = null;
48024         var s = this.getScrollState();
48025         this.refresh(true);
48026         this.restoreScroll(s);
48027         this.afterMove(newIndex);
48028     },
48029
48030     afterMove : function(colIndex){
48031         if(this.enableMoveAnim && Roo.enableFx){
48032             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48033         }
48034     },
48035
48036     updateCell : function(dm, rowIndex, dataIndex){
48037         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48038         if(typeof colIndex == "undefined"){ // not present in grid
48039             return;
48040         }
48041         var cm = this.grid.colModel;
48042         var cell = this.getCell(rowIndex, colIndex);
48043         var cellText = this.getCellText(rowIndex, colIndex);
48044
48045         var p = {
48046             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48047             id : cm.getColumnId(colIndex),
48048             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48049         };
48050         var renderer = cm.getRenderer(colIndex);
48051         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48052         if(typeof val == "undefined" || val === "") val = "&#160;";
48053         cellText.innerHTML = val;
48054         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48055         this.syncRowHeights(rowIndex, rowIndex);
48056     },
48057
48058     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48059         var maxWidth = 0;
48060         if(this.grid.autoSizeHeaders){
48061             var h = this.getHeaderCellMeasure(colIndex);
48062             maxWidth = Math.max(maxWidth, h.scrollWidth);
48063         }
48064         var tb, index;
48065         if(this.cm.isLocked(colIndex)){
48066             tb = this.getLockedTable();
48067             index = colIndex;
48068         }else{
48069             tb = this.getBodyTable();
48070             index = colIndex - this.cm.getLockedCount();
48071         }
48072         if(tb && tb.rows){
48073             var rows = tb.rows;
48074             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48075             for(var i = 0; i < stopIndex; i++){
48076                 var cell = rows[i].childNodes[index].firstChild;
48077                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48078             }
48079         }
48080         return maxWidth + /*margin for error in IE*/ 5;
48081     },
48082     /**
48083      * Autofit a column to its content.
48084      * @param {Number} colIndex
48085      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48086      */
48087      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48088          if(this.cm.isHidden(colIndex)){
48089              return; // can't calc a hidden column
48090          }
48091         if(forceMinSize){
48092             var cid = this.cm.getColumnId(colIndex);
48093             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48094            if(this.grid.autoSizeHeaders){
48095                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48096            }
48097         }
48098         var newWidth = this.calcColumnWidth(colIndex);
48099         this.cm.setColumnWidth(colIndex,
48100             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48101         if(!suppressEvent){
48102             this.grid.fireEvent("columnresize", colIndex, newWidth);
48103         }
48104     },
48105
48106     /**
48107      * Autofits all columns to their content and then expands to fit any extra space in the grid
48108      */
48109      autoSizeColumns : function(){
48110         var cm = this.grid.colModel;
48111         var colCount = cm.getColumnCount();
48112         for(var i = 0; i < colCount; i++){
48113             this.autoSizeColumn(i, true, true);
48114         }
48115         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48116             this.fitColumns();
48117         }else{
48118             this.updateColumns();
48119             this.layout();
48120         }
48121     },
48122
48123     /**
48124      * Autofits all columns to the grid's width proportionate with their current size
48125      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48126      */
48127     fitColumns : function(reserveScrollSpace){
48128         var cm = this.grid.colModel;
48129         var colCount = cm.getColumnCount();
48130         var cols = [];
48131         var width = 0;
48132         var i, w;
48133         for (i = 0; i < colCount; i++){
48134             if(!cm.isHidden(i) && !cm.isFixed(i)){
48135                 w = cm.getColumnWidth(i);
48136                 cols.push(i);
48137                 cols.push(w);
48138                 width += w;
48139             }
48140         }
48141         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48142         if(reserveScrollSpace){
48143             avail -= 17;
48144         }
48145         var frac = (avail - cm.getTotalWidth())/width;
48146         while (cols.length){
48147             w = cols.pop();
48148             i = cols.pop();
48149             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48150         }
48151         this.updateColumns();
48152         this.layout();
48153     },
48154
48155     onRowSelect : function(rowIndex){
48156         var row = this.getRowComposite(rowIndex);
48157         row.addClass("x-grid-row-selected");
48158     },
48159
48160     onRowDeselect : function(rowIndex){
48161         var row = this.getRowComposite(rowIndex);
48162         row.removeClass("x-grid-row-selected");
48163     },
48164
48165     onCellSelect : function(row, col){
48166         var cell = this.getCell(row, col);
48167         if(cell){
48168             Roo.fly(cell).addClass("x-grid-cell-selected");
48169         }
48170     },
48171
48172     onCellDeselect : function(row, col){
48173         var cell = this.getCell(row, col);
48174         if(cell){
48175             Roo.fly(cell).removeClass("x-grid-cell-selected");
48176         }
48177     },
48178
48179     updateHeaderSortState : function(){
48180         var state = this.ds.getSortState();
48181         if(!state){
48182             return;
48183         }
48184         this.sortState = state;
48185         var sortColumn = this.cm.findColumnIndex(state.field);
48186         if(sortColumn != -1){
48187             var sortDir = state.direction;
48188             var sc = this.sortClasses;
48189             var hds = this.el.select(this.headerSelector).removeClass(sc);
48190             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48191         }
48192     },
48193
48194     handleHeaderClick : function(g, index){
48195         if(this.headersDisabled){
48196             return;
48197         }
48198         var dm = g.dataSource, cm = g.colModel;
48199             if(!cm.isSortable(index)){
48200             return;
48201         }
48202             g.stopEditing();
48203         dm.sort(cm.getDataIndex(index));
48204     },
48205
48206
48207     destroy : function(){
48208         if(this.colMenu){
48209             this.colMenu.removeAll();
48210             Roo.menu.MenuMgr.unregister(this.colMenu);
48211             this.colMenu.getEl().remove();
48212             delete this.colMenu;
48213         }
48214         if(this.hmenu){
48215             this.hmenu.removeAll();
48216             Roo.menu.MenuMgr.unregister(this.hmenu);
48217             this.hmenu.getEl().remove();
48218             delete this.hmenu;
48219         }
48220         if(this.grid.enableColumnMove){
48221             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48222             if(dds){
48223                 for(var dd in dds){
48224                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48225                         var elid = dds[dd].dragElId;
48226                         dds[dd].unreg();
48227                         Roo.get(elid).remove();
48228                     } else if(dds[dd].config.isTarget){
48229                         dds[dd].proxyTop.remove();
48230                         dds[dd].proxyBottom.remove();
48231                         dds[dd].unreg();
48232                     }
48233                     if(Roo.dd.DDM.locationCache[dd]){
48234                         delete Roo.dd.DDM.locationCache[dd];
48235                     }
48236                 }
48237                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48238             }
48239         }
48240         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48241         this.bind(null, null);
48242         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48243     },
48244
48245     handleLockChange : function(){
48246         this.refresh(true);
48247     },
48248
48249     onDenyColumnLock : function(){
48250
48251     },
48252
48253     onDenyColumnHide : function(){
48254
48255     },
48256
48257     handleHdMenuClick : function(item){
48258         var index = this.hdCtxIndex;
48259         var cm = this.cm, ds = this.ds;
48260         switch(item.id){
48261             case "asc":
48262                 ds.sort(cm.getDataIndex(index), "ASC");
48263                 break;
48264             case "desc":
48265                 ds.sort(cm.getDataIndex(index), "DESC");
48266                 break;
48267             case "lock":
48268                 var lc = cm.getLockedCount();
48269                 if(cm.getColumnCount(true) <= lc+1){
48270                     this.onDenyColumnLock();
48271                     return;
48272                 }
48273                 if(lc != index){
48274                     cm.setLocked(index, true, true);
48275                     cm.moveColumn(index, lc);
48276                     this.grid.fireEvent("columnmove", index, lc);
48277                 }else{
48278                     cm.setLocked(index, true);
48279                 }
48280             break;
48281             case "unlock":
48282                 var lc = cm.getLockedCount();
48283                 if((lc-1) != index){
48284                     cm.setLocked(index, false, true);
48285                     cm.moveColumn(index, lc-1);
48286                     this.grid.fireEvent("columnmove", index, lc-1);
48287                 }else{
48288                     cm.setLocked(index, false);
48289                 }
48290             break;
48291             default:
48292                 index = cm.getIndexById(item.id.substr(4));
48293                 if(index != -1){
48294                     if(item.checked && cm.getColumnCount(true) <= 1){
48295                         this.onDenyColumnHide();
48296                         return false;
48297                     }
48298                     cm.setHidden(index, item.checked);
48299                 }
48300         }
48301         return true;
48302     },
48303
48304     beforeColMenuShow : function(){
48305         var cm = this.cm,  colCount = cm.getColumnCount();
48306         this.colMenu.removeAll();
48307         for(var i = 0; i < colCount; i++){
48308             this.colMenu.add(new Roo.menu.CheckItem({
48309                 id: "col-"+cm.getColumnId(i),
48310                 text: cm.getColumnHeader(i),
48311                 checked: !cm.isHidden(i),
48312                 hideOnClick:false
48313             }));
48314         }
48315     },
48316
48317     handleHdCtx : function(g, index, e){
48318         e.stopEvent();
48319         var hd = this.getHeaderCell(index);
48320         this.hdCtxIndex = index;
48321         var ms = this.hmenu.items, cm = this.cm;
48322         ms.get("asc").setDisabled(!cm.isSortable(index));
48323         ms.get("desc").setDisabled(!cm.isSortable(index));
48324         if(this.grid.enableColLock !== false){
48325             ms.get("lock").setDisabled(cm.isLocked(index));
48326             ms.get("unlock").setDisabled(!cm.isLocked(index));
48327         }
48328         this.hmenu.show(hd, "tl-bl");
48329     },
48330
48331     handleHdOver : function(e){
48332         var hd = this.findHeaderCell(e.getTarget());
48333         if(hd && !this.headersDisabled){
48334             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48335                this.fly(hd).addClass("x-grid-hd-over");
48336             }
48337         }
48338     },
48339
48340     handleHdOut : function(e){
48341         var hd = this.findHeaderCell(e.getTarget());
48342         if(hd){
48343             this.fly(hd).removeClass("x-grid-hd-over");
48344         }
48345     },
48346
48347     handleSplitDblClick : function(e, t){
48348         var i = this.getCellIndex(t);
48349         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48350             this.autoSizeColumn(i, true);
48351             this.layout();
48352         }
48353     },
48354
48355     render : function(){
48356
48357         var cm = this.cm;
48358         var colCount = cm.getColumnCount();
48359
48360         if(this.grid.monitorWindowResize === true){
48361             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48362         }
48363         var header = this.renderHeaders();
48364         var body = this.templates.body.apply({rows:""});
48365         var html = this.templates.master.apply({
48366             lockedBody: body,
48367             body: body,
48368             lockedHeader: header[0],
48369             header: header[1]
48370         });
48371
48372         //this.updateColumns();
48373
48374         this.grid.getGridEl().dom.innerHTML = html;
48375
48376         this.initElements();
48377         
48378         // a kludge to fix the random scolling effect in webkit
48379         this.el.on("scroll", function() {
48380             this.el.dom.scrollTop=0; // hopefully not recursive..
48381         },this);
48382
48383         this.scroller.on("scroll", this.handleScroll, this);
48384         this.lockedBody.on("mousewheel", this.handleWheel, this);
48385         this.mainBody.on("mousewheel", this.handleWheel, this);
48386
48387         this.mainHd.on("mouseover", this.handleHdOver, this);
48388         this.mainHd.on("mouseout", this.handleHdOut, this);
48389         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48390                 {delegate: "."+this.splitClass});
48391
48392         this.lockedHd.on("mouseover", this.handleHdOver, this);
48393         this.lockedHd.on("mouseout", this.handleHdOut, this);
48394         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48395                 {delegate: "."+this.splitClass});
48396
48397         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48398             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48399         }
48400
48401         this.updateSplitters();
48402
48403         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48404             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48405             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48406         }
48407
48408         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48409             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48410             this.hmenu.add(
48411                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48412                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48413             );
48414             if(this.grid.enableColLock !== false){
48415                 this.hmenu.add('-',
48416                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48417                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48418                 );
48419             }
48420             if(this.grid.enableColumnHide !== false){
48421
48422                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48423                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48424                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48425
48426                 this.hmenu.add('-',
48427                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48428                 );
48429             }
48430             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48431
48432             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48433         }
48434
48435         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48436             this.dd = new Roo.grid.GridDragZone(this.grid, {
48437                 ddGroup : this.grid.ddGroup || 'GridDD'
48438             });
48439         }
48440
48441         /*
48442         for(var i = 0; i < colCount; i++){
48443             if(cm.isHidden(i)){
48444                 this.hideColumn(i);
48445             }
48446             if(cm.config[i].align){
48447                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48448                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48449             }
48450         }*/
48451         
48452         this.updateHeaderSortState();
48453
48454         this.beforeInitialResize();
48455         this.layout(true);
48456
48457         // two part rendering gives faster view to the user
48458         this.renderPhase2.defer(1, this);
48459     },
48460
48461     renderPhase2 : function(){
48462         // render the rows now
48463         this.refresh();
48464         if(this.grid.autoSizeColumns){
48465             this.autoSizeColumns();
48466         }
48467     },
48468
48469     beforeInitialResize : function(){
48470
48471     },
48472
48473     onColumnSplitterMoved : function(i, w){
48474         this.userResized = true;
48475         var cm = this.grid.colModel;
48476         cm.setColumnWidth(i, w, true);
48477         var cid = cm.getColumnId(i);
48478         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48479         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48480         this.updateSplitters();
48481         this.layout();
48482         this.grid.fireEvent("columnresize", i, w);
48483     },
48484
48485     syncRowHeights : function(startIndex, endIndex){
48486         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48487             startIndex = startIndex || 0;
48488             var mrows = this.getBodyTable().rows;
48489             var lrows = this.getLockedTable().rows;
48490             var len = mrows.length-1;
48491             endIndex = Math.min(endIndex || len, len);
48492             for(var i = startIndex; i <= endIndex; i++){
48493                 var m = mrows[i], l = lrows[i];
48494                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48495                 m.style.height = l.style.height = h + "px";
48496             }
48497         }
48498     },
48499
48500     layout : function(initialRender, is2ndPass){
48501         var g = this.grid;
48502         var auto = g.autoHeight;
48503         var scrollOffset = 16;
48504         var c = g.getGridEl(), cm = this.cm,
48505                 expandCol = g.autoExpandColumn,
48506                 gv = this;
48507         //c.beginMeasure();
48508
48509         if(!c.dom.offsetWidth){ // display:none?
48510             if(initialRender){
48511                 this.lockedWrap.show();
48512                 this.mainWrap.show();
48513             }
48514             return;
48515         }
48516
48517         var hasLock = this.cm.isLocked(0);
48518
48519         var tbh = this.headerPanel.getHeight();
48520         var bbh = this.footerPanel.getHeight();
48521
48522         if(auto){
48523             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48524             var newHeight = ch + c.getBorderWidth("tb");
48525             if(g.maxHeight){
48526                 newHeight = Math.min(g.maxHeight, newHeight);
48527             }
48528             c.setHeight(newHeight);
48529         }
48530
48531         if(g.autoWidth){
48532             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48533         }
48534
48535         var s = this.scroller;
48536
48537         var csize = c.getSize(true);
48538
48539         this.el.setSize(csize.width, csize.height);
48540
48541         this.headerPanel.setWidth(csize.width);
48542         this.footerPanel.setWidth(csize.width);
48543
48544         var hdHeight = this.mainHd.getHeight();
48545         var vw = csize.width;
48546         var vh = csize.height - (tbh + bbh);
48547
48548         s.setSize(vw, vh);
48549
48550         var bt = this.getBodyTable();
48551         var ltWidth = hasLock ?
48552                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48553
48554         var scrollHeight = bt.offsetHeight;
48555         var scrollWidth = ltWidth + bt.offsetWidth;
48556         var vscroll = false, hscroll = false;
48557
48558         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48559
48560         var lw = this.lockedWrap, mw = this.mainWrap;
48561         var lb = this.lockedBody, mb = this.mainBody;
48562
48563         setTimeout(function(){
48564             var t = s.dom.offsetTop;
48565             var w = s.dom.clientWidth,
48566                 h = s.dom.clientHeight;
48567
48568             lw.setTop(t);
48569             lw.setSize(ltWidth, h);
48570
48571             mw.setLeftTop(ltWidth, t);
48572             mw.setSize(w-ltWidth, h);
48573
48574             lb.setHeight(h-hdHeight);
48575             mb.setHeight(h-hdHeight);
48576
48577             if(is2ndPass !== true && !gv.userResized && expandCol){
48578                 // high speed resize without full column calculation
48579                 
48580                 var ci = cm.getIndexById(expandCol);
48581                 if (ci < 0) {
48582                     ci = cm.findColumnIndex(expandCol);
48583                 }
48584                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48585                 var expandId = cm.getColumnId(ci);
48586                 var  tw = cm.getTotalWidth(false);
48587                 var currentWidth = cm.getColumnWidth(ci);
48588                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48589                 if(currentWidth != cw){
48590                     cm.setColumnWidth(ci, cw, true);
48591                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48592                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48593                     gv.updateSplitters();
48594                     gv.layout(false, true);
48595                 }
48596             }
48597
48598             if(initialRender){
48599                 lw.show();
48600                 mw.show();
48601             }
48602             //c.endMeasure();
48603         }, 10);
48604     },
48605
48606     onWindowResize : function(){
48607         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48608             return;
48609         }
48610         this.layout();
48611     },
48612
48613     appendFooter : function(parentEl){
48614         return null;
48615     },
48616
48617     sortAscText : "Sort Ascending",
48618     sortDescText : "Sort Descending",
48619     lockText : "Lock Column",
48620     unlockText : "Unlock Column",
48621     columnsText : "Columns"
48622 });
48623
48624
48625 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48626     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48627     this.proxy.el.addClass('x-grid3-col-dd');
48628 };
48629
48630 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48631     handleMouseDown : function(e){
48632
48633     },
48634
48635     callHandleMouseDown : function(e){
48636         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48637     }
48638 });
48639 /*
48640  * Based on:
48641  * Ext JS Library 1.1.1
48642  * Copyright(c) 2006-2007, Ext JS, LLC.
48643  *
48644  * Originally Released Under LGPL - original licence link has changed is not relivant.
48645  *
48646  * Fork - LGPL
48647  * <script type="text/javascript">
48648  */
48649  
48650 // private
48651 // This is a support class used internally by the Grid components
48652 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48653     this.grid = grid;
48654     this.view = grid.getView();
48655     this.proxy = this.view.resizeProxy;
48656     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48657         "gridSplitters" + this.grid.getGridEl().id, {
48658         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48659     });
48660     this.setHandleElId(Roo.id(hd));
48661     this.setOuterHandleElId(Roo.id(hd2));
48662     this.scroll = false;
48663 };
48664 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48665     fly: Roo.Element.fly,
48666
48667     b4StartDrag : function(x, y){
48668         this.view.headersDisabled = true;
48669         this.proxy.setHeight(this.view.mainWrap.getHeight());
48670         var w = this.cm.getColumnWidth(this.cellIndex);
48671         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48672         this.resetConstraints();
48673         this.setXConstraint(minw, 1000);
48674         this.setYConstraint(0, 0);
48675         this.minX = x - minw;
48676         this.maxX = x + 1000;
48677         this.startPos = x;
48678         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48679     },
48680
48681
48682     handleMouseDown : function(e){
48683         ev = Roo.EventObject.setEvent(e);
48684         var t = this.fly(ev.getTarget());
48685         if(t.hasClass("x-grid-split")){
48686             this.cellIndex = this.view.getCellIndex(t.dom);
48687             this.split = t.dom;
48688             this.cm = this.grid.colModel;
48689             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48690                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48691             }
48692         }
48693     },
48694
48695     endDrag : function(e){
48696         this.view.headersDisabled = false;
48697         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48698         var diff = endX - this.startPos;
48699         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48700     },
48701
48702     autoOffset : function(){
48703         this.setDelta(0,0);
48704     }
48705 });/*
48706  * Based on:
48707  * Ext JS Library 1.1.1
48708  * Copyright(c) 2006-2007, Ext JS, LLC.
48709  *
48710  * Originally Released Under LGPL - original licence link has changed is not relivant.
48711  *
48712  * Fork - LGPL
48713  * <script type="text/javascript">
48714  */
48715  
48716 // private
48717 // This is a support class used internally by the Grid components
48718 Roo.grid.GridDragZone = function(grid, config){
48719     this.view = grid.getView();
48720     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48721     if(this.view.lockedBody){
48722         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48723         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48724     }
48725     this.scroll = false;
48726     this.grid = grid;
48727     this.ddel = document.createElement('div');
48728     this.ddel.className = 'x-grid-dd-wrap';
48729 };
48730
48731 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48732     ddGroup : "GridDD",
48733
48734     getDragData : function(e){
48735         var t = Roo.lib.Event.getTarget(e);
48736         var rowIndex = this.view.findRowIndex(t);
48737         if(rowIndex !== false){
48738             var sm = this.grid.selModel;
48739             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48740               //  sm.mouseDown(e, t);
48741             //}
48742             if (e.hasModifier()){
48743                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48744             }
48745             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48746         }
48747         return false;
48748     },
48749
48750     onInitDrag : function(e){
48751         var data = this.dragData;
48752         this.ddel.innerHTML = this.grid.getDragDropText();
48753         this.proxy.update(this.ddel);
48754         // fire start drag?
48755     },
48756
48757     afterRepair : function(){
48758         this.dragging = false;
48759     },
48760
48761     getRepairXY : function(e, data){
48762         return false;
48763     },
48764
48765     onEndDrag : function(data, e){
48766         // fire end drag?
48767     },
48768
48769     onValidDrop : function(dd, e, id){
48770         // fire drag drop?
48771         this.hideProxy();
48772     },
48773
48774     beforeInvalidDrop : function(e, id){
48775
48776     }
48777 });/*
48778  * Based on:
48779  * Ext JS Library 1.1.1
48780  * Copyright(c) 2006-2007, Ext JS, LLC.
48781  *
48782  * Originally Released Under LGPL - original licence link has changed is not relivant.
48783  *
48784  * Fork - LGPL
48785  * <script type="text/javascript">
48786  */
48787  
48788
48789 /**
48790  * @class Roo.grid.ColumnModel
48791  * @extends Roo.util.Observable
48792  * This is the default implementation of a ColumnModel used by the Grid. It defines
48793  * the columns in the grid.
48794  * <br>Usage:<br>
48795  <pre><code>
48796  var colModel = new Roo.grid.ColumnModel([
48797         {header: "Ticker", width: 60, sortable: true, locked: true},
48798         {header: "Company Name", width: 150, sortable: true},
48799         {header: "Market Cap.", width: 100, sortable: true},
48800         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48801         {header: "Employees", width: 100, sortable: true, resizable: false}
48802  ]);
48803  </code></pre>
48804  * <p>
48805  
48806  * The config options listed for this class are options which may appear in each
48807  * individual column definition.
48808  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48809  * @constructor
48810  * @param {Object} config An Array of column config objects. See this class's
48811  * config objects for details.
48812 */
48813 Roo.grid.ColumnModel = function(config){
48814         /**
48815      * The config passed into the constructor
48816      */
48817     this.config = config;
48818     this.lookup = {};
48819
48820     // if no id, create one
48821     // if the column does not have a dataIndex mapping,
48822     // map it to the order it is in the config
48823     for(var i = 0, len = config.length; i < len; i++){
48824         var c = config[i];
48825         if(typeof c.dataIndex == "undefined"){
48826             c.dataIndex = i;
48827         }
48828         if(typeof c.renderer == "string"){
48829             c.renderer = Roo.util.Format[c.renderer];
48830         }
48831         if(typeof c.id == "undefined"){
48832             c.id = Roo.id();
48833         }
48834         if(c.editor && c.editor.xtype){
48835             c.editor  = Roo.factory(c.editor, Roo.grid);
48836         }
48837         if(c.editor && c.editor.isFormField){
48838             c.editor = new Roo.grid.GridEditor(c.editor);
48839         }
48840         this.lookup[c.id] = c;
48841     }
48842
48843     /**
48844      * The width of columns which have no width specified (defaults to 100)
48845      * @type Number
48846      */
48847     this.defaultWidth = 100;
48848
48849     /**
48850      * Default sortable of columns which have no sortable specified (defaults to false)
48851      * @type Boolean
48852      */
48853     this.defaultSortable = false;
48854
48855     this.addEvents({
48856         /**
48857              * @event widthchange
48858              * Fires when the width of a column changes.
48859              * @param {ColumnModel} this
48860              * @param {Number} columnIndex The column index
48861              * @param {Number} newWidth The new width
48862              */
48863             "widthchange": true,
48864         /**
48865              * @event headerchange
48866              * Fires when the text of a header changes.
48867              * @param {ColumnModel} this
48868              * @param {Number} columnIndex The column index
48869              * @param {Number} newText The new header text
48870              */
48871             "headerchange": true,
48872         /**
48873              * @event hiddenchange
48874              * Fires when a column is hidden or "unhidden".
48875              * @param {ColumnModel} this
48876              * @param {Number} columnIndex The column index
48877              * @param {Boolean} hidden true if hidden, false otherwise
48878              */
48879             "hiddenchange": true,
48880             /**
48881          * @event columnmoved
48882          * Fires when a column is moved.
48883          * @param {ColumnModel} this
48884          * @param {Number} oldIndex
48885          * @param {Number} newIndex
48886          */
48887         "columnmoved" : true,
48888         /**
48889          * @event columlockchange
48890          * Fires when a column's locked state is changed
48891          * @param {ColumnModel} this
48892          * @param {Number} colIndex
48893          * @param {Boolean} locked true if locked
48894          */
48895         "columnlockchange" : true
48896     });
48897     Roo.grid.ColumnModel.superclass.constructor.call(this);
48898 };
48899 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48900     /**
48901      * @cfg {String} header The header text to display in the Grid view.
48902      */
48903     /**
48904      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48905      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48906      * specified, the column's index is used as an index into the Record's data Array.
48907      */
48908     /**
48909      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48910      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48911      */
48912     /**
48913      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48914      * Defaults to the value of the {@link #defaultSortable} property.
48915      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48916      */
48917     /**
48918      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48919      */
48920     /**
48921      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48922      */
48923     /**
48924      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48925      */
48926     /**
48927      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48928      */
48929     /**
48930      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48931      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48932      * default renderer uses the raw data value.
48933      */
48934        /**
48935      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48936      */
48937     /**
48938      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48939      */
48940
48941     /**
48942      * Returns the id of the column at the specified index.
48943      * @param {Number} index The column index
48944      * @return {String} the id
48945      */
48946     getColumnId : function(index){
48947         return this.config[index].id;
48948     },
48949
48950     /**
48951      * Returns the column for a specified id.
48952      * @param {String} id The column id
48953      * @return {Object} the column
48954      */
48955     getColumnById : function(id){
48956         return this.lookup[id];
48957     },
48958
48959     
48960     /**
48961      * Returns the column for a specified dataIndex.
48962      * @param {String} dataIndex The column dataIndex
48963      * @return {Object|Boolean} the column or false if not found
48964      */
48965     getColumnByDataIndex: function(dataIndex){
48966         var index = this.findColumnIndex(dataIndex);
48967         return index > -1 ? this.config[index] : false;
48968     },
48969     
48970     /**
48971      * Returns the index for a specified column id.
48972      * @param {String} id The column id
48973      * @return {Number} the index, or -1 if not found
48974      */
48975     getIndexById : function(id){
48976         for(var i = 0, len = this.config.length; i < len; i++){
48977             if(this.config[i].id == id){
48978                 return i;
48979             }
48980         }
48981         return -1;
48982     },
48983     
48984     /**
48985      * Returns the index for a specified column dataIndex.
48986      * @param {String} dataIndex The column dataIndex
48987      * @return {Number} the index, or -1 if not found
48988      */
48989     
48990     findColumnIndex : function(dataIndex){
48991         for(var i = 0, len = this.config.length; i < len; i++){
48992             if(this.config[i].dataIndex == dataIndex){
48993                 return i;
48994             }
48995         }
48996         return -1;
48997     },
48998     
48999     
49000     moveColumn : function(oldIndex, newIndex){
49001         var c = this.config[oldIndex];
49002         this.config.splice(oldIndex, 1);
49003         this.config.splice(newIndex, 0, c);
49004         this.dataMap = null;
49005         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49006     },
49007
49008     isLocked : function(colIndex){
49009         return this.config[colIndex].locked === true;
49010     },
49011
49012     setLocked : function(colIndex, value, suppressEvent){
49013         if(this.isLocked(colIndex) == value){
49014             return;
49015         }
49016         this.config[colIndex].locked = value;
49017         if(!suppressEvent){
49018             this.fireEvent("columnlockchange", this, colIndex, value);
49019         }
49020     },
49021
49022     getTotalLockedWidth : function(){
49023         var totalWidth = 0;
49024         for(var i = 0; i < this.config.length; i++){
49025             if(this.isLocked(i) && !this.isHidden(i)){
49026                 this.totalWidth += this.getColumnWidth(i);
49027             }
49028         }
49029         return totalWidth;
49030     },
49031
49032     getLockedCount : function(){
49033         for(var i = 0, len = this.config.length; i < len; i++){
49034             if(!this.isLocked(i)){
49035                 return i;
49036             }
49037         }
49038     },
49039
49040     /**
49041      * Returns the number of columns.
49042      * @return {Number}
49043      */
49044     getColumnCount : function(visibleOnly){
49045         if(visibleOnly === true){
49046             var c = 0;
49047             for(var i = 0, len = this.config.length; i < len; i++){
49048                 if(!this.isHidden(i)){
49049                     c++;
49050                 }
49051             }
49052             return c;
49053         }
49054         return this.config.length;
49055     },
49056
49057     /**
49058      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49059      * @param {Function} fn
49060      * @param {Object} scope (optional)
49061      * @return {Array} result
49062      */
49063     getColumnsBy : function(fn, scope){
49064         var r = [];
49065         for(var i = 0, len = this.config.length; i < len; i++){
49066             var c = this.config[i];
49067             if(fn.call(scope||this, c, i) === true){
49068                 r[r.length] = c;
49069             }
49070         }
49071         return r;
49072     },
49073
49074     /**
49075      * Returns true if the specified column is sortable.
49076      * @param {Number} col The column index
49077      * @return {Boolean}
49078      */
49079     isSortable : function(col){
49080         if(typeof this.config[col].sortable == "undefined"){
49081             return this.defaultSortable;
49082         }
49083         return this.config[col].sortable;
49084     },
49085
49086     /**
49087      * Returns the rendering (formatting) function defined for the column.
49088      * @param {Number} col The column index.
49089      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49090      */
49091     getRenderer : function(col){
49092         if(!this.config[col].renderer){
49093             return Roo.grid.ColumnModel.defaultRenderer;
49094         }
49095         return this.config[col].renderer;
49096     },
49097
49098     /**
49099      * Sets the rendering (formatting) function for a column.
49100      * @param {Number} col The column index
49101      * @param {Function} fn The function to use to process the cell's raw data
49102      * to return HTML markup for the grid view. The render function is called with
49103      * the following parameters:<ul>
49104      * <li>Data value.</li>
49105      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49106      * <li>css A CSS style string to apply to the table cell.</li>
49107      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49108      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49109      * <li>Row index</li>
49110      * <li>Column index</li>
49111      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49112      */
49113     setRenderer : function(col, fn){
49114         this.config[col].renderer = fn;
49115     },
49116
49117     /**
49118      * Returns the width for the specified column.
49119      * @param {Number} col The column index
49120      * @return {Number}
49121      */
49122     getColumnWidth : function(col){
49123         return this.config[col].width || this.defaultWidth;
49124     },
49125
49126     /**
49127      * Sets the width for a column.
49128      * @param {Number} col The column index
49129      * @param {Number} width The new width
49130      */
49131     setColumnWidth : function(col, width, suppressEvent){
49132         this.config[col].width = width;
49133         this.totalWidth = null;
49134         if(!suppressEvent){
49135              this.fireEvent("widthchange", this, col, width);
49136         }
49137     },
49138
49139     /**
49140      * Returns the total width of all columns.
49141      * @param {Boolean} includeHidden True to include hidden column widths
49142      * @return {Number}
49143      */
49144     getTotalWidth : function(includeHidden){
49145         if(!this.totalWidth){
49146             this.totalWidth = 0;
49147             for(var i = 0, len = this.config.length; i < len; i++){
49148                 if(includeHidden || !this.isHidden(i)){
49149                     this.totalWidth += this.getColumnWidth(i);
49150                 }
49151             }
49152         }
49153         return this.totalWidth;
49154     },
49155
49156     /**
49157      * Returns the header for the specified column.
49158      * @param {Number} col The column index
49159      * @return {String}
49160      */
49161     getColumnHeader : function(col){
49162         return this.config[col].header;
49163     },
49164
49165     /**
49166      * Sets the header for a column.
49167      * @param {Number} col The column index
49168      * @param {String} header The new header
49169      */
49170     setColumnHeader : function(col, header){
49171         this.config[col].header = header;
49172         this.fireEvent("headerchange", this, col, header);
49173     },
49174
49175     /**
49176      * Returns the tooltip for the specified column.
49177      * @param {Number} col The column index
49178      * @return {String}
49179      */
49180     getColumnTooltip : function(col){
49181             return this.config[col].tooltip;
49182     },
49183     /**
49184      * Sets the tooltip for a column.
49185      * @param {Number} col The column index
49186      * @param {String} tooltip The new tooltip
49187      */
49188     setColumnTooltip : function(col, tooltip){
49189             this.config[col].tooltip = tooltip;
49190     },
49191
49192     /**
49193      * Returns the dataIndex for the specified column.
49194      * @param {Number} col The column index
49195      * @return {Number}
49196      */
49197     getDataIndex : function(col){
49198         return this.config[col].dataIndex;
49199     },
49200
49201     /**
49202      * Sets the dataIndex for a column.
49203      * @param {Number} col The column index
49204      * @param {Number} dataIndex The new dataIndex
49205      */
49206     setDataIndex : function(col, dataIndex){
49207         this.config[col].dataIndex = dataIndex;
49208     },
49209
49210     
49211     
49212     /**
49213      * Returns true if the cell is editable.
49214      * @param {Number} colIndex The column index
49215      * @param {Number} rowIndex The row index
49216      * @return {Boolean}
49217      */
49218     isCellEditable : function(colIndex, rowIndex){
49219         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49220     },
49221
49222     /**
49223      * Returns the editor defined for the cell/column.
49224      * return false or null to disable editing.
49225      * @param {Number} colIndex The column index
49226      * @param {Number} rowIndex The row index
49227      * @return {Object}
49228      */
49229     getCellEditor : function(colIndex, rowIndex){
49230         return this.config[colIndex].editor;
49231     },
49232
49233     /**
49234      * Sets if a column is editable.
49235      * @param {Number} col The column index
49236      * @param {Boolean} editable True if the column is editable
49237      */
49238     setEditable : function(col, editable){
49239         this.config[col].editable = editable;
49240     },
49241
49242
49243     /**
49244      * Returns true if the column is hidden.
49245      * @param {Number} colIndex The column index
49246      * @return {Boolean}
49247      */
49248     isHidden : function(colIndex){
49249         return this.config[colIndex].hidden;
49250     },
49251
49252
49253     /**
49254      * Returns true if the column width cannot be changed
49255      */
49256     isFixed : function(colIndex){
49257         return this.config[colIndex].fixed;
49258     },
49259
49260     /**
49261      * Returns true if the column can be resized
49262      * @return {Boolean}
49263      */
49264     isResizable : function(colIndex){
49265         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49266     },
49267     /**
49268      * Sets if a column is hidden.
49269      * @param {Number} colIndex The column index
49270      * @param {Boolean} hidden True if the column is hidden
49271      */
49272     setHidden : function(colIndex, hidden){
49273         this.config[colIndex].hidden = hidden;
49274         this.totalWidth = null;
49275         this.fireEvent("hiddenchange", this, colIndex, hidden);
49276     },
49277
49278     /**
49279      * Sets the editor for a column.
49280      * @param {Number} col The column index
49281      * @param {Object} editor The editor object
49282      */
49283     setEditor : function(col, editor){
49284         this.config[col].editor = editor;
49285     }
49286 });
49287
49288 Roo.grid.ColumnModel.defaultRenderer = function(value){
49289         if(typeof value == "string" && value.length < 1){
49290             return "&#160;";
49291         }
49292         return value;
49293 };
49294
49295 // Alias for backwards compatibility
49296 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49297 /*
49298  * Based on:
49299  * Ext JS Library 1.1.1
49300  * Copyright(c) 2006-2007, Ext JS, LLC.
49301  *
49302  * Originally Released Under LGPL - original licence link has changed is not relivant.
49303  *
49304  * Fork - LGPL
49305  * <script type="text/javascript">
49306  */
49307
49308 /**
49309  * @class Roo.grid.AbstractSelectionModel
49310  * @extends Roo.util.Observable
49311  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49312  * implemented by descendant classes.  This class should not be directly instantiated.
49313  * @constructor
49314  */
49315 Roo.grid.AbstractSelectionModel = function(){
49316     this.locked = false;
49317     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49318 };
49319
49320 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49321     /** @ignore Called by the grid automatically. Do not call directly. */
49322     init : function(grid){
49323         this.grid = grid;
49324         this.initEvents();
49325     },
49326
49327     /**
49328      * Locks the selections.
49329      */
49330     lock : function(){
49331         this.locked = true;
49332     },
49333
49334     /**
49335      * Unlocks the selections.
49336      */
49337     unlock : function(){
49338         this.locked = false;
49339     },
49340
49341     /**
49342      * Returns true if the selections are locked.
49343      * @return {Boolean}
49344      */
49345     isLocked : function(){
49346         return this.locked;
49347     }
49348 });/*
49349  * Based on:
49350  * Ext JS Library 1.1.1
49351  * Copyright(c) 2006-2007, Ext JS, LLC.
49352  *
49353  * Originally Released Under LGPL - original licence link has changed is not relivant.
49354  *
49355  * Fork - LGPL
49356  * <script type="text/javascript">
49357  */
49358 /**
49359  * @extends Roo.grid.AbstractSelectionModel
49360  * @class Roo.grid.RowSelectionModel
49361  * The default SelectionModel used by {@link Roo.grid.Grid}.
49362  * It supports multiple selections and keyboard selection/navigation. 
49363  * @constructor
49364  * @param {Object} config
49365  */
49366 Roo.grid.RowSelectionModel = function(config){
49367     Roo.apply(this, config);
49368     this.selections = new Roo.util.MixedCollection(false, function(o){
49369         return o.id;
49370     });
49371
49372     this.last = false;
49373     this.lastActive = false;
49374
49375     this.addEvents({
49376         /**
49377              * @event selectionchange
49378              * Fires when the selection changes
49379              * @param {SelectionModel} this
49380              */
49381             "selectionchange" : true,
49382         /**
49383              * @event afterselectionchange
49384              * Fires after the selection changes (eg. by key press or clicking)
49385              * @param {SelectionModel} this
49386              */
49387             "afterselectionchange" : true,
49388         /**
49389              * @event beforerowselect
49390              * Fires when a row is selected being selected, return false to cancel.
49391              * @param {SelectionModel} this
49392              * @param {Number} rowIndex The selected index
49393              * @param {Boolean} keepExisting False if other selections will be cleared
49394              */
49395             "beforerowselect" : true,
49396         /**
49397              * @event rowselect
49398              * Fires when a row is selected.
49399              * @param {SelectionModel} this
49400              * @param {Number} rowIndex The selected index
49401              * @param {Roo.data.Record} r The record
49402              */
49403             "rowselect" : true,
49404         /**
49405              * @event rowdeselect
49406              * Fires when a row is deselected.
49407              * @param {SelectionModel} this
49408              * @param {Number} rowIndex The selected index
49409              */
49410         "rowdeselect" : true
49411     });
49412     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49413     this.locked = false;
49414 };
49415
49416 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49417     /**
49418      * @cfg {Boolean} singleSelect
49419      * True to allow selection of only one row at a time (defaults to false)
49420      */
49421     singleSelect : false,
49422
49423     // private
49424     initEvents : function(){
49425
49426         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49427             this.grid.on("mousedown", this.handleMouseDown, this);
49428         }else{ // allow click to work like normal
49429             this.grid.on("rowclick", this.handleDragableRowClick, this);
49430         }
49431
49432         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49433             "up" : function(e){
49434                 if(!e.shiftKey){
49435                     this.selectPrevious(e.shiftKey);
49436                 }else if(this.last !== false && this.lastActive !== false){
49437                     var last = this.last;
49438                     this.selectRange(this.last,  this.lastActive-1);
49439                     this.grid.getView().focusRow(this.lastActive);
49440                     if(last !== false){
49441                         this.last = last;
49442                     }
49443                 }else{
49444                     this.selectFirstRow();
49445                 }
49446                 this.fireEvent("afterselectionchange", this);
49447             },
49448             "down" : function(e){
49449                 if(!e.shiftKey){
49450                     this.selectNext(e.shiftKey);
49451                 }else if(this.last !== false && this.lastActive !== false){
49452                     var last = this.last;
49453                     this.selectRange(this.last,  this.lastActive+1);
49454                     this.grid.getView().focusRow(this.lastActive);
49455                     if(last !== false){
49456                         this.last = last;
49457                     }
49458                 }else{
49459                     this.selectFirstRow();
49460                 }
49461                 this.fireEvent("afterselectionchange", this);
49462             },
49463             scope: this
49464         });
49465
49466         var view = this.grid.view;
49467         view.on("refresh", this.onRefresh, this);
49468         view.on("rowupdated", this.onRowUpdated, this);
49469         view.on("rowremoved", this.onRemove, this);
49470     },
49471
49472     // private
49473     onRefresh : function(){
49474         var ds = this.grid.dataSource, i, v = this.grid.view;
49475         var s = this.selections;
49476         s.each(function(r){
49477             if((i = ds.indexOfId(r.id)) != -1){
49478                 v.onRowSelect(i);
49479             }else{
49480                 s.remove(r);
49481             }
49482         });
49483     },
49484
49485     // private
49486     onRemove : function(v, index, r){
49487         this.selections.remove(r);
49488     },
49489
49490     // private
49491     onRowUpdated : function(v, index, r){
49492         if(this.isSelected(r)){
49493             v.onRowSelect(index);
49494         }
49495     },
49496
49497     /**
49498      * Select records.
49499      * @param {Array} records The records to select
49500      * @param {Boolean} keepExisting (optional) True to keep existing selections
49501      */
49502     selectRecords : function(records, keepExisting){
49503         if(!keepExisting){
49504             this.clearSelections();
49505         }
49506         var ds = this.grid.dataSource;
49507         for(var i = 0, len = records.length; i < len; i++){
49508             this.selectRow(ds.indexOf(records[i]), true);
49509         }
49510     },
49511
49512     /**
49513      * Gets the number of selected rows.
49514      * @return {Number}
49515      */
49516     getCount : function(){
49517         return this.selections.length;
49518     },
49519
49520     /**
49521      * Selects the first row in the grid.
49522      */
49523     selectFirstRow : function(){
49524         this.selectRow(0);
49525     },
49526
49527     /**
49528      * Select the last row.
49529      * @param {Boolean} keepExisting (optional) True to keep existing selections
49530      */
49531     selectLastRow : function(keepExisting){
49532         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49533     },
49534
49535     /**
49536      * Selects the row immediately following the last selected row.
49537      * @param {Boolean} keepExisting (optional) True to keep existing selections
49538      */
49539     selectNext : function(keepExisting){
49540         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49541             this.selectRow(this.last+1, keepExisting);
49542             this.grid.getView().focusRow(this.last);
49543         }
49544     },
49545
49546     /**
49547      * Selects the row that precedes the last selected row.
49548      * @param {Boolean} keepExisting (optional) True to keep existing selections
49549      */
49550     selectPrevious : function(keepExisting){
49551         if(this.last){
49552             this.selectRow(this.last-1, keepExisting);
49553             this.grid.getView().focusRow(this.last);
49554         }
49555     },
49556
49557     /**
49558      * Returns the selected records
49559      * @return {Array} Array of selected records
49560      */
49561     getSelections : function(){
49562         return [].concat(this.selections.items);
49563     },
49564
49565     /**
49566      * Returns the first selected record.
49567      * @return {Record}
49568      */
49569     getSelected : function(){
49570         return this.selections.itemAt(0);
49571     },
49572
49573
49574     /**
49575      * Clears all selections.
49576      */
49577     clearSelections : function(fast){
49578         if(this.locked) return;
49579         if(fast !== true){
49580             var ds = this.grid.dataSource;
49581             var s = this.selections;
49582             s.each(function(r){
49583                 this.deselectRow(ds.indexOfId(r.id));
49584             }, this);
49585             s.clear();
49586         }else{
49587             this.selections.clear();
49588         }
49589         this.last = false;
49590     },
49591
49592
49593     /**
49594      * Selects all rows.
49595      */
49596     selectAll : function(){
49597         if(this.locked) return;
49598         this.selections.clear();
49599         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49600             this.selectRow(i, true);
49601         }
49602     },
49603
49604     /**
49605      * Returns True if there is a selection.
49606      * @return {Boolean}
49607      */
49608     hasSelection : function(){
49609         return this.selections.length > 0;
49610     },
49611
49612     /**
49613      * Returns True if the specified row is selected.
49614      * @param {Number/Record} record The record or index of the record to check
49615      * @return {Boolean}
49616      */
49617     isSelected : function(index){
49618         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49619         return (r && this.selections.key(r.id) ? true : false);
49620     },
49621
49622     /**
49623      * Returns True if the specified record id is selected.
49624      * @param {String} id The id of record to check
49625      * @return {Boolean}
49626      */
49627     isIdSelected : function(id){
49628         return (this.selections.key(id) ? true : false);
49629     },
49630
49631     // private
49632     handleMouseDown : function(e, t){
49633         var view = this.grid.getView(), rowIndex;
49634         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49635             return;
49636         };
49637         if(e.shiftKey && this.last !== false){
49638             var last = this.last;
49639             this.selectRange(last, rowIndex, e.ctrlKey);
49640             this.last = last; // reset the last
49641             view.focusRow(rowIndex);
49642         }else{
49643             var isSelected = this.isSelected(rowIndex);
49644             if(e.button !== 0 && isSelected){
49645                 view.focusRow(rowIndex);
49646             }else if(e.ctrlKey && isSelected){
49647                 this.deselectRow(rowIndex);
49648             }else if(!isSelected){
49649                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49650                 view.focusRow(rowIndex);
49651             }
49652         }
49653         this.fireEvent("afterselectionchange", this);
49654     },
49655     // private
49656     handleDragableRowClick :  function(grid, rowIndex, e) 
49657     {
49658         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49659             this.selectRow(rowIndex, false);
49660             grid.view.focusRow(rowIndex);
49661              this.fireEvent("afterselectionchange", this);
49662         }
49663     },
49664     
49665     /**
49666      * Selects multiple rows.
49667      * @param {Array} rows Array of the indexes of the row to select
49668      * @param {Boolean} keepExisting (optional) True to keep existing selections
49669      */
49670     selectRows : function(rows, keepExisting){
49671         if(!keepExisting){
49672             this.clearSelections();
49673         }
49674         for(var i = 0, len = rows.length; i < len; i++){
49675             this.selectRow(rows[i], true);
49676         }
49677     },
49678
49679     /**
49680      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49681      * @param {Number} startRow The index of the first row in the range
49682      * @param {Number} endRow The index of the last row in the range
49683      * @param {Boolean} keepExisting (optional) True to retain existing selections
49684      */
49685     selectRange : function(startRow, endRow, keepExisting){
49686         if(this.locked) return;
49687         if(!keepExisting){
49688             this.clearSelections();
49689         }
49690         if(startRow <= endRow){
49691             for(var i = startRow; i <= endRow; i++){
49692                 this.selectRow(i, true);
49693             }
49694         }else{
49695             for(var i = startRow; i >= endRow; i--){
49696                 this.selectRow(i, true);
49697             }
49698         }
49699     },
49700
49701     /**
49702      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49703      * @param {Number} startRow The index of the first row in the range
49704      * @param {Number} endRow The index of the last row in the range
49705      */
49706     deselectRange : function(startRow, endRow, preventViewNotify){
49707         if(this.locked) return;
49708         for(var i = startRow; i <= endRow; i++){
49709             this.deselectRow(i, preventViewNotify);
49710         }
49711     },
49712
49713     /**
49714      * Selects a row.
49715      * @param {Number} row The index of the row to select
49716      * @param {Boolean} keepExisting (optional) True to keep existing selections
49717      */
49718     selectRow : function(index, keepExisting, preventViewNotify){
49719         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49720         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49721             if(!keepExisting || this.singleSelect){
49722                 this.clearSelections();
49723             }
49724             var r = this.grid.dataSource.getAt(index);
49725             this.selections.add(r);
49726             this.last = this.lastActive = index;
49727             if(!preventViewNotify){
49728                 this.grid.getView().onRowSelect(index);
49729             }
49730             this.fireEvent("rowselect", this, index, r);
49731             this.fireEvent("selectionchange", this);
49732         }
49733     },
49734
49735     /**
49736      * Deselects a row.
49737      * @param {Number} row The index of the row to deselect
49738      */
49739     deselectRow : function(index, preventViewNotify){
49740         if(this.locked) return;
49741         if(this.last == index){
49742             this.last = false;
49743         }
49744         if(this.lastActive == index){
49745             this.lastActive = false;
49746         }
49747         var r = this.grid.dataSource.getAt(index);
49748         this.selections.remove(r);
49749         if(!preventViewNotify){
49750             this.grid.getView().onRowDeselect(index);
49751         }
49752         this.fireEvent("rowdeselect", this, index);
49753         this.fireEvent("selectionchange", this);
49754     },
49755
49756     // private
49757     restoreLast : function(){
49758         if(this._last){
49759             this.last = this._last;
49760         }
49761     },
49762
49763     // private
49764     acceptsNav : function(row, col, cm){
49765         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49766     },
49767
49768     // private
49769     onEditorKey : function(field, e){
49770         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49771         if(k == e.TAB){
49772             e.stopEvent();
49773             ed.completeEdit();
49774             if(e.shiftKey){
49775                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49776             }else{
49777                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49778             }
49779         }else if(k == e.ENTER && !e.ctrlKey){
49780             e.stopEvent();
49781             ed.completeEdit();
49782             if(e.shiftKey){
49783                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49784             }else{
49785                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49786             }
49787         }else if(k == e.ESC){
49788             ed.cancelEdit();
49789         }
49790         if(newCell){
49791             g.startEditing(newCell[0], newCell[1]);
49792         }
49793     }
49794 });/*
49795  * Based on:
49796  * Ext JS Library 1.1.1
49797  * Copyright(c) 2006-2007, Ext JS, LLC.
49798  *
49799  * Originally Released Under LGPL - original licence link has changed is not relivant.
49800  *
49801  * Fork - LGPL
49802  * <script type="text/javascript">
49803  */
49804 /**
49805  * @class Roo.grid.CellSelectionModel
49806  * @extends Roo.grid.AbstractSelectionModel
49807  * This class provides the basic implementation for cell selection in a grid.
49808  * @constructor
49809  * @param {Object} config The object containing the configuration of this model.
49810  */
49811 Roo.grid.CellSelectionModel = function(config){
49812     Roo.apply(this, config);
49813
49814     this.selection = null;
49815
49816     this.addEvents({
49817         /**
49818              * @event beforerowselect
49819              * Fires before a cell is selected.
49820              * @param {SelectionModel} this
49821              * @param {Number} rowIndex The selected row index
49822              * @param {Number} colIndex The selected cell index
49823              */
49824             "beforecellselect" : true,
49825         /**
49826              * @event cellselect
49827              * Fires when a cell is selected.
49828              * @param {SelectionModel} this
49829              * @param {Number} rowIndex The selected row index
49830              * @param {Number} colIndex The selected cell index
49831              */
49832             "cellselect" : true,
49833         /**
49834              * @event selectionchange
49835              * Fires when the active selection changes.
49836              * @param {SelectionModel} this
49837              * @param {Object} selection null for no selection or an object (o) with two properties
49838                 <ul>
49839                 <li>o.record: the record object for the row the selection is in</li>
49840                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49841                 </ul>
49842              */
49843             "selectionchange" : true
49844     });
49845     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49846 };
49847
49848 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49849
49850     /** @ignore */
49851     initEvents : function(){
49852         this.grid.on("mousedown", this.handleMouseDown, this);
49853         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49854         var view = this.grid.view;
49855         view.on("refresh", this.onViewChange, this);
49856         view.on("rowupdated", this.onRowUpdated, this);
49857         view.on("beforerowremoved", this.clearSelections, this);
49858         view.on("beforerowsinserted", this.clearSelections, this);
49859         if(this.grid.isEditor){
49860             this.grid.on("beforeedit", this.beforeEdit,  this);
49861         }
49862     },
49863
49864         //private
49865     beforeEdit : function(e){
49866         this.select(e.row, e.column, false, true, e.record);
49867     },
49868
49869         //private
49870     onRowUpdated : function(v, index, r){
49871         if(this.selection && this.selection.record == r){
49872             v.onCellSelect(index, this.selection.cell[1]);
49873         }
49874     },
49875
49876         //private
49877     onViewChange : function(){
49878         this.clearSelections(true);
49879     },
49880
49881         /**
49882          * Returns the currently selected cell,.
49883          * @return {Array} The selected cell (row, column) or null if none selected.
49884          */
49885     getSelectedCell : function(){
49886         return this.selection ? this.selection.cell : null;
49887     },
49888
49889     /**
49890      * Clears all selections.
49891      * @param {Boolean} true to prevent the gridview from being notified about the change.
49892      */
49893     clearSelections : function(preventNotify){
49894         var s = this.selection;
49895         if(s){
49896             if(preventNotify !== true){
49897                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49898             }
49899             this.selection = null;
49900             this.fireEvent("selectionchange", this, null);
49901         }
49902     },
49903
49904     /**
49905      * Returns true if there is a selection.
49906      * @return {Boolean}
49907      */
49908     hasSelection : function(){
49909         return this.selection ? true : false;
49910     },
49911
49912     /** @ignore */
49913     handleMouseDown : function(e, t){
49914         var v = this.grid.getView();
49915         if(this.isLocked()){
49916             return;
49917         };
49918         var row = v.findRowIndex(t);
49919         var cell = v.findCellIndex(t);
49920         if(row !== false && cell !== false){
49921             this.select(row, cell);
49922         }
49923     },
49924
49925     /**
49926      * Selects a cell.
49927      * @param {Number} rowIndex
49928      * @param {Number} collIndex
49929      */
49930     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49931         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49932             this.clearSelections();
49933             r = r || this.grid.dataSource.getAt(rowIndex);
49934             this.selection = {
49935                 record : r,
49936                 cell : [rowIndex, colIndex]
49937             };
49938             if(!preventViewNotify){
49939                 var v = this.grid.getView();
49940                 v.onCellSelect(rowIndex, colIndex);
49941                 if(preventFocus !== true){
49942                     v.focusCell(rowIndex, colIndex);
49943                 }
49944             }
49945             this.fireEvent("cellselect", this, rowIndex, colIndex);
49946             this.fireEvent("selectionchange", this, this.selection);
49947         }
49948     },
49949
49950         //private
49951     isSelectable : function(rowIndex, colIndex, cm){
49952         return !cm.isHidden(colIndex);
49953     },
49954
49955     /** @ignore */
49956     handleKeyDown : function(e){
49957         Roo.log('Cell Sel Model handleKeyDown');
49958         if(!e.isNavKeyPress()){
49959             return;
49960         }
49961         var g = this.grid, s = this.selection;
49962         if(!s){
49963             e.stopEvent();
49964             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49965             if(cell){
49966                 this.select(cell[0], cell[1]);
49967             }
49968             return;
49969         }
49970         var sm = this;
49971         var walk = function(row, col, step){
49972             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49973         };
49974         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49975         var newCell;
49976
49977         switch(k){
49978             case e.TAB:
49979                 // handled by onEditorKey
49980                 if (g.isEditor && g.editing) {
49981                     return;
49982                 }
49983                 if(e.shiftKey){
49984                      newCell = walk(r, c-1, -1);
49985                 }else{
49986                      newCell = walk(r, c+1, 1);
49987                 }
49988              break;
49989              case e.DOWN:
49990                  newCell = walk(r+1, c, 1);
49991              break;
49992              case e.UP:
49993                  newCell = walk(r-1, c, -1);
49994              break;
49995              case e.RIGHT:
49996                  newCell = walk(r, c+1, 1);
49997              break;
49998              case e.LEFT:
49999                  newCell = walk(r, c-1, -1);
50000              break;
50001              case e.ENTER:
50002                  if(g.isEditor && !g.editing){
50003                     g.startEditing(r, c);
50004                     e.stopEvent();
50005                     return;
50006                 }
50007              break;
50008         };
50009         if(newCell){
50010             this.select(newCell[0], newCell[1]);
50011             e.stopEvent();
50012         }
50013     },
50014
50015     acceptsNav : function(row, col, cm){
50016         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50017     },
50018
50019     onEditorKey : function(field, e){
50020         
50021         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50022         ///Roo.log('onEditorKey' + k);
50023         
50024         if(k == e.TAB){
50025             if(e.shiftKey){
50026                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50027             }else{
50028                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50029             }
50030             e.stopEvent();
50031         }else if(k == e.ENTER && !e.ctrlKey){
50032             ed.completeEdit();
50033             e.stopEvent();
50034             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50035         }else if(k == e.ESC){
50036             ed.cancelEdit();
50037         }
50038         
50039         
50040         if(newCell){
50041             //Roo.log('next cell after edit');
50042             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50043         }
50044     }
50045 });/*
50046  * Based on:
50047  * Ext JS Library 1.1.1
50048  * Copyright(c) 2006-2007, Ext JS, LLC.
50049  *
50050  * Originally Released Under LGPL - original licence link has changed is not relivant.
50051  *
50052  * Fork - LGPL
50053  * <script type="text/javascript">
50054  */
50055  
50056 /**
50057  * @class Roo.grid.EditorGrid
50058  * @extends Roo.grid.Grid
50059  * Class for creating and editable grid.
50060  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50061  * The container MUST have some type of size defined for the grid to fill. The container will be 
50062  * automatically set to position relative if it isn't already.
50063  * @param {Object} dataSource The data model to bind to
50064  * @param {Object} colModel The column model with info about this grid's columns
50065  */
50066 Roo.grid.EditorGrid = function(container, config){
50067     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50068     this.getGridEl().addClass("xedit-grid");
50069
50070     if(!this.selModel){
50071         this.selModel = new Roo.grid.CellSelectionModel();
50072     }
50073
50074     this.activeEditor = null;
50075
50076         this.addEvents({
50077             /**
50078              * @event beforeedit
50079              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50080              * <ul style="padding:5px;padding-left:16px;">
50081              * <li>grid - This grid</li>
50082              * <li>record - The record being edited</li>
50083              * <li>field - The field name being edited</li>
50084              * <li>value - The value for the field being edited.</li>
50085              * <li>row - The grid row index</li>
50086              * <li>column - The grid column index</li>
50087              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50088              * </ul>
50089              * @param {Object} e An edit event (see above for description)
50090              */
50091             "beforeedit" : true,
50092             /**
50093              * @event afteredit
50094              * Fires after a cell is edited. <br />
50095              * <ul style="padding:5px;padding-left:16px;">
50096              * <li>grid - This grid</li>
50097              * <li>record - The record being edited</li>
50098              * <li>field - The field name being edited</li>
50099              * <li>value - The value being set</li>
50100              * <li>originalValue - The original value for the field, before the edit.</li>
50101              * <li>row - The grid row index</li>
50102              * <li>column - The grid column index</li>
50103              * </ul>
50104              * @param {Object} e An edit event (see above for description)
50105              */
50106             "afteredit" : true,
50107             /**
50108              * @event validateedit
50109              * Fires after a cell is edited, but before the value is set in the record. 
50110          * You can use this to modify the value being set in the field, Return false
50111              * to cancel the change. The edit event object has the following properties <br />
50112              * <ul style="padding:5px;padding-left:16px;">
50113          * <li>editor - This editor</li>
50114              * <li>grid - This grid</li>
50115              * <li>record - The record being edited</li>
50116              * <li>field - The field name being edited</li>
50117              * <li>value - The value being set</li>
50118              * <li>originalValue - The original value for the field, before the edit.</li>
50119              * <li>row - The grid row index</li>
50120              * <li>column - The grid column index</li>
50121              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50122              * </ul>
50123              * @param {Object} e An edit event (see above for description)
50124              */
50125             "validateedit" : true
50126         });
50127     this.on("bodyscroll", this.stopEditing,  this);
50128     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50129 };
50130
50131 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50132     /**
50133      * @cfg {Number} clicksToEdit
50134      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50135      */
50136     clicksToEdit: 2,
50137
50138     // private
50139     isEditor : true,
50140     // private
50141     trackMouseOver: false, // causes very odd FF errors
50142
50143     onCellDblClick : function(g, row, col){
50144         this.startEditing(row, col);
50145     },
50146
50147     onEditComplete : function(ed, value, startValue){
50148         this.editing = false;
50149         this.activeEditor = null;
50150         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50151         var r = ed.record;
50152         var field = this.colModel.getDataIndex(ed.col);
50153         var e = {
50154             grid: this,
50155             record: r,
50156             field: field,
50157             originalValue: startValue,
50158             value: value,
50159             row: ed.row,
50160             column: ed.col,
50161             cancel:false,
50162             editor: ed
50163         };
50164         if(String(value) !== String(startValue)){
50165             
50166             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50167                 r.set(field, e.value);
50168                 // if we are dealing with a combo box..
50169                 // then we also set the 'name' colum to be the displayField
50170                 if (ed.field.displayField && ed.field.name) {
50171                     r.set(ed.field.name, ed.field.el.dom.value);
50172                 }
50173                 
50174                 delete e.cancel; //?? why!!!
50175                 this.fireEvent("afteredit", e);
50176             }
50177         } else {
50178             this.fireEvent("afteredit", e); // always fire it!
50179         }
50180         this.view.focusCell(ed.row, ed.col);
50181     },
50182
50183     /**
50184      * Starts editing the specified for the specified row/column
50185      * @param {Number} rowIndex
50186      * @param {Number} colIndex
50187      */
50188     startEditing : function(row, col){
50189         this.stopEditing();
50190         if(this.colModel.isCellEditable(col, row)){
50191             this.view.ensureVisible(row, col, true);
50192             var r = this.dataSource.getAt(row);
50193             var field = this.colModel.getDataIndex(col);
50194             var e = {
50195                 grid: this,
50196                 record: r,
50197                 field: field,
50198                 value: r.data[field],
50199                 row: row,
50200                 column: col,
50201                 cancel:false
50202             };
50203             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50204                 this.editing = true;
50205                 var ed = this.colModel.getCellEditor(col, row);
50206                 
50207                 if (!ed) {
50208                     return;
50209                 }
50210                 if(!ed.rendered){
50211                     ed.render(ed.parentEl || document.body);
50212                 }
50213                 ed.field.reset();
50214                 (function(){ // complex but required for focus issues in safari, ie and opera
50215                     ed.row = row;
50216                     ed.col = col;
50217                     ed.record = r;
50218                     ed.on("complete", this.onEditComplete, this, {single: true});
50219                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50220                     this.activeEditor = ed;
50221                     var v = r.data[field];
50222                     ed.startEdit(this.view.getCell(row, col), v);
50223                     // combo's with 'displayField and name set
50224                     if (ed.field.displayField && ed.field.name) {
50225                         ed.field.el.dom.value = r.data[ed.field.name];
50226                     }
50227                     
50228                     
50229                 }).defer(50, this);
50230             }
50231         }
50232     },
50233         
50234     /**
50235      * Stops any active editing
50236      */
50237     stopEditing : function(){
50238         if(this.activeEditor){
50239             this.activeEditor.completeEdit();
50240         }
50241         this.activeEditor = null;
50242     }
50243 });/*
50244  * Based on:
50245  * Ext JS Library 1.1.1
50246  * Copyright(c) 2006-2007, Ext JS, LLC.
50247  *
50248  * Originally Released Under LGPL - original licence link has changed is not relivant.
50249  *
50250  * Fork - LGPL
50251  * <script type="text/javascript">
50252  */
50253
50254 // private - not really -- you end up using it !
50255 // This is a support class used internally by the Grid components
50256
50257 /**
50258  * @class Roo.grid.GridEditor
50259  * @extends Roo.Editor
50260  * Class for creating and editable grid elements.
50261  * @param {Object} config any settings (must include field)
50262  */
50263 Roo.grid.GridEditor = function(field, config){
50264     if (!config && field.field) {
50265         config = field;
50266         field = Roo.factory(config.field, Roo.form);
50267     }
50268     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50269     field.monitorTab = false;
50270 };
50271
50272 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50273     
50274     /**
50275      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50276      */
50277     
50278     alignment: "tl-tl",
50279     autoSize: "width",
50280     hideEl : false,
50281     cls: "x-small-editor x-grid-editor",
50282     shim:false,
50283     shadow:"frame"
50284 });/*
50285  * Based on:
50286  * Ext JS Library 1.1.1
50287  * Copyright(c) 2006-2007, Ext JS, LLC.
50288  *
50289  * Originally Released Under LGPL - original licence link has changed is not relivant.
50290  *
50291  * Fork - LGPL
50292  * <script type="text/javascript">
50293  */
50294   
50295
50296   
50297 Roo.grid.PropertyRecord = Roo.data.Record.create([
50298     {name:'name',type:'string'},  'value'
50299 ]);
50300
50301
50302 Roo.grid.PropertyStore = function(grid, source){
50303     this.grid = grid;
50304     this.store = new Roo.data.Store({
50305         recordType : Roo.grid.PropertyRecord
50306     });
50307     this.store.on('update', this.onUpdate,  this);
50308     if(source){
50309         this.setSource(source);
50310     }
50311     Roo.grid.PropertyStore.superclass.constructor.call(this);
50312 };
50313
50314
50315
50316 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50317     setSource : function(o){
50318         this.source = o;
50319         this.store.removeAll();
50320         var data = [];
50321         for(var k in o){
50322             if(this.isEditableValue(o[k])){
50323                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50324             }
50325         }
50326         this.store.loadRecords({records: data}, {}, true);
50327     },
50328
50329     onUpdate : function(ds, record, type){
50330         if(type == Roo.data.Record.EDIT){
50331             var v = record.data['value'];
50332             var oldValue = record.modified['value'];
50333             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50334                 this.source[record.id] = v;
50335                 record.commit();
50336                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50337             }else{
50338                 record.reject();
50339             }
50340         }
50341     },
50342
50343     getProperty : function(row){
50344        return this.store.getAt(row);
50345     },
50346
50347     isEditableValue: function(val){
50348         if(val && val instanceof Date){
50349             return true;
50350         }else if(typeof val == 'object' || typeof val == 'function'){
50351             return false;
50352         }
50353         return true;
50354     },
50355
50356     setValue : function(prop, value){
50357         this.source[prop] = value;
50358         this.store.getById(prop).set('value', value);
50359     },
50360
50361     getSource : function(){
50362         return this.source;
50363     }
50364 });
50365
50366 Roo.grid.PropertyColumnModel = function(grid, store){
50367     this.grid = grid;
50368     var g = Roo.grid;
50369     g.PropertyColumnModel.superclass.constructor.call(this, [
50370         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50371         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50372     ]);
50373     this.store = store;
50374     this.bselect = Roo.DomHelper.append(document.body, {
50375         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50376             {tag: 'option', value: 'true', html: 'true'},
50377             {tag: 'option', value: 'false', html: 'false'}
50378         ]
50379     });
50380     Roo.id(this.bselect);
50381     var f = Roo.form;
50382     this.editors = {
50383         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50384         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50385         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50386         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50387         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50388     };
50389     this.renderCellDelegate = this.renderCell.createDelegate(this);
50390     this.renderPropDelegate = this.renderProp.createDelegate(this);
50391 };
50392
50393 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50394     
50395     
50396     nameText : 'Name',
50397     valueText : 'Value',
50398     
50399     dateFormat : 'm/j/Y',
50400     
50401     
50402     renderDate : function(dateVal){
50403         return dateVal.dateFormat(this.dateFormat);
50404     },
50405
50406     renderBool : function(bVal){
50407         return bVal ? 'true' : 'false';
50408     },
50409
50410     isCellEditable : function(colIndex, rowIndex){
50411         return colIndex == 1;
50412     },
50413
50414     getRenderer : function(col){
50415         return col == 1 ?
50416             this.renderCellDelegate : this.renderPropDelegate;
50417     },
50418
50419     renderProp : function(v){
50420         return this.getPropertyName(v);
50421     },
50422
50423     renderCell : function(val){
50424         var rv = val;
50425         if(val instanceof Date){
50426             rv = this.renderDate(val);
50427         }else if(typeof val == 'boolean'){
50428             rv = this.renderBool(val);
50429         }
50430         return Roo.util.Format.htmlEncode(rv);
50431     },
50432
50433     getPropertyName : function(name){
50434         var pn = this.grid.propertyNames;
50435         return pn && pn[name] ? pn[name] : name;
50436     },
50437
50438     getCellEditor : function(colIndex, rowIndex){
50439         var p = this.store.getProperty(rowIndex);
50440         var n = p.data['name'], val = p.data['value'];
50441         
50442         if(typeof(this.grid.customEditors[n]) == 'string'){
50443             return this.editors[this.grid.customEditors[n]];
50444         }
50445         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50446             return this.grid.customEditors[n];
50447         }
50448         if(val instanceof Date){
50449             return this.editors['date'];
50450         }else if(typeof val == 'number'){
50451             return this.editors['number'];
50452         }else if(typeof val == 'boolean'){
50453             return this.editors['boolean'];
50454         }else{
50455             return this.editors['string'];
50456         }
50457     }
50458 });
50459
50460 /**
50461  * @class Roo.grid.PropertyGrid
50462  * @extends Roo.grid.EditorGrid
50463  * This class represents the  interface of a component based property grid control.
50464  * <br><br>Usage:<pre><code>
50465  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50466       
50467  });
50468  // set any options
50469  grid.render();
50470  * </code></pre>
50471   
50472  * @constructor
50473  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50474  * The container MUST have some type of size defined for the grid to fill. The container will be
50475  * automatically set to position relative if it isn't already.
50476  * @param {Object} config A config object that sets properties on this grid.
50477  */
50478 Roo.grid.PropertyGrid = function(container, config){
50479     config = config || {};
50480     var store = new Roo.grid.PropertyStore(this);
50481     this.store = store;
50482     var cm = new Roo.grid.PropertyColumnModel(this, store);
50483     store.store.sort('name', 'ASC');
50484     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50485         ds: store.store,
50486         cm: cm,
50487         enableColLock:false,
50488         enableColumnMove:false,
50489         stripeRows:false,
50490         trackMouseOver: false,
50491         clicksToEdit:1
50492     }, config));
50493     this.getGridEl().addClass('x-props-grid');
50494     this.lastEditRow = null;
50495     this.on('columnresize', this.onColumnResize, this);
50496     this.addEvents({
50497          /**
50498              * @event beforepropertychange
50499              * Fires before a property changes (return false to stop?)
50500              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50501              * @param {String} id Record Id
50502              * @param {String} newval New Value
50503          * @param {String} oldval Old Value
50504              */
50505         "beforepropertychange": true,
50506         /**
50507              * @event propertychange
50508              * Fires after a property changes
50509              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50510              * @param {String} id Record Id
50511              * @param {String} newval New Value
50512          * @param {String} oldval Old Value
50513              */
50514         "propertychange": true
50515     });
50516     this.customEditors = this.customEditors || {};
50517 };
50518 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50519     
50520      /**
50521      * @cfg {Object} customEditors map of colnames=> custom editors.
50522      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50523      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50524      * false disables editing of the field.
50525          */
50526     
50527       /**
50528      * @cfg {Object} propertyNames map of property Names to their displayed value
50529          */
50530     
50531     render : function(){
50532         Roo.grid.PropertyGrid.superclass.render.call(this);
50533         this.autoSize.defer(100, this);
50534     },
50535
50536     autoSize : function(){
50537         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50538         if(this.view){
50539             this.view.fitColumns();
50540         }
50541     },
50542
50543     onColumnResize : function(){
50544         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50545         this.autoSize();
50546     },
50547     /**
50548      * Sets the data for the Grid
50549      * accepts a Key => Value object of all the elements avaiable.
50550      * @param {Object} data  to appear in grid.
50551      */
50552     setSource : function(source){
50553         this.store.setSource(source);
50554         //this.autoSize();
50555     },
50556     /**
50557      * Gets all the data from the grid.
50558      * @return {Object} data  data stored in grid
50559      */
50560     getSource : function(){
50561         return this.store.getSource();
50562     }
50563 });/*
50564  * Based on:
50565  * Ext JS Library 1.1.1
50566  * Copyright(c) 2006-2007, Ext JS, LLC.
50567  *
50568  * Originally Released Under LGPL - original licence link has changed is not relivant.
50569  *
50570  * Fork - LGPL
50571  * <script type="text/javascript">
50572  */
50573  
50574 /**
50575  * @class Roo.LoadMask
50576  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50577  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50578  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50579  * element's UpdateManager load indicator and will be destroyed after the initial load.
50580  * @constructor
50581  * Create a new LoadMask
50582  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50583  * @param {Object} config The config object
50584  */
50585 Roo.LoadMask = function(el, config){
50586     this.el = Roo.get(el);
50587     Roo.apply(this, config);
50588     if(this.store){
50589         this.store.on('beforeload', this.onBeforeLoad, this);
50590         this.store.on('load', this.onLoad, this);
50591         this.store.on('loadexception', this.onLoad, this);
50592         this.removeMask = false;
50593     }else{
50594         var um = this.el.getUpdateManager();
50595         um.showLoadIndicator = false; // disable the default indicator
50596         um.on('beforeupdate', this.onBeforeLoad, this);
50597         um.on('update', this.onLoad, this);
50598         um.on('failure', this.onLoad, this);
50599         this.removeMask = true;
50600     }
50601 };
50602
50603 Roo.LoadMask.prototype = {
50604     /**
50605      * @cfg {Boolean} removeMask
50606      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50607      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50608      */
50609     /**
50610      * @cfg {String} msg
50611      * The text to display in a centered loading message box (defaults to 'Loading...')
50612      */
50613     msg : 'Loading...',
50614     /**
50615      * @cfg {String} msgCls
50616      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50617      */
50618     msgCls : 'x-mask-loading',
50619
50620     /**
50621      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50622      * @type Boolean
50623      */
50624     disabled: false,
50625
50626     /**
50627      * Disables the mask to prevent it from being displayed
50628      */
50629     disable : function(){
50630        this.disabled = true;
50631     },
50632
50633     /**
50634      * Enables the mask so that it can be displayed
50635      */
50636     enable : function(){
50637         this.disabled = false;
50638     },
50639
50640     // private
50641     onLoad : function(){
50642         this.el.unmask(this.removeMask);
50643     },
50644
50645     // private
50646     onBeforeLoad : function(){
50647         if(!this.disabled){
50648             this.el.mask(this.msg, this.msgCls);
50649         }
50650     },
50651
50652     // private
50653     destroy : function(){
50654         if(this.store){
50655             this.store.un('beforeload', this.onBeforeLoad, this);
50656             this.store.un('load', this.onLoad, this);
50657             this.store.un('loadexception', this.onLoad, this);
50658         }else{
50659             var um = this.el.getUpdateManager();
50660             um.un('beforeupdate', this.onBeforeLoad, this);
50661             um.un('update', this.onLoad, this);
50662             um.un('failure', this.onLoad, this);
50663         }
50664     }
50665 };/*
50666  * Based on:
50667  * Ext JS Library 1.1.1
50668  * Copyright(c) 2006-2007, Ext JS, LLC.
50669  *
50670  * Originally Released Under LGPL - original licence link has changed is not relivant.
50671  *
50672  * Fork - LGPL
50673  * <script type="text/javascript">
50674  */
50675 Roo.XTemplate = function(){
50676     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50677     var s = this.html;
50678
50679     s = ['<tpl>', s, '</tpl>'].join('');
50680
50681     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50682
50683     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50684     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50685     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50686     var m, id = 0;
50687     var tpls = [];
50688
50689     while(m = s.match(re)){
50690        var m2 = m[0].match(nameRe);
50691        var m3 = m[0].match(ifRe);
50692        var m4 = m[0].match(execRe);
50693        var exp = null, fn = null, exec = null;
50694        var name = m2 && m2[1] ? m2[1] : '';
50695        if(m3){
50696            exp = m3 && m3[1] ? m3[1] : null;
50697            if(exp){
50698                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50699            }
50700        }
50701        if(m4){
50702            exp = m4 && m4[1] ? m4[1] : null;
50703            if(exp){
50704                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50705            }
50706        }
50707        if(name){
50708            switch(name){
50709                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50710                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50711                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50712            }
50713        }
50714        tpls.push({
50715             id: id,
50716             target: name,
50717             exec: exec,
50718             test: fn,
50719             body: m[1]||''
50720         });
50721        s = s.replace(m[0], '{xtpl'+ id + '}');
50722        ++id;
50723     }
50724     for(var i = tpls.length-1; i >= 0; --i){
50725         this.compileTpl(tpls[i]);
50726     }
50727     this.master = tpls[tpls.length-1];
50728     this.tpls = tpls;
50729 };
50730 Roo.extend(Roo.XTemplate, Roo.Template, {
50731
50732     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50733
50734     applySubTemplate : function(id, values, parent){
50735         var t = this.tpls[id];
50736         if(t.test && !t.test.call(this, values, parent)){
50737             return '';
50738         }
50739         if(t.exec && t.exec.call(this, values, parent)){
50740             return '';
50741         }
50742         var vs = t.target ? t.target.call(this, values, parent) : values;
50743         parent = t.target ? values : parent;
50744         if(t.target && vs instanceof Array){
50745             var buf = [];
50746             for(var i = 0, len = vs.length; i < len; i++){
50747                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50748             }
50749             return buf.join('');
50750         }
50751         return t.compiled.call(this, vs, parent);
50752     },
50753
50754     compileTpl : function(tpl){
50755         var fm = Roo.util.Format;
50756         var useF = this.disableFormats !== true;
50757         var sep = Roo.isGecko ? "+" : ",";
50758         var fn = function(m, name, format, args){
50759             if(name.substr(0, 4) == 'xtpl'){
50760                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50761             }
50762             var v;
50763             if(name.indexOf('.') != -1){
50764                 v = name;
50765             }else{
50766                 v = "values['" + name + "']";
50767             }
50768             if(format && useF){
50769                 args = args ? ',' + args : "";
50770                 if(format.substr(0, 5) != "this."){
50771                     format = "fm." + format + '(';
50772                 }else{
50773                     format = 'this.call("'+ format.substr(5) + '", ';
50774                     args = ", values";
50775                 }
50776             }else{
50777                 args= ''; format = "("+v+" === undefined ? '' : ";
50778             }
50779             return "'"+ sep + format + v + args + ")"+sep+"'";
50780         };
50781         var body;
50782         // branched to use + in gecko and [].join() in others
50783         if(Roo.isGecko){
50784             body = "tpl.compiled = function(values, parent){ return '" +
50785                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50786                     "';};";
50787         }else{
50788             body = ["tpl.compiled = function(values, parent){ return ['"];
50789             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50790             body.push("'].join('');};");
50791             body = body.join('');
50792         }
50793         /** eval:var:zzzzzzz */
50794         eval(body);
50795         return this;
50796     },
50797
50798     applyTemplate : function(values){
50799         return this.master.compiled.call(this, values, {});
50800         var s = this.subs;
50801     },
50802
50803     apply : function(){
50804         return this.applyTemplate.apply(this, arguments);
50805     },
50806
50807     compile : function(){return this;}
50808 });
50809
50810 Roo.XTemplate.from = function(el){
50811     el = Roo.getDom(el);
50812     return new Roo.XTemplate(el.value || el.innerHTML);
50813 };/*
50814  * Original code for Roojs - LGPL
50815  * <script type="text/javascript">
50816  */
50817  
50818 /**
50819  * @class Roo.XComponent
50820  * A delayed Element creator...
50821  * 
50822  * Mypart.xyx = new Roo.XComponent({
50823
50824     parent : 'Mypart.xyz', // empty == document.element.!!
50825     order : '001',
50826     name : 'xxxx'
50827     region : 'xxxx'
50828     disabled : function() {} 
50829      
50830     tree : function() { // return an tree of xtype declared components
50831         var MODULE = this;
50832         return 
50833         {
50834             xtype : 'NestedLayoutPanel',
50835             // technicall
50836         }
50837      ]
50838  *})
50839  * @extends Roo.util.Observable
50840  * @constructor
50841  * @param cfg {Object} configuration of component
50842  * 
50843  */
50844 Roo.XComponent = function(cfg) {
50845     Roo.apply(this, cfg);
50846     this.addEvents({ 
50847         /**
50848              * @event built
50849              * Fires when this the componnt is built
50850              * @param {Roo.XComponent} c the component
50851              */
50852         'built' : true,
50853         /**
50854              * @event buildcomplete
50855              * Fires on the top level element when all elements have been built
50856              * @param {Roo.XComponent} c the top level component.
50857          */
50858         'buildcomplete' : true
50859         
50860     });
50861     
50862     Roo.XComponent.register(this);
50863     this.modules = false;
50864     this.el = false; // where the layout goes..
50865     
50866     
50867 }
50868 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50869     /**
50870      * @property el
50871      * The created element (with Roo.factory())
50872      * @type {Roo.Layout}
50873      */
50874     el  : false,
50875     
50876     /**
50877      * @property el
50878      * for BC  - use el in new code
50879      * @type {Roo.Layout}
50880      */
50881     panel : false,
50882     
50883     /**
50884      * @property layout
50885      * for BC  - use el in new code
50886      * @type {Roo.Layout}
50887      */
50888     layout : false,
50889     
50890      /**
50891      * @cfg {Function|boolean} disabled
50892      * If this module is disabled by some rule, return true from the funtion
50893      */
50894     disabled : false,
50895     
50896     /**
50897      * @cfg {String} parent 
50898      * Name of parent element which it get xtype added to..
50899      */
50900     parent: false,
50901     
50902     /**
50903      * @cfg {String} order
50904      * Used to set the order in which elements are created (usefull for multiple tabs)
50905      */
50906     
50907     order : false,
50908     /**
50909      * @cfg {String} name
50910      * String to display while loading.
50911      */
50912     name : false,
50913     /**
50914      * @cfg {Array} items
50915      * A single item array - the first element is the root of the tree..
50916      * It's done this way to stay compatible with the Xtype system...
50917      */
50918     items : false
50919      
50920      
50921     
50922 });
50923
50924 Roo.apply(Roo.XComponent, {
50925     
50926     /**
50927      * @property  buildCompleted
50928      * True when the builder has completed building the interface.
50929      * @type Boolean
50930      */
50931     buildCompleted : false,
50932      
50933     /**
50934      * @property  topModule
50935      * the upper most module - uses document.element as it's constructor.
50936      * @type Object
50937      */
50938      
50939     topModule  : false,
50940       
50941     /**
50942      * @property  modules
50943      * array of modules to be created by registration system.
50944      * @type Roo.XComponent
50945      */
50946     
50947     modules : [],
50948       
50949     
50950     /**
50951      * Register components to be built later.
50952      *
50953      * This solves the following issues
50954      * - Building is not done on page load, but after an authentication process has occured.
50955      * - Interface elements are registered on page load
50956      * - Parent Interface elements may not be loaded before child, so this handles that..
50957      * 
50958      *
50959      * example:
50960      * 
50961      * MyApp.register({
50962           order : '000001',
50963           module : 'Pman.Tab.projectMgr',
50964           region : 'center',
50965           parent : 'Pman.layout',
50966           disabled : false,  // or use a function..
50967         })
50968      
50969      * * @param {Object} details about module
50970      */
50971     register : function(obj) {
50972         this.modules.push(obj);
50973          
50974     },
50975     /**
50976      * convert a string to an object..
50977      * 
50978      */
50979     
50980     toObject : function(str)
50981     {
50982         if (!str || typeof(str) == 'object') {
50983             return str;
50984         }
50985         var ar = str.split('.');
50986         var rt, o;
50987         rt = ar.shift();
50988             /** eval:var:o */
50989         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50990         if (o === false) {
50991             throw "Module not found : " + str;
50992         }
50993         Roo.each(ar, function(e) {
50994             if (typeof(o[e]) == 'undefined') {
50995                 throw "Module not found : " + str;
50996             }
50997             o = o[e];
50998         });
50999         return o;
51000         
51001     },
51002     
51003     
51004     /**
51005      * move modules into their correct place in the tree..
51006      * 
51007      */
51008     preBuild : function ()
51009     {
51010         
51011         Roo.each(this.modules , function (obj)
51012         {
51013             obj.parent = this.toObject(obj.parent);
51014             
51015             if (!obj.parent) {
51016                 this.topModule = obj;
51017                 return;
51018             }
51019             
51020             if (!obj.parent.modules) {
51021                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51022                     function(o) { return o.order + '' }
51023                 );
51024             }
51025             
51026             obj.parent.modules.add(obj);
51027         }, this);
51028     },
51029     
51030      /**
51031      * make a list of modules to build.
51032      * @return {Array} list of modules. 
51033      */ 
51034     
51035     buildOrder : function()
51036     {
51037         var _this = this;
51038         var cmp = function(a,b) {   
51039             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51040         };
51041         
51042         if (!this.topModule || !this.topModule.modules) {
51043             throw "No top level modules to build";
51044         }
51045        
51046         // make a flat list in order of modules to build.
51047         var mods = [ this.topModule ];
51048         
51049         
51050         // add modules to their parents..
51051         var addMod = function(m) {
51052            // Roo.debug && Roo.log(m.modKey);
51053             
51054             mods.push(m);
51055             if (m.modules) {
51056                 m.modules.keySort('ASC',  cmp );
51057                 m.modules.each(addMod);
51058             }
51059             // not sure if this is used any more..
51060             if (m.finalize) {
51061                 m.finalize.name = m.name + " (clean up) ";
51062                 mods.push(m.finalize);
51063             }
51064             
51065         }
51066         this.topModule.modules.keySort('ASC',  cmp );
51067         this.topModule.modules.each(addMod);
51068         return mods;
51069     },
51070     
51071      /**
51072      * Build the registered modules.
51073      * @param {Object} parent element.
51074      * @param {Function} optional method to call after module has been added.
51075      * 
51076      */ 
51077    
51078     build : function() 
51079     {
51080         
51081         this.preBuild();
51082         var mods = this.buildOrder();
51083       
51084         //this.allmods = mods;
51085         //Roo.debug && Roo.log(mods);
51086         //return;
51087         if (!mods.length) { // should not happen
51088             throw "NO modules!!!";
51089         }
51090         
51091         
51092         
51093         // flash it up as modal - so we store the mask!?
51094         Roo.MessageBox.show({ title: 'loading' });
51095         Roo.MessageBox.show({
51096            title: "Please wait...",
51097            msg: "Building Interface...",
51098            width:450,
51099            progress:true,
51100            closable:false,
51101            modal: false
51102           
51103         });
51104         var total = mods.length;
51105         
51106         var _this = this;
51107         var progressRun = function() {
51108             if (!mods.length) {
51109                 Roo.debug && Roo.log('hide?');
51110                 Roo.MessageBox.hide();
51111                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
51112                 return;    
51113             }
51114             
51115             var m = mods.shift();
51116             Roo.debug && Roo.log(m);
51117             if (typeof(m) == 'function') { // not sure if this is supported any more..
51118                 m.call(this);
51119                 return progressRun.defer(10, _this);
51120             } 
51121             
51122             Roo.MessageBox.updateProgress(
51123                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51124                     " of " + total + 
51125                     (m.name ? (' - ' + m.name) : '')
51126                     );
51127             
51128          
51129             
51130             var disabled = (typeof(m.disabled) == 'function') ?
51131                 m.disabled.call(m.module.disabled) : m.disabled;    
51132             
51133             
51134             if (disabled) {
51135                 return progressRun(); // we do not update the display!
51136             }
51137             
51138             if (!m.parent) {
51139                 // it's a top level one..
51140                 var layoutbase = new Ext.BorderLayout(document.body, {
51141                
51142                     center: {
51143                          titlebar: false,
51144                          autoScroll:false,
51145                          closeOnTab: true,
51146                          tabPosition: 'top',
51147                          //resizeTabs: true,
51148                          alwaysShowTabs: true,
51149                          minTabWidth: 140
51150                     }
51151                 });
51152                 var tree = m.tree();
51153                 tree.region = 'center';
51154                 m.el = layoutbase.addxtype(tree);
51155                 m.panel = m.el;
51156                 m.layout = m.panel.layout;    
51157                 return progressRun.defer(10, _this);
51158             }
51159             
51160             var tree = m.tree();
51161             tree.region = tree.region || m.region;
51162             m.el = m.parent.el.addxtype(tree);
51163             m.fireEvent('built', m);
51164             m.panel = m.el;
51165             m.layout = m.panel.layout;    
51166             progressRun.defer(10, _this); 
51167             
51168         }
51169         progressRun.defer(1, _this);
51170      
51171         
51172         
51173     }
51174      
51175    
51176     
51177     
51178 });
51179  //<script type="text/javascript">
51180
51181
51182 /**
51183  * @class Roo.Login
51184  * @extends Roo.LayoutDialog
51185  * A generic Login Dialog..... - only one needed in theory!?!?
51186  *
51187  * Fires XComponent builder on success...
51188  * 
51189  * Sends 
51190  *    username,password, lang = for login actions.
51191  *    check = 1 for periodic checking that sesion is valid.
51192  *    passwordRequest = email request password
51193  *    logout = 1 = to logout
51194  * 
51195  * Affects: (this id="????" elements)
51196  *   loading  (removed) (used to indicate application is loading)
51197  *   loading-mask (hides) (used to hide application when it's building loading)
51198  *   
51199  * 
51200  * Usage: 
51201  *    
51202  * 
51203  * Myapp.login = Roo.Login({
51204      url: xxxx,
51205    
51206      realm : 'Myapp', 
51207      
51208      
51209      method : 'POST',
51210      
51211      
51212      * 
51213  })
51214  * 
51215  * 
51216  * 
51217  **/
51218  
51219 Roo.Login = function(cfg)
51220 {
51221     this.addEvents({
51222         'refreshed' : true
51223     });
51224     
51225     Roo.apply(this,cfg);
51226     
51227     Roo.onReady(function() {
51228         this.onLoad();
51229     }, this);
51230     // call parent..
51231     
51232    
51233     Roo.Login.superclass.constructor.call(this, this);
51234     //this.addxtype(this.items[0]);
51235     
51236     
51237 }
51238
51239
51240 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51241     
51242     /**
51243      * @cfg {String} method
51244      * Method used to query for login details.
51245      */
51246     
51247     method : 'POST',
51248     /**
51249      * @cfg {String} url
51250      * URL to query login data. - eg. baseURL + '/Login.php'
51251      */
51252     url : '',
51253     
51254     /**
51255      * @property user
51256      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51257      * @type {Object} 
51258      */
51259     user : false,
51260     /**
51261      * @property checkFails
51262      * Number of times we have attempted to get authentication check, and failed.
51263      * @type {Number} 
51264      */
51265     checkFails : 0,
51266       /**
51267      * @property intervalID
51268      * The window interval that does the constant login checking.
51269      * @type {Number} 
51270      */
51271     intervalID : 0,
51272     
51273     
51274     onLoad : function() // called on page load...
51275     {
51276         // load 
51277          
51278         if (Roo.get('loading')) { // clear any loading indicator..
51279             Roo.get('loading').remove();
51280         }
51281         
51282         //this.switchLang('en'); // set the language to english..
51283        
51284         this.check({
51285             success:  function(response, opts)  {  // check successfull...
51286             
51287                 var res = this.processResponse(response);
51288                 this.checkFails =0;
51289                 if (!res.success) { // error!
51290                     this.checkFails = 5;
51291                     //console.log('call failure');
51292                     return this.failure(response,opts);
51293                 }
51294                 
51295                 if (!res.data.id) { // id=0 == login failure.
51296                     return this.show();
51297                 }
51298                 
51299                               
51300                         //console.log(success);
51301                 this.fillAuth(res.data);   
51302                 this.checkFails =0;
51303                 Roo.XComponent.build();
51304             },
51305             failure : this.show
51306         });
51307         
51308     }, 
51309     
51310     
51311     check: function(cfg) // called every so often to refresh cookie etc..
51312     {
51313         if (cfg.again) { // could be undefined..
51314             this.checkFails++;
51315         } else {
51316             this.checkFails = 0;
51317         }
51318         var _this = this;
51319         if (this.sending) {
51320             if ( this.checkFails > 4) {
51321                 Roo.MessageBox.alert("Error",  
51322                     "Error getting authentication status. - try reloading, or wait a while", function() {
51323                         _this.sending = false;
51324                     }); 
51325                 return;
51326             }
51327             cfg.again = true;
51328             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51329             return;
51330         }
51331         this.sending = true;
51332         
51333         Roo.Ajax.request({  
51334             url: this.url,
51335             params: {
51336                 getAuthUser: true
51337             },  
51338             method: this.method,
51339             success:  cfg.success || this.success,
51340             failure : cfg.failure || this.failure,
51341             scope : this,
51342             callCfg : cfg
51343               
51344         });  
51345     }, 
51346     
51347     
51348     logout: function()
51349     {
51350         window.onbeforeunload = function() { }; // false does not work for IE..
51351         this.user = false;
51352         var _this = this;
51353         
51354         Roo.Ajax.request({  
51355             url: this.url,
51356             params: {
51357                 logout: 1
51358             },  
51359             method: 'GET',
51360             failure : function() {
51361                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51362                     document.location = document.location.toString() + '?ts=' + Math.random();
51363                 });
51364                 
51365             },
51366             success : function() {
51367                 _this.user = false;
51368                 this.checkFails =0;
51369                 // fixme..
51370                 document.location = document.location.toString() + '?ts=' + Math.random();
51371             }
51372               
51373               
51374         }); 
51375     },
51376     
51377     processResponse : function (response)
51378     {
51379         var res = '';
51380         try {
51381             res = Roo.decode(response.responseText);
51382             // oops...
51383             if (typeof(res) != 'object') {
51384                 res = { success : false, errorMsg : res, errors : true };
51385             }
51386             if (typeof(res.success) == 'undefined') {
51387                 res.success = false;
51388             }
51389             
51390         } catch(e) {
51391             res = { success : false,  errorMsg : response.responseText, errors : true };
51392         }
51393         return res;
51394     },
51395     
51396     success : function(response, opts)  // check successfull...
51397     {  
51398         this.sending = false;
51399         var res = this.processResponse(response);
51400         if (!res.success) {
51401             return this.failure(response, opts);
51402         }
51403         if (!res.data || !res.data.id) {
51404             return this.failure(response,opts);
51405         }
51406         //console.log(res);
51407         this.fillAuth(res.data);
51408         
51409         this.checkFails =0;
51410         
51411     },
51412     
51413     
51414     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51415     {
51416         this.authUser = -1;
51417         this.sending = false;
51418         var res = this.processResponse(response);
51419         //console.log(res);
51420         if ( this.checkFails > 2) {
51421         
51422             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51423                 "Error getting authentication status. - try reloading"); 
51424             return;
51425         }
51426         opts.callCfg.again = true;
51427         this.check.defer(1000, this, [ opts.callCfg ]);
51428         return;  
51429     },
51430     
51431     
51432     
51433     fillAuth: function(au) {
51434         this.startAuthCheck();
51435         this.authUserId = au.id;
51436         this.authUser = au;
51437         this.lastChecked = new Date();
51438         this.fireEvent('refreshed', au);
51439         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51440         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51441         au.lang = au.lang || 'en';
51442         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51443         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51444         this.switchLang(au.lang );
51445         
51446      
51447         // open system... - -on setyp..
51448         if (this.authUserId  < 0) {
51449             Roo.MessageBox.alert("Warning", 
51450                 "This is an open system - please set up a admin user with a password.");  
51451         }
51452          
51453         //Pman.onload(); // which should do nothing if it's a re-auth result...
51454         
51455              
51456     },
51457     
51458     startAuthCheck : function() // starter for timeout checking..
51459     {
51460         if (this.intervalID) { // timer already in place...
51461             return false;
51462         }
51463         var _this = this;
51464         this.intervalID =  window.setInterval(function() {
51465               _this.check(false);
51466             }, 120000); // every 120 secs = 2mins..
51467         
51468         
51469     },
51470          
51471     
51472     switchLang : function (lang) 
51473     {
51474         _T = typeof(_T) == 'undefined' ? false : _T;
51475           if (!_T || !lang.length) {
51476             return;
51477         }
51478         
51479         if (!_T && lang != 'en') {
51480             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51481             return;
51482         }
51483         
51484         if (typeof(_T.en) == 'undefined') {
51485             _T.en = {};
51486             Roo.apply(_T.en, _T);
51487         }
51488         
51489         if (typeof(_T[lang]) == 'undefined') {
51490             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51491             return;
51492         }
51493         
51494         
51495         Roo.apply(_T, _T[lang]);
51496         // just need to set the text values for everything...
51497         var _this = this;
51498         /* this will not work ...
51499         if (this.form) { 
51500             
51501                
51502             function formLabel(name, val) {
51503                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51504             }
51505             
51506             formLabel('password', "Password"+':');
51507             formLabel('username', "Email Address"+':');
51508             formLabel('lang', "Language"+':');
51509             this.dialog.setTitle("Login");
51510             this.dialog.buttons[0].setText("Forgot Password");
51511             this.dialog.buttons[1].setText("Login");
51512         }
51513         */
51514         
51515         
51516     },
51517     
51518     
51519     title: "Login",
51520     modal: true,
51521     width:  350,
51522     //height: 230,
51523     height: 180,
51524     shadow: true,
51525     minWidth:200,
51526     minHeight:180,
51527     //proxyDrag: true,
51528     closable: false,
51529     draggable: false,
51530     collapsible: false,
51531     resizable: false,
51532     center: {  // needed??
51533         autoScroll:false,
51534         titlebar: false,
51535        // tabPosition: 'top',
51536         hideTabs: true,
51537         closeOnTab: true,
51538         alwaysShowTabs: false
51539     } ,
51540     listeners : {
51541         
51542         show  : function(dlg)
51543         {
51544             //console.log(this);
51545             this.form = this.layout.getRegion('center').activePanel.form;
51546             this.form.dialog = dlg;
51547             this.buttons[0].form = this.form;
51548             this.buttons[0].dialog = dlg;
51549             this.buttons[1].form = this.form;
51550             this.buttons[1].dialog = dlg;
51551            
51552            //this.resizeToLogo.defer(1000,this);
51553             // this is all related to resizing for logos..
51554             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51555            //// if (!sz) {
51556              //   this.resizeToLogo.defer(1000,this);
51557              //   return;
51558            // }
51559             //var w = Ext.lib.Dom.getViewWidth() - 100;
51560             //var h = Ext.lib.Dom.getViewHeight() - 100;
51561             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51562             //this.center();
51563             if (this.disabled) {
51564                 this.hide();
51565                 return;
51566             }
51567             
51568             if (this.user.id < 0) { // used for inital setup situations.
51569                 return;
51570             }
51571             
51572             if (this.intervalID) {
51573                 // remove the timer
51574                 window.clearInterval(this.intervalID);
51575                 this.intervalID = false;
51576             }
51577             
51578             
51579             if (Roo.get('loading')) {
51580                 Roo.get('loading').remove();
51581             }
51582             if (Roo.get('loading-mask')) {
51583                 Roo.get('loading-mask').hide();
51584             }
51585             
51586             //incomming._node = tnode;
51587             this.form.reset();
51588             //this.dialog.modal = !modal;
51589             //this.dialog.show();
51590             this.el.unmask(); 
51591             
51592             
51593             this.form.setValues({
51594                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51595                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51596             });
51597             
51598             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51599             if (this.form.findField('username').getValue().length > 0 ){
51600                 this.form.findField('password').focus();
51601             } else {
51602                this.form.findField('username').focus();
51603             }
51604     
51605         }
51606     },
51607     items : [
51608          {
51609        
51610             xtype : 'ContentPanel',
51611             xns : Roo,
51612             region: 'center',
51613             fitToFrame : true,
51614             
51615             items : [
51616     
51617                 {
51618                
51619                     xtype : 'Form',
51620                     xns : Roo.form,
51621                     labelWidth: 100,
51622                     style : 'margin: 10px;',
51623                     
51624                     listeners : {
51625                         actionfailed : function(f, act) {
51626                             // form can return { errors: .... }
51627                                 
51628                             //act.result.errors // invalid form element list...
51629                             //act.result.errorMsg// invalid form element list...
51630                             
51631                             this.dialog.el.unmask();
51632                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51633                                         "Login failed - communication error - try again.");
51634                                       
51635                         },
51636                         actioncomplete: function(re, act) {
51637                              
51638                             Roo.state.Manager.set(
51639                                 this.dialog.realm + '.username',  
51640                                     this.findField('username').getValue()
51641                             );
51642                             Roo.state.Manager.set(
51643                                 this.dialog.realm + '.lang',  
51644                                 this.findField('lang').getValue() 
51645                             );
51646                             
51647                             this.dialog.fillAuth(act.result.data);
51648                               
51649                             this.dialog.hide();
51650                             
51651                             if (Roo.get('loading-mask')) {
51652                                 Roo.get('loading-mask').show();
51653                             }
51654                             Roo.XComponent.build();
51655                             
51656                              
51657                             
51658                         }
51659                     },
51660                     items : [
51661                         {
51662                             xtype : 'TextField',
51663                             xns : Roo.form,
51664                             fieldLabel: "Email Address",
51665                             name: 'username',
51666                             width:200,
51667                             autoCreate : {tag: "input", type: "text", size: "20"}
51668                         },
51669                         {
51670                             xtype : 'TextField',
51671                             xns : Roo.form,
51672                             fieldLabel: "Password",
51673                             inputType: 'password',
51674                             name: 'password',
51675                             width:200,
51676                             autoCreate : {tag: "input", type: "text", size: "20"},
51677                             listeners : {
51678                                 specialkey : function(e,ev) {
51679                                     if (ev.keyCode == 13) {
51680                                         this.form.dialog.el.mask("Logging in");
51681                                         this.form.doAction('submit', {
51682                                             url: this.form.dialog.url,
51683                                             method: this.form.dialog.method
51684                                         });
51685                                     }
51686                                 }
51687                             }  
51688                         },
51689                         {
51690                             xtype : 'ComboBox',
51691                             xns : Roo.form,
51692                             fieldLabel: "Language",
51693                             name : 'langdisp',
51694                             store: {
51695                                 xtype : 'SimpleStore',
51696                                 fields: ['lang', 'ldisp'],
51697                                 data : [
51698                                     [ 'en', 'English' ],
51699                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51700                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51701                                 ]
51702                             },
51703                             
51704                             valueField : 'lang',
51705                             hiddenName:  'lang',
51706                             width: 200,
51707                             displayField:'ldisp',
51708                             typeAhead: false,
51709                             editable: false,
51710                             mode: 'local',
51711                             triggerAction: 'all',
51712                             emptyText:'Select a Language...',
51713                             selectOnFocus:true,
51714                             listeners : {
51715                                 select :  function(cb, rec, ix) {
51716                                     this.form.switchLang(rec.data.lang);
51717                                 }
51718                             }
51719                         
51720                         }
51721                     ]
51722                 }
51723                   
51724                 
51725             ]
51726         }
51727     ],
51728     buttons : [
51729         {
51730             xtype : 'Button',
51731             xns : 'Roo',
51732             text : "Forgot Password",
51733             listeners : {
51734                 click : function() {
51735                     //console.log(this);
51736                     var n = this.form.findField('username').getValue();
51737                     if (!n.length) {
51738                         Roo.MessageBox.alert("Error", "Fill in your email address");
51739                         return;
51740                     }
51741                     Roo.Ajax.request({
51742                         url: this.dialog.url,
51743                         params: {
51744                             passwordRequest: n
51745                         },
51746                         method: this.dialog.method,
51747                         success:  function(response, opts)  {  // check successfull...
51748                         
51749                             var res = this.dialog.processResponse(response);
51750                             if (!res.success) { // error!
51751                                Roo.MessageBox.alert("Error" ,
51752                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51753                                return;
51754                             }
51755                             Roo.MessageBox.alert("Notice" ,
51756                                 "Please check you email for the Password Reset message");
51757                         },
51758                         failure : function() {
51759                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51760                         }
51761                         
51762                     });
51763                 }
51764             }
51765         },
51766         {
51767             xtype : 'Button',
51768             xns : 'Roo',
51769             text : "Login",
51770             listeners : {
51771                 
51772                 click : function () {
51773                         
51774                     this.dialog.el.mask("Logging in");
51775                     this.form.doAction('submit', {
51776                             url: this.dialog.url,
51777                             method: this.dialog.method
51778                     });
51779                 }
51780             }
51781         }
51782     ]
51783   
51784   
51785 })
51786  
51787
51788
51789