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     var listeners = false; ;
18268     if (config && config.listeners) {
18269         listeners= config.listeners;
18270         delete config.listeners;
18271     }
18272     Roo.apply(this, config);
18273     
18274     if(this.containerScroll){
18275         Roo.dd.ScrollManager.register(this.el);
18276     }
18277     this.addEvents( {
18278          /**
18279          * @scope Roo.dd.DropTarget
18280          */
18281          
18282          /**
18283          * @event enter
18284          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18285          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18286          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18287          * 
18288          * IMPORTANT : it should set this.overClass and this.dropAllowed
18289          * 
18290          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18291          * @param {Event} e The event
18292          * @param {Object} data An object containing arbitrary data supplied by the drag source
18293          */
18294         "enter" : true,
18295         
18296          /**
18297          * @event over
18298          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18299          * This method will be called on every mouse movement while the drag source is over the drop target.
18300          * This default implementation simply returns the dropAllowed config value.
18301          * 
18302          * IMPORTANT : it should set this.dropAllowed
18303          * 
18304          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18305          * @param {Event} e The event
18306          * @param {Object} data An object containing arbitrary data supplied by the drag source
18307          
18308          */
18309         "over" : true,
18310         /**
18311          * @event out
18312          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18313          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18314          * overClass (if any) from the drop element.
18315          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18316          * @param {Event} e The event
18317          * @param {Object} data An object containing arbitrary data supplied by the drag source
18318          */
18319          "out" : true,
18320          
18321         /**
18322          * @event drop
18323          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18324          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18325          * implementation that does something to process the drop event and returns true so that the drag source's
18326          * repair action does not run.
18327          * 
18328          * IMPORTANT : it should set this.success
18329          * 
18330          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18331          * @param {Event} e The event
18332          * @param {Object} data An object containing arbitrary data supplied by the drag source
18333         */
18334          "drop" : true
18335     });
18336             
18337      
18338     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18339         this.el.dom, 
18340         this.ddGroup || this.group,
18341         {
18342             isTarget: true,
18343             listeners : listeners || {} 
18344            
18345         
18346         }
18347     );
18348
18349 };
18350
18351 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18352     /**
18353      * @cfg {String} overClass
18354      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18355      */
18356      /**
18357      * @cfg {String} ddGroup
18358      * The drag drop group to handle drop events for
18359      */
18360      
18361     /**
18362      * @cfg {String} dropAllowed
18363      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18364      */
18365     dropAllowed : "x-dd-drop-ok",
18366     /**
18367      * @cfg {String} dropNotAllowed
18368      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18369      */
18370     dropNotAllowed : "x-dd-drop-nodrop",
18371     /**
18372      * @cfg {boolean} success
18373      * set this after drop listener.. 
18374      */
18375     success : false,
18376     /**
18377      * @cfg {boolean|String} valid true/false or string (add/sub/ok/nodrop)
18378      * if the drop point is valid for over/enter..
18379      */
18380     valid : false,
18381     // private
18382     isTarget : true,
18383
18384     // private
18385     isNotifyTarget : true,
18386     
18387     /**
18388      * @hide
18389      */
18390     notifyEnter : function(dd, e, data){
18391         this.valid = true;
18392         this.fireEvent('enter', this, dd, e, data);
18393         if(this.overClass){
18394             this.el.addClass(this.overClass);
18395         }
18396         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18397             this.valid ? this.dropAllowed : this.dropNotAllowed
18398         );
18399     },
18400
18401     /**
18402      * @hide
18403      */
18404     notifyOver : function(dd, e, data){
18405         this.valid = true;
18406         this.fireEvent('over', this, dd, e, data);
18407         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18408             this.valid ? this.dropAllowed : this.dropNotAllowed
18409         );
18410     },
18411
18412     /**
18413      * @hide
18414      */
18415     notifyOut : function(dd, e, data){
18416         this.fireEvent('out', this, dd, e, data);
18417         if(this.overClass){
18418             this.el.removeClass(this.overClass);
18419         }
18420     },
18421
18422     /**
18423      * @hide
18424      */
18425     notifyDrop : function(dd, e, data){
18426         this.success = false;
18427         this.fireEvent('drop', this, dd, e, data);
18428         return this.success;
18429     }
18430 });/*
18431  * Based on:
18432  * Ext JS Library 1.1.1
18433  * Copyright(c) 2006-2007, Ext JS, LLC.
18434  *
18435  * Originally Released Under LGPL - original licence link has changed is not relivant.
18436  *
18437  * Fork - LGPL
18438  * <script type="text/javascript">
18439  */
18440
18441
18442 /**
18443  * @class Roo.dd.DragZone
18444  * @extends Roo.dd.DragSource
18445  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18446  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18447  * @constructor
18448  * @param {String/HTMLElement/Element} el The container element
18449  * @param {Object} config
18450  */
18451 Roo.dd.DragZone = function(el, config){
18452     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18453     if(this.containerScroll){
18454         Roo.dd.ScrollManager.register(this.el);
18455     }
18456 };
18457
18458 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18459     /**
18460      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18461      * for auto scrolling during drag operations.
18462      */
18463     /**
18464      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18465      * method after a failed drop (defaults to "c3daf9" - light blue)
18466      */
18467
18468     /**
18469      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18470      * for a valid target to drag based on the mouse down. Override this method
18471      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18472      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18473      * @param {EventObject} e The mouse down event
18474      * @return {Object} The dragData
18475      */
18476     getDragData : function(e){
18477         return Roo.dd.Registry.getHandleFromEvent(e);
18478     },
18479     
18480     /**
18481      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18482      * this.dragData.ddel
18483      * @param {Number} x The x position of the click on the dragged object
18484      * @param {Number} y The y position of the click on the dragged object
18485      * @return {Boolean} true to continue the drag, false to cancel
18486      */
18487     onInitDrag : function(x, y){
18488         this.proxy.update(this.dragData.ddel.cloneNode(true));
18489         this.onStartDrag(x, y);
18490         return true;
18491     },
18492     
18493     /**
18494      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18495      */
18496     afterRepair : function(){
18497         if(Roo.enableFx){
18498             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18499         }
18500         this.dragging = false;
18501     },
18502
18503     /**
18504      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18505      * the XY of this.dragData.ddel
18506      * @param {EventObject} e The mouse up event
18507      * @return {Array} The xy location (e.g. [100, 200])
18508      */
18509     getRepairXY : function(e){
18510         return Roo.Element.fly(this.dragData.ddel).getXY();  
18511     }
18512 });/*
18513  * Based on:
18514  * Ext JS Library 1.1.1
18515  * Copyright(c) 2006-2007, Ext JS, LLC.
18516  *
18517  * Originally Released Under LGPL - original licence link has changed is not relivant.
18518  *
18519  * Fork - LGPL
18520  * <script type="text/javascript">
18521  */
18522 /**
18523  * @class Roo.dd.DropZone
18524  * @extends Roo.dd.DropTarget
18525  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18526  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18527  * @constructor
18528  * @param {String/HTMLElement/Element} el The container element
18529  * @param {Object} config
18530  */
18531 Roo.dd.DropZone = function(el, config){
18532     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18533 };
18534
18535 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18536     /**
18537      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18538      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18539      * provide your own custom lookup.
18540      * @param {Event} e The event
18541      * @return {Object} data The custom data
18542      */
18543     getTargetFromEvent : function(e){
18544         return Roo.dd.Registry.getTargetFromEvent(e);
18545     },
18546
18547     /**
18548      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18549      * that it has registered.  This method has no default implementation and should be overridden to provide
18550      * node-specific processing if necessary.
18551      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18552      * {@link #getTargetFromEvent} for this node)
18553      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18554      * @param {Event} e The event
18555      * @param {Object} data An object containing arbitrary data supplied by the drag source
18556      */
18557     onNodeEnter : function(n, dd, e, data){
18558         
18559     },
18560
18561     /**
18562      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18563      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18564      * overridden to provide the proper feedback.
18565      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18566      * {@link #getTargetFromEvent} for this node)
18567      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18568      * @param {Event} e The event
18569      * @param {Object} data An object containing arbitrary data supplied by the drag source
18570      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18571      * underlying {@link Roo.dd.StatusProxy} can be updated
18572      */
18573     onNodeOver : function(n, dd, e, data){
18574         return this.dropAllowed;
18575     },
18576
18577     /**
18578      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18579      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18580      * node-specific processing if necessary.
18581      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18582      * {@link #getTargetFromEvent} for this node)
18583      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18584      * @param {Event} e The event
18585      * @param {Object} data An object containing arbitrary data supplied by the drag source
18586      */
18587     onNodeOut : function(n, dd, e, data){
18588         
18589     },
18590
18591     /**
18592      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18593      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18594      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18595      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18596      * {@link #getTargetFromEvent} for this node)
18597      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18598      * @param {Event} e The event
18599      * @param {Object} data An object containing arbitrary data supplied by the drag source
18600      * @return {Boolean} True if the drop was valid, else false
18601      */
18602     onNodeDrop : function(n, dd, e, data){
18603         return false;
18604     },
18605
18606     /**
18607      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18608      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18609      * it should be overridden to provide the proper feedback if necessary.
18610      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18611      * @param {Event} e The event
18612      * @param {Object} data An object containing arbitrary data supplied by the drag source
18613      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18614      * underlying {@link Roo.dd.StatusProxy} can be updated
18615      */
18616     onContainerOver : function(dd, e, data){
18617         return this.dropNotAllowed;
18618     },
18619
18620     /**
18621      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18622      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18623      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18624      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18625      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18626      * @param {Event} e The event
18627      * @param {Object} data An object containing arbitrary data supplied by the drag source
18628      * @return {Boolean} True if the drop was valid, else false
18629      */
18630     onContainerDrop : function(dd, e, data){
18631         return false;
18632     },
18633
18634     /**
18635      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18636      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18637      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18638      * you should override this method and provide a custom implementation.
18639      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18640      * @param {Event} e The event
18641      * @param {Object} data An object containing arbitrary data supplied by the drag source
18642      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18643      * underlying {@link Roo.dd.StatusProxy} can be updated
18644      */
18645     notifyEnter : function(dd, e, data){
18646         return this.dropNotAllowed;
18647     },
18648
18649     /**
18650      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18651      * This method will be called on every mouse movement while the drag source is over the drop zone.
18652      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18653      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18654      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18655      * registered node, it will call {@link #onContainerOver}.
18656      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18657      * @param {Event} e The event
18658      * @param {Object} data An object containing arbitrary data supplied by the drag source
18659      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18660      * underlying {@link Roo.dd.StatusProxy} can be updated
18661      */
18662     notifyOver : function(dd, e, data){
18663         var n = this.getTargetFromEvent(e);
18664         if(!n){ // not over valid drop target
18665             if(this.lastOverNode){
18666                 this.onNodeOut(this.lastOverNode, dd, e, data);
18667                 this.lastOverNode = null;
18668             }
18669             return this.onContainerOver(dd, e, data);
18670         }
18671         if(this.lastOverNode != n){
18672             if(this.lastOverNode){
18673                 this.onNodeOut(this.lastOverNode, dd, e, data);
18674             }
18675             this.onNodeEnter(n, dd, e, data);
18676             this.lastOverNode = n;
18677         }
18678         return this.onNodeOver(n, dd, e, data);
18679     },
18680
18681     /**
18682      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18683      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18684      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18685      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18686      * @param {Event} e The event
18687      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18688      */
18689     notifyOut : function(dd, e, data){
18690         if(this.lastOverNode){
18691             this.onNodeOut(this.lastOverNode, dd, e, data);
18692             this.lastOverNode = null;
18693         }
18694     },
18695
18696     /**
18697      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18698      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18699      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18700      * otherwise it will call {@link #onContainerDrop}.
18701      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18702      * @param {Event} e The event
18703      * @param {Object} data An object containing arbitrary data supplied by the drag source
18704      * @return {Boolean} True if the drop was valid, else false
18705      */
18706     notifyDrop : function(dd, e, data){
18707         if(this.lastOverNode){
18708             this.onNodeOut(this.lastOverNode, dd, e, data);
18709             this.lastOverNode = null;
18710         }
18711         var n = this.getTargetFromEvent(e);
18712         return n ?
18713             this.onNodeDrop(n, dd, e, data) :
18714             this.onContainerDrop(dd, e, data);
18715     },
18716
18717     // private
18718     triggerCacheRefresh : function(){
18719         Roo.dd.DDM.refreshCache(this.groups);
18720     }  
18721 });/*
18722  * Based on:
18723  * Ext JS Library 1.1.1
18724  * Copyright(c) 2006-2007, Ext JS, LLC.
18725  *
18726  * Originally Released Under LGPL - original licence link has changed is not relivant.
18727  *
18728  * Fork - LGPL
18729  * <script type="text/javascript">
18730  */
18731
18732
18733 /**
18734  * @class Roo.data.SortTypes
18735  * @singleton
18736  * Defines the default sorting (casting?) comparison functions used when sorting data.
18737  */
18738 Roo.data.SortTypes = {
18739     /**
18740      * Default sort that does nothing
18741      * @param {Mixed} s The value being converted
18742      * @return {Mixed} The comparison value
18743      */
18744     none : function(s){
18745         return s;
18746     },
18747     
18748     /**
18749      * The regular expression used to strip tags
18750      * @type {RegExp}
18751      * @property
18752      */
18753     stripTagsRE : /<\/?[^>]+>/gi,
18754     
18755     /**
18756      * Strips all HTML tags to sort on text only
18757      * @param {Mixed} s The value being converted
18758      * @return {String} The comparison value
18759      */
18760     asText : function(s){
18761         return String(s).replace(this.stripTagsRE, "");
18762     },
18763     
18764     /**
18765      * Strips all HTML tags to sort on text only - Case insensitive
18766      * @param {Mixed} s The value being converted
18767      * @return {String} The comparison value
18768      */
18769     asUCText : function(s){
18770         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18771     },
18772     
18773     /**
18774      * Case insensitive string
18775      * @param {Mixed} s The value being converted
18776      * @return {String} The comparison value
18777      */
18778     asUCString : function(s) {
18779         return String(s).toUpperCase();
18780     },
18781     
18782     /**
18783      * Date sorting
18784      * @param {Mixed} s The value being converted
18785      * @return {Number} The comparison value
18786      */
18787     asDate : function(s) {
18788         if(!s){
18789             return 0;
18790         }
18791         if(s instanceof Date){
18792             return s.getTime();
18793         }
18794         return Date.parse(String(s));
18795     },
18796     
18797     /**
18798      * Float sorting
18799      * @param {Mixed} s The value being converted
18800      * @return {Float} The comparison value
18801      */
18802     asFloat : function(s) {
18803         var val = parseFloat(String(s).replace(/,/g, ""));
18804         if(isNaN(val)) val = 0;
18805         return val;
18806     },
18807     
18808     /**
18809      * Integer sorting
18810      * @param {Mixed} s The value being converted
18811      * @return {Number} The comparison value
18812      */
18813     asInt : function(s) {
18814         var val = parseInt(String(s).replace(/,/g, ""));
18815         if(isNaN(val)) val = 0;
18816         return val;
18817     }
18818 };/*
18819  * Based on:
18820  * Ext JS Library 1.1.1
18821  * Copyright(c) 2006-2007, Ext JS, LLC.
18822  *
18823  * Originally Released Under LGPL - original licence link has changed is not relivant.
18824  *
18825  * Fork - LGPL
18826  * <script type="text/javascript">
18827  */
18828
18829 /**
18830 * @class Roo.data.Record
18831  * Instances of this class encapsulate both record <em>definition</em> information, and record
18832  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18833  * to access Records cached in an {@link Roo.data.Store} object.<br>
18834  * <p>
18835  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18836  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18837  * objects.<br>
18838  * <p>
18839  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18840  * @constructor
18841  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18842  * {@link #create}. The parameters are the same.
18843  * @param {Array} data An associative Array of data values keyed by the field name.
18844  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18845  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18846  * not specified an integer id is generated.
18847  */
18848 Roo.data.Record = function(data, id){
18849     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18850     this.data = data;
18851 };
18852
18853 /**
18854  * Generate a constructor for a specific record layout.
18855  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18856  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18857  * Each field definition object may contain the following properties: <ul>
18858  * <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,
18859  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18860  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18861  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18862  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18863  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18864  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18865  * this may be omitted.</p></li>
18866  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18867  * <ul><li>auto (Default, implies no conversion)</li>
18868  * <li>string</li>
18869  * <li>int</li>
18870  * <li>float</li>
18871  * <li>boolean</li>
18872  * <li>date</li></ul></p></li>
18873  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18874  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18875  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18876  * by the Reader into an object that will be stored in the Record. It is passed the
18877  * following parameters:<ul>
18878  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18879  * </ul></p></li>
18880  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18881  * </ul>
18882  * <br>usage:<br><pre><code>
18883 var TopicRecord = Roo.data.Record.create(
18884     {name: 'title', mapping: 'topic_title'},
18885     {name: 'author', mapping: 'username'},
18886     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18887     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18888     {name: 'lastPoster', mapping: 'user2'},
18889     {name: 'excerpt', mapping: 'post_text'}
18890 );
18891
18892 var myNewRecord = new TopicRecord({
18893     title: 'Do my job please',
18894     author: 'noobie',
18895     totalPosts: 1,
18896     lastPost: new Date(),
18897     lastPoster: 'Animal',
18898     excerpt: 'No way dude!'
18899 });
18900 myStore.add(myNewRecord);
18901 </code></pre>
18902  * @method create
18903  * @static
18904  */
18905 Roo.data.Record.create = function(o){
18906     var f = function(){
18907         f.superclass.constructor.apply(this, arguments);
18908     };
18909     Roo.extend(f, Roo.data.Record);
18910     var p = f.prototype;
18911     p.fields = new Roo.util.MixedCollection(false, function(field){
18912         return field.name;
18913     });
18914     for(var i = 0, len = o.length; i < len; i++){
18915         p.fields.add(new Roo.data.Field(o[i]));
18916     }
18917     f.getField = function(name){
18918         return p.fields.get(name);  
18919     };
18920     return f;
18921 };
18922
18923 Roo.data.Record.AUTO_ID = 1000;
18924 Roo.data.Record.EDIT = 'edit';
18925 Roo.data.Record.REJECT = 'reject';
18926 Roo.data.Record.COMMIT = 'commit';
18927
18928 Roo.data.Record.prototype = {
18929     /**
18930      * Readonly flag - true if this record has been modified.
18931      * @type Boolean
18932      */
18933     dirty : false,
18934     editing : false,
18935     error: null,
18936     modified: null,
18937
18938     // private
18939     join : function(store){
18940         this.store = store;
18941     },
18942
18943     /**
18944      * Set the named field to the specified value.
18945      * @param {String} name The name of the field to set.
18946      * @param {Object} value The value to set the field to.
18947      */
18948     set : function(name, value){
18949         if(this.data[name] == value){
18950             return;
18951         }
18952         this.dirty = true;
18953         if(!this.modified){
18954             this.modified = {};
18955         }
18956         if(typeof this.modified[name] == 'undefined'){
18957             this.modified[name] = this.data[name];
18958         }
18959         this.data[name] = value;
18960         if(!this.editing){
18961             this.store.afterEdit(this);
18962         }       
18963     },
18964
18965     /**
18966      * Get the value of the named field.
18967      * @param {String} name The name of the field to get the value of.
18968      * @return {Object} The value of the field.
18969      */
18970     get : function(name){
18971         return this.data[name]; 
18972     },
18973
18974     // private
18975     beginEdit : function(){
18976         this.editing = true;
18977         this.modified = {}; 
18978     },
18979
18980     // private
18981     cancelEdit : function(){
18982         this.editing = false;
18983         delete this.modified;
18984     },
18985
18986     // private
18987     endEdit : function(){
18988         this.editing = false;
18989         if(this.dirty && this.store){
18990             this.store.afterEdit(this);
18991         }
18992     },
18993
18994     /**
18995      * Usually called by the {@link Roo.data.Store} which owns the Record.
18996      * Rejects all changes made to the Record since either creation, or the last commit operation.
18997      * Modified fields are reverted to their original values.
18998      * <p>
18999      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19000      * of reject operations.
19001      */
19002     reject : function(){
19003         var m = this.modified;
19004         for(var n in m){
19005             if(typeof m[n] != "function"){
19006                 this.data[n] = m[n];
19007             }
19008         }
19009         this.dirty = false;
19010         delete this.modified;
19011         this.editing = false;
19012         if(this.store){
19013             this.store.afterReject(this);
19014         }
19015     },
19016
19017     /**
19018      * Usually called by the {@link Roo.data.Store} which owns the Record.
19019      * Commits all changes made to the Record since either creation, or the last commit operation.
19020      * <p>
19021      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19022      * of commit operations.
19023      */
19024     commit : function(){
19025         this.dirty = false;
19026         delete this.modified;
19027         this.editing = false;
19028         if(this.store){
19029             this.store.afterCommit(this);
19030         }
19031     },
19032
19033     // private
19034     hasError : function(){
19035         return this.error != null;
19036     },
19037
19038     // private
19039     clearError : function(){
19040         this.error = null;
19041     },
19042
19043     /**
19044      * Creates a copy of this record.
19045      * @param {String} id (optional) A new record id if you don't want to use this record's id
19046      * @return {Record}
19047      */
19048     copy : function(newId) {
19049         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19050     }
19051 };/*
19052  * Based on:
19053  * Ext JS Library 1.1.1
19054  * Copyright(c) 2006-2007, Ext JS, LLC.
19055  *
19056  * Originally Released Under LGPL - original licence link has changed is not relivant.
19057  *
19058  * Fork - LGPL
19059  * <script type="text/javascript">
19060  */
19061
19062
19063
19064 /**
19065  * @class Roo.data.Store
19066  * @extends Roo.util.Observable
19067  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19068  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19069  * <p>
19070  * 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
19071  * has no knowledge of the format of the data returned by the Proxy.<br>
19072  * <p>
19073  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19074  * instances from the data object. These records are cached and made available through accessor functions.
19075  * @constructor
19076  * Creates a new Store.
19077  * @param {Object} config A config object containing the objects needed for the Store to access data,
19078  * and read the data into Records.
19079  */
19080 Roo.data.Store = function(config){
19081     this.data = new Roo.util.MixedCollection(false);
19082     this.data.getKey = function(o){
19083         return o.id;
19084     };
19085     this.baseParams = {};
19086     // private
19087     this.paramNames = {
19088         "start" : "start",
19089         "limit" : "limit",
19090         "sort" : "sort",
19091         "dir" : "dir"
19092     };
19093
19094     if(config && config.data){
19095         this.inlineData = config.data;
19096         delete config.data;
19097     }
19098
19099     Roo.apply(this, config);
19100     
19101     if(this.reader){ // reader passed
19102         this.reader = Roo.factory(this.reader, Roo.data);
19103         this.reader.xmodule = this.xmodule || false;
19104         if(!this.recordType){
19105             this.recordType = this.reader.recordType;
19106         }
19107         if(this.reader.onMetaChange){
19108             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19109         }
19110     }
19111
19112     if(this.recordType){
19113         this.fields = this.recordType.prototype.fields;
19114     }
19115     this.modified = [];
19116
19117     this.addEvents({
19118         /**
19119          * @event datachanged
19120          * Fires when the data cache has changed, and a widget which is using this Store
19121          * as a Record cache should refresh its view.
19122          * @param {Store} this
19123          */
19124         datachanged : true,
19125         /**
19126          * @event metachange
19127          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19128          * @param {Store} this
19129          * @param {Object} meta The JSON metadata
19130          */
19131         metachange : true,
19132         /**
19133          * @event add
19134          * Fires when Records have been added to the Store
19135          * @param {Store} this
19136          * @param {Roo.data.Record[]} records The array of Records added
19137          * @param {Number} index The index at which the record(s) were added
19138          */
19139         add : true,
19140         /**
19141          * @event remove
19142          * Fires when a Record has been removed from the Store
19143          * @param {Store} this
19144          * @param {Roo.data.Record} record The Record that was removed
19145          * @param {Number} index The index at which the record was removed
19146          */
19147         remove : true,
19148         /**
19149          * @event update
19150          * Fires when a Record has been updated
19151          * @param {Store} this
19152          * @param {Roo.data.Record} record The Record that was updated
19153          * @param {String} operation The update operation being performed.  Value may be one of:
19154          * <pre><code>
19155  Roo.data.Record.EDIT
19156  Roo.data.Record.REJECT
19157  Roo.data.Record.COMMIT
19158          * </code></pre>
19159          */
19160         update : true,
19161         /**
19162          * @event clear
19163          * Fires when the data cache has been cleared.
19164          * @param {Store} this
19165          */
19166         clear : true,
19167         /**
19168          * @event beforeload
19169          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19170          * the load action will be canceled.
19171          * @param {Store} this
19172          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19173          */
19174         beforeload : true,
19175         /**
19176          * @event load
19177          * Fires after a new set of Records has been loaded.
19178          * @param {Store} this
19179          * @param {Roo.data.Record[]} records The Records that were loaded
19180          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19181          */
19182         load : true,
19183         /**
19184          * @event loadexception
19185          * Fires if an exception occurs in the Proxy during loading.
19186          * Called with the signature of the Proxy's "loadexception" event.
19187          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19188          * 
19189          * @param {Proxy} 
19190          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19191          * @param {Object} load options 
19192          * @param {Object} jsonData from your request (normally this contains the Exception)
19193          */
19194         loadexception : true
19195     });
19196     
19197     if(this.proxy){
19198         this.proxy = Roo.factory(this.proxy, Roo.data);
19199         this.proxy.xmodule = this.xmodule || false;
19200         this.relayEvents(this.proxy,  ["loadexception"]);
19201     }
19202     this.sortToggle = {};
19203
19204     Roo.data.Store.superclass.constructor.call(this);
19205
19206     if(this.inlineData){
19207         this.loadData(this.inlineData);
19208         delete this.inlineData;
19209     }
19210 };
19211 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19212      /**
19213     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19214     * without a remote query - used by combo/forms at present.
19215     */
19216     
19217     /**
19218     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19219     */
19220     /**
19221     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19222     */
19223     /**
19224     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19225     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19226     */
19227     /**
19228     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19229     * on any HTTP request
19230     */
19231     /**
19232     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19233     */
19234     /**
19235     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19236     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19237     */
19238     remoteSort : false,
19239
19240     /**
19241     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19242      * loaded or when a record is removed. (defaults to false).
19243     */
19244     pruneModifiedRecords : false,
19245
19246     // private
19247     lastOptions : null,
19248
19249     /**
19250      * Add Records to the Store and fires the add event.
19251      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19252      */
19253     add : function(records){
19254         records = [].concat(records);
19255         for(var i = 0, len = records.length; i < len; i++){
19256             records[i].join(this);
19257         }
19258         var index = this.data.length;
19259         this.data.addAll(records);
19260         this.fireEvent("add", this, records, index);
19261     },
19262
19263     /**
19264      * Remove a Record from the Store and fires the remove event.
19265      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19266      */
19267     remove : function(record){
19268         var index = this.data.indexOf(record);
19269         this.data.removeAt(index);
19270         if(this.pruneModifiedRecords){
19271             this.modified.remove(record);
19272         }
19273         this.fireEvent("remove", this, record, index);
19274     },
19275
19276     /**
19277      * Remove all Records from the Store and fires the clear event.
19278      */
19279     removeAll : function(){
19280         this.data.clear();
19281         if(this.pruneModifiedRecords){
19282             this.modified = [];
19283         }
19284         this.fireEvent("clear", this);
19285     },
19286
19287     /**
19288      * Inserts Records to the Store at the given index and fires the add event.
19289      * @param {Number} index The start index at which to insert the passed Records.
19290      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19291      */
19292     insert : function(index, records){
19293         records = [].concat(records);
19294         for(var i = 0, len = records.length; i < len; i++){
19295             this.data.insert(index, records[i]);
19296             records[i].join(this);
19297         }
19298         this.fireEvent("add", this, records, index);
19299     },
19300
19301     /**
19302      * Get the index within the cache of the passed Record.
19303      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19304      * @return {Number} The index of the passed Record. Returns -1 if not found.
19305      */
19306     indexOf : function(record){
19307         return this.data.indexOf(record);
19308     },
19309
19310     /**
19311      * Get the index within the cache of the Record with the passed id.
19312      * @param {String} id The id of the Record to find.
19313      * @return {Number} The index of the Record. Returns -1 if not found.
19314      */
19315     indexOfId : function(id){
19316         return this.data.indexOfKey(id);
19317     },
19318
19319     /**
19320      * Get the Record with the specified id.
19321      * @param {String} id The id of the Record to find.
19322      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19323      */
19324     getById : function(id){
19325         return this.data.key(id);
19326     },
19327
19328     /**
19329      * Get the Record at the specified index.
19330      * @param {Number} index The index of the Record to find.
19331      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19332      */
19333     getAt : function(index){
19334         return this.data.itemAt(index);
19335     },
19336
19337     /**
19338      * Returns a range of Records between specified indices.
19339      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19340      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19341      * @return {Roo.data.Record[]} An array of Records
19342      */
19343     getRange : function(start, end){
19344         return this.data.getRange(start, end);
19345     },
19346
19347     // private
19348     storeOptions : function(o){
19349         o = Roo.apply({}, o);
19350         delete o.callback;
19351         delete o.scope;
19352         this.lastOptions = o;
19353     },
19354
19355     /**
19356      * Loads the Record cache from the configured Proxy using the configured Reader.
19357      * <p>
19358      * If using remote paging, then the first load call must specify the <em>start</em>
19359      * and <em>limit</em> properties in the options.params property to establish the initial
19360      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19361      * <p>
19362      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19363      * and this call will return before the new data has been loaded. Perform any post-processing
19364      * in a callback function, or in a "load" event handler.</strong>
19365      * <p>
19366      * @param {Object} options An object containing properties which control loading options:<ul>
19367      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19368      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19369      * passed the following arguments:<ul>
19370      * <li>r : Roo.data.Record[]</li>
19371      * <li>options: Options object from the load call</li>
19372      * <li>success: Boolean success indicator</li></ul></li>
19373      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19374      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19375      * </ul>
19376      */
19377     load : function(options){
19378         options = options || {};
19379         if(this.fireEvent("beforeload", this, options) !== false){
19380             this.storeOptions(options);
19381             var p = Roo.apply(options.params || {}, this.baseParams);
19382             // if meta was not loaded from remote source.. try requesting it.
19383             if (!this.reader.metaFromRemote) {
19384                 p._requestMeta = 1;
19385             }
19386             if(this.sortInfo && this.remoteSort){
19387                 var pn = this.paramNames;
19388                 p[pn["sort"]] = this.sortInfo.field;
19389                 p[pn["dir"]] = this.sortInfo.direction;
19390             }
19391             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19392         }
19393     },
19394
19395     /**
19396      * Reloads the Record cache from the configured Proxy using the configured Reader and
19397      * the options from the last load operation performed.
19398      * @param {Object} options (optional) An object containing properties which may override the options
19399      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19400      * the most recently used options are reused).
19401      */
19402     reload : function(options){
19403         this.load(Roo.applyIf(options||{}, this.lastOptions));
19404     },
19405
19406     // private
19407     // Called as a callback by the Reader during a load operation.
19408     loadRecords : function(o, options, success){
19409         if(!o || success === false){
19410             if(success !== false){
19411                 this.fireEvent("load", this, [], options);
19412             }
19413             if(options.callback){
19414                 options.callback.call(options.scope || this, [], options, false);
19415             }
19416             return;
19417         }
19418         // if data returned failure - throw an exception.
19419         if (o.success === false) {
19420             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19421             return;
19422         }
19423         var r = o.records, t = o.totalRecords || r.length;
19424         if(!options || options.add !== true){
19425             if(this.pruneModifiedRecords){
19426                 this.modified = [];
19427             }
19428             for(var i = 0, len = r.length; i < len; i++){
19429                 r[i].join(this);
19430             }
19431             if(this.snapshot){
19432                 this.data = this.snapshot;
19433                 delete this.snapshot;
19434             }
19435             this.data.clear();
19436             this.data.addAll(r);
19437             this.totalLength = t;
19438             this.applySort();
19439             this.fireEvent("datachanged", this);
19440         }else{
19441             this.totalLength = Math.max(t, this.data.length+r.length);
19442             this.add(r);
19443         }
19444         this.fireEvent("load", this, r, options);
19445         if(options.callback){
19446             options.callback.call(options.scope || this, r, options, true);
19447         }
19448     },
19449
19450     /**
19451      * Loads data from a passed data block. A Reader which understands the format of the data
19452      * must have been configured in the constructor.
19453      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19454      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19455      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19456      */
19457     loadData : function(o, append){
19458         var r = this.reader.readRecords(o);
19459         this.loadRecords(r, {add: append}, true);
19460     },
19461
19462     /**
19463      * Gets the number of cached records.
19464      * <p>
19465      * <em>If using paging, this may not be the total size of the dataset. If the data object
19466      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19467      * the data set size</em>
19468      */
19469     getCount : function(){
19470         return this.data.length || 0;
19471     },
19472
19473     /**
19474      * Gets the total number of records in the dataset as returned by the server.
19475      * <p>
19476      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19477      * the dataset size</em>
19478      */
19479     getTotalCount : function(){
19480         return this.totalLength || 0;
19481     },
19482
19483     /**
19484      * Returns the sort state of the Store as an object with two properties:
19485      * <pre><code>
19486  field {String} The name of the field by which the Records are sorted
19487  direction {String} The sort order, "ASC" or "DESC"
19488      * </code></pre>
19489      */
19490     getSortState : function(){
19491         return this.sortInfo;
19492     },
19493
19494     // private
19495     applySort : function(){
19496         if(this.sortInfo && !this.remoteSort){
19497             var s = this.sortInfo, f = s.field;
19498             var st = this.fields.get(f).sortType;
19499             var fn = function(r1, r2){
19500                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19501                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19502             };
19503             this.data.sort(s.direction, fn);
19504             if(this.snapshot && this.snapshot != this.data){
19505                 this.snapshot.sort(s.direction, fn);
19506             }
19507         }
19508     },
19509
19510     /**
19511      * Sets the default sort column and order to be used by the next load operation.
19512      * @param {String} fieldName The name of the field to sort by.
19513      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19514      */
19515     setDefaultSort : function(field, dir){
19516         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19517     },
19518
19519     /**
19520      * Sort the Records.
19521      * If remote sorting is used, the sort is performed on the server, and the cache is
19522      * reloaded. If local sorting is used, the cache is sorted internally.
19523      * @param {String} fieldName The name of the field to sort by.
19524      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19525      */
19526     sort : function(fieldName, dir){
19527         var f = this.fields.get(fieldName);
19528         if(!dir){
19529             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19530                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19531             }else{
19532                 dir = f.sortDir;
19533             }
19534         }
19535         this.sortToggle[f.name] = dir;
19536         this.sortInfo = {field: f.name, direction: dir};
19537         if(!this.remoteSort){
19538             this.applySort();
19539             this.fireEvent("datachanged", this);
19540         }else{
19541             this.load(this.lastOptions);
19542         }
19543     },
19544
19545     /**
19546      * Calls the specified function for each of the Records in the cache.
19547      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19548      * Returning <em>false</em> aborts and exits the iteration.
19549      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19550      */
19551     each : function(fn, scope){
19552         this.data.each(fn, scope);
19553     },
19554
19555     /**
19556      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19557      * (e.g., during paging).
19558      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19559      */
19560     getModifiedRecords : function(){
19561         return this.modified;
19562     },
19563
19564     // private
19565     createFilterFn : function(property, value, anyMatch){
19566         if(!value.exec){ // not a regex
19567             value = String(value);
19568             if(value.length == 0){
19569                 return false;
19570             }
19571             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19572         }
19573         return function(r){
19574             return value.test(r.data[property]);
19575         };
19576     },
19577
19578     /**
19579      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19580      * @param {String} property A field on your records
19581      * @param {Number} start The record index to start at (defaults to 0)
19582      * @param {Number} end The last record index to include (defaults to length - 1)
19583      * @return {Number} The sum
19584      */
19585     sum : function(property, start, end){
19586         var rs = this.data.items, v = 0;
19587         start = start || 0;
19588         end = (end || end === 0) ? end : rs.length-1;
19589
19590         for(var i = start; i <= end; i++){
19591             v += (rs[i].data[property] || 0);
19592         }
19593         return v;
19594     },
19595
19596     /**
19597      * Filter the records by a specified property.
19598      * @param {String} field A field on your records
19599      * @param {String/RegExp} value Either a string that the field
19600      * should start with or a RegExp to test against the field
19601      * @param {Boolean} anyMatch True to match any part not just the beginning
19602      */
19603     filter : function(property, value, anyMatch){
19604         var fn = this.createFilterFn(property, value, anyMatch);
19605         return fn ? this.filterBy(fn) : this.clearFilter();
19606     },
19607
19608     /**
19609      * Filter by a function. The specified function will be called with each
19610      * record in this data source. If the function returns true the record is included,
19611      * otherwise it is filtered.
19612      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19613      * @param {Object} scope (optional) The scope of the function (defaults to this)
19614      */
19615     filterBy : function(fn, scope){
19616         this.snapshot = this.snapshot || this.data;
19617         this.data = this.queryBy(fn, scope||this);
19618         this.fireEvent("datachanged", this);
19619     },
19620
19621     /**
19622      * Query the records by a specified property.
19623      * @param {String} field A field on your records
19624      * @param {String/RegExp} value Either a string that the field
19625      * should start with or a RegExp to test against the field
19626      * @param {Boolean} anyMatch True to match any part not just the beginning
19627      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19628      */
19629     query : function(property, value, anyMatch){
19630         var fn = this.createFilterFn(property, value, anyMatch);
19631         return fn ? this.queryBy(fn) : this.data.clone();
19632     },
19633
19634     /**
19635      * Query by a function. The specified function will be called with each
19636      * record in this data source. If the function returns true the record is included
19637      * in the results.
19638      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19639      * @param {Object} scope (optional) The scope of the function (defaults to this)
19640       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19641      **/
19642     queryBy : function(fn, scope){
19643         var data = this.snapshot || this.data;
19644         return data.filterBy(fn, scope||this);
19645     },
19646
19647     /**
19648      * Collects unique values for a particular dataIndex from this store.
19649      * @param {String} dataIndex The property to collect
19650      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19651      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19652      * @return {Array} An array of the unique values
19653      **/
19654     collect : function(dataIndex, allowNull, bypassFilter){
19655         var d = (bypassFilter === true && this.snapshot) ?
19656                 this.snapshot.items : this.data.items;
19657         var v, sv, r = [], l = {};
19658         for(var i = 0, len = d.length; i < len; i++){
19659             v = d[i].data[dataIndex];
19660             sv = String(v);
19661             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19662                 l[sv] = true;
19663                 r[r.length] = v;
19664             }
19665         }
19666         return r;
19667     },
19668
19669     /**
19670      * Revert to a view of the Record cache with no filtering applied.
19671      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19672      */
19673     clearFilter : function(suppressEvent){
19674         if(this.snapshot && this.snapshot != this.data){
19675             this.data = this.snapshot;
19676             delete this.snapshot;
19677             if(suppressEvent !== true){
19678                 this.fireEvent("datachanged", this);
19679             }
19680         }
19681     },
19682
19683     // private
19684     afterEdit : function(record){
19685         if(this.modified.indexOf(record) == -1){
19686             this.modified.push(record);
19687         }
19688         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19689     },
19690
19691     // private
19692     afterReject : function(record){
19693         this.modified.remove(record);
19694         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19695     },
19696
19697     // private
19698     afterCommit : function(record){
19699         this.modified.remove(record);
19700         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19701     },
19702
19703     /**
19704      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19705      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19706      */
19707     commitChanges : function(){
19708         var m = this.modified.slice(0);
19709         this.modified = [];
19710         for(var i = 0, len = m.length; i < len; i++){
19711             m[i].commit();
19712         }
19713     },
19714
19715     /**
19716      * Cancel outstanding changes on all changed records.
19717      */
19718     rejectChanges : function(){
19719         var m = this.modified.slice(0);
19720         this.modified = [];
19721         for(var i = 0, len = m.length; i < len; i++){
19722             m[i].reject();
19723         }
19724     },
19725
19726     onMetaChange : function(meta, rtype, o){
19727         this.recordType = rtype;
19728         this.fields = rtype.prototype.fields;
19729         delete this.snapshot;
19730         this.sortInfo = meta.sortInfo || this.sortInfo;
19731         this.modified = [];
19732         this.fireEvent('metachange', this, this.reader.meta);
19733     }
19734 });/*
19735  * Based on:
19736  * Ext JS Library 1.1.1
19737  * Copyright(c) 2006-2007, Ext JS, LLC.
19738  *
19739  * Originally Released Under LGPL - original licence link has changed is not relivant.
19740  *
19741  * Fork - LGPL
19742  * <script type="text/javascript">
19743  */
19744
19745 /**
19746  * @class Roo.data.SimpleStore
19747  * @extends Roo.data.Store
19748  * Small helper class to make creating Stores from Array data easier.
19749  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19750  * @cfg {Array} fields An array of field definition objects, or field name strings.
19751  * @cfg {Array} data The multi-dimensional array of data
19752  * @constructor
19753  * @param {Object} config
19754  */
19755 Roo.data.SimpleStore = function(config){
19756     Roo.data.SimpleStore.superclass.constructor.call(this, {
19757         isLocal : true,
19758         reader: new Roo.data.ArrayReader({
19759                 id: config.id
19760             },
19761             Roo.data.Record.create(config.fields)
19762         ),
19763         proxy : new Roo.data.MemoryProxy(config.data)
19764     });
19765     this.load();
19766 };
19767 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19768  * Based on:
19769  * Ext JS Library 1.1.1
19770  * Copyright(c) 2006-2007, Ext JS, LLC.
19771  *
19772  * Originally Released Under LGPL - original licence link has changed is not relivant.
19773  *
19774  * Fork - LGPL
19775  * <script type="text/javascript">
19776  */
19777
19778 /**
19779 /**
19780  * @extends Roo.data.Store
19781  * @class Roo.data.JsonStore
19782  * Small helper class to make creating Stores for JSON data easier. <br/>
19783 <pre><code>
19784 var store = new Roo.data.JsonStore({
19785     url: 'get-images.php',
19786     root: 'images',
19787     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19788 });
19789 </code></pre>
19790  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19791  * JsonReader and HttpProxy (unless inline data is provided).</b>
19792  * @cfg {Array} fields An array of field definition objects, or field name strings.
19793  * @constructor
19794  * @param {Object} config
19795  */
19796 Roo.data.JsonStore = function(c){
19797     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19798         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19799         reader: new Roo.data.JsonReader(c, c.fields)
19800     }));
19801 };
19802 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19803  * Based on:
19804  * Ext JS Library 1.1.1
19805  * Copyright(c) 2006-2007, Ext JS, LLC.
19806  *
19807  * Originally Released Under LGPL - original licence link has changed is not relivant.
19808  *
19809  * Fork - LGPL
19810  * <script type="text/javascript">
19811  */
19812
19813  
19814 Roo.data.Field = function(config){
19815     if(typeof config == "string"){
19816         config = {name: config};
19817     }
19818     Roo.apply(this, config);
19819     
19820     if(!this.type){
19821         this.type = "auto";
19822     }
19823     
19824     var st = Roo.data.SortTypes;
19825     // named sortTypes are supported, here we look them up
19826     if(typeof this.sortType == "string"){
19827         this.sortType = st[this.sortType];
19828     }
19829     
19830     // set default sortType for strings and dates
19831     if(!this.sortType){
19832         switch(this.type){
19833             case "string":
19834                 this.sortType = st.asUCString;
19835                 break;
19836             case "date":
19837                 this.sortType = st.asDate;
19838                 break;
19839             default:
19840                 this.sortType = st.none;
19841         }
19842     }
19843
19844     // define once
19845     var stripRe = /[\$,%]/g;
19846
19847     // prebuilt conversion function for this field, instead of
19848     // switching every time we're reading a value
19849     if(!this.convert){
19850         var cv, dateFormat = this.dateFormat;
19851         switch(this.type){
19852             case "":
19853             case "auto":
19854             case undefined:
19855                 cv = function(v){ return v; };
19856                 break;
19857             case "string":
19858                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19859                 break;
19860             case "int":
19861                 cv = function(v){
19862                     return v !== undefined && v !== null && v !== '' ?
19863                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19864                     };
19865                 break;
19866             case "float":
19867                 cv = function(v){
19868                     return v !== undefined && v !== null && v !== '' ?
19869                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19870                     };
19871                 break;
19872             case "bool":
19873             case "boolean":
19874                 cv = function(v){ return v === true || v === "true" || v == 1; };
19875                 break;
19876             case "date":
19877                 cv = function(v){
19878                     if(!v){
19879                         return '';
19880                     }
19881                     if(v instanceof Date){
19882                         return v;
19883                     }
19884                     if(dateFormat){
19885                         if(dateFormat == "timestamp"){
19886                             return new Date(v*1000);
19887                         }
19888                         return Date.parseDate(v, dateFormat);
19889                     }
19890                     var parsed = Date.parse(v);
19891                     return parsed ? new Date(parsed) : null;
19892                 };
19893              break;
19894             
19895         }
19896         this.convert = cv;
19897     }
19898 };
19899
19900 Roo.data.Field.prototype = {
19901     dateFormat: null,
19902     defaultValue: "",
19903     mapping: null,
19904     sortType : null,
19905     sortDir : "ASC"
19906 };/*
19907  * Based on:
19908  * Ext JS Library 1.1.1
19909  * Copyright(c) 2006-2007, Ext JS, LLC.
19910  *
19911  * Originally Released Under LGPL - original licence link has changed is not relivant.
19912  *
19913  * Fork - LGPL
19914  * <script type="text/javascript">
19915  */
19916  
19917 // Base class for reading structured data from a data source.  This class is intended to be
19918 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19919
19920 /**
19921  * @class Roo.data.DataReader
19922  * Base class for reading structured data from a data source.  This class is intended to be
19923  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19924  */
19925
19926 Roo.data.DataReader = function(meta, recordType){
19927     
19928     this.meta = meta;
19929     
19930     this.recordType = recordType instanceof Array ? 
19931         Roo.data.Record.create(recordType) : recordType;
19932 };
19933
19934 Roo.data.DataReader.prototype = {
19935      /**
19936      * Create an empty record
19937      * @param {Object} data (optional) - overlay some values
19938      * @return {Roo.data.Record} record created.
19939      */
19940     newRow :  function(d) {
19941         var da =  {};
19942         this.recordType.prototype.fields.each(function(c) {
19943             switch( c.type) {
19944                 case 'int' : da[c.name] = 0; break;
19945                 case 'date' : da[c.name] = new Date(); break;
19946                 case 'float' : da[c.name] = 0.0; break;
19947                 case 'boolean' : da[c.name] = false; break;
19948                 default : da[c.name] = ""; break;
19949             }
19950             
19951         });
19952         return new this.recordType(Roo.apply(da, d));
19953     }
19954     
19955 };/*
19956  * Based on:
19957  * Ext JS Library 1.1.1
19958  * Copyright(c) 2006-2007, Ext JS, LLC.
19959  *
19960  * Originally Released Under LGPL - original licence link has changed is not relivant.
19961  *
19962  * Fork - LGPL
19963  * <script type="text/javascript">
19964  */
19965
19966 /**
19967  * @class Roo.data.DataProxy
19968  * @extends Roo.data.Observable
19969  * This class is an abstract base class for implementations which provide retrieval of
19970  * unformatted data objects.<br>
19971  * <p>
19972  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19973  * (of the appropriate type which knows how to parse the data object) to provide a block of
19974  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19975  * <p>
19976  * Custom implementations must implement the load method as described in
19977  * {@link Roo.data.HttpProxy#load}.
19978  */
19979 Roo.data.DataProxy = function(){
19980     this.addEvents({
19981         /**
19982          * @event beforeload
19983          * Fires before a network request is made to retrieve a data object.
19984          * @param {Object} This DataProxy object.
19985          * @param {Object} params The params parameter to the load function.
19986          */
19987         beforeload : true,
19988         /**
19989          * @event load
19990          * Fires before the load method's callback is called.
19991          * @param {Object} This DataProxy object.
19992          * @param {Object} o The data object.
19993          * @param {Object} arg The callback argument object passed to the load function.
19994          */
19995         load : true,
19996         /**
19997          * @event loadexception
19998          * Fires if an Exception occurs during data retrieval.
19999          * @param {Object} This DataProxy object.
20000          * @param {Object} o The data object.
20001          * @param {Object} arg The callback argument object passed to the load function.
20002          * @param {Object} e The Exception.
20003          */
20004         loadexception : true
20005     });
20006     Roo.data.DataProxy.superclass.constructor.call(this);
20007 };
20008
20009 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20010
20011     /**
20012      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20013      */
20014 /*
20015  * Based on:
20016  * Ext JS Library 1.1.1
20017  * Copyright(c) 2006-2007, Ext JS, LLC.
20018  *
20019  * Originally Released Under LGPL - original licence link has changed is not relivant.
20020  *
20021  * Fork - LGPL
20022  * <script type="text/javascript">
20023  */
20024 /**
20025  * @class Roo.data.MemoryProxy
20026  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20027  * to the Reader when its load method is called.
20028  * @constructor
20029  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20030  */
20031 Roo.data.MemoryProxy = function(data){
20032     if (data.data) {
20033         data = data.data;
20034     }
20035     Roo.data.MemoryProxy.superclass.constructor.call(this);
20036     this.data = data;
20037 };
20038
20039 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20040     /**
20041      * Load data from the requested source (in this case an in-memory
20042      * data object passed to the constructor), read the data object into
20043      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20044      * process that block using the passed callback.
20045      * @param {Object} params This parameter is not used by the MemoryProxy class.
20046      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20047      * object into a block of Roo.data.Records.
20048      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20049      * The function must be passed <ul>
20050      * <li>The Record block object</li>
20051      * <li>The "arg" argument from the load function</li>
20052      * <li>A boolean success indicator</li>
20053      * </ul>
20054      * @param {Object} scope The scope in which to call the callback
20055      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20056      */
20057     load : function(params, reader, callback, scope, arg){
20058         params = params || {};
20059         var result;
20060         try {
20061             result = reader.readRecords(this.data);
20062         }catch(e){
20063             this.fireEvent("loadexception", this, arg, null, e);
20064             callback.call(scope, null, arg, false);
20065             return;
20066         }
20067         callback.call(scope, result, arg, true);
20068     },
20069     
20070     // private
20071     update : function(params, records){
20072         
20073     }
20074 });/*
20075  * Based on:
20076  * Ext JS Library 1.1.1
20077  * Copyright(c) 2006-2007, Ext JS, LLC.
20078  *
20079  * Originally Released Under LGPL - original licence link has changed is not relivant.
20080  *
20081  * Fork - LGPL
20082  * <script type="text/javascript">
20083  */
20084 /**
20085  * @class Roo.data.HttpProxy
20086  * @extends Roo.data.DataProxy
20087  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20088  * configured to reference a certain URL.<br><br>
20089  * <p>
20090  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20091  * from which the running page was served.<br><br>
20092  * <p>
20093  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20094  * <p>
20095  * Be aware that to enable the browser to parse an XML document, the server must set
20096  * the Content-Type header in the HTTP response to "text/xml".
20097  * @constructor
20098  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20099  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20100  * will be used to make the request.
20101  */
20102 Roo.data.HttpProxy = function(conn){
20103     Roo.data.HttpProxy.superclass.constructor.call(this);
20104     // is conn a conn config or a real conn?
20105     this.conn = conn;
20106     this.useAjax = !conn || !conn.events;
20107   
20108 };
20109
20110 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20111     // thse are take from connection...
20112     
20113     /**
20114      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20115      */
20116     /**
20117      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20118      * extra parameters to each request made by this object. (defaults to undefined)
20119      */
20120     /**
20121      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20122      *  to each request made by this object. (defaults to undefined)
20123      */
20124     /**
20125      * @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)
20126      */
20127     /**
20128      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20129      */
20130      /**
20131      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20132      * @type Boolean
20133      */
20134   
20135
20136     /**
20137      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20138      * @type Boolean
20139      */
20140     /**
20141      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20142      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20143      * a finer-grained basis than the DataProxy events.
20144      */
20145     getConnection : function(){
20146         return this.useAjax ? Roo.Ajax : this.conn;
20147     },
20148
20149     /**
20150      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20151      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20152      * process that block using the passed callback.
20153      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20154      * for the request to the remote server.
20155      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20156      * object into a block of Roo.data.Records.
20157      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20158      * The function must be passed <ul>
20159      * <li>The Record block object</li>
20160      * <li>The "arg" argument from the load function</li>
20161      * <li>A boolean success indicator</li>
20162      * </ul>
20163      * @param {Object} scope The scope in which to call the callback
20164      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20165      */
20166     load : function(params, reader, callback, scope, arg){
20167         if(this.fireEvent("beforeload", this, params) !== false){
20168             var  o = {
20169                 params : params || {},
20170                 request: {
20171                     callback : callback,
20172                     scope : scope,
20173                     arg : arg
20174                 },
20175                 reader: reader,
20176                 callback : this.loadResponse,
20177                 scope: this
20178             };
20179             if(this.useAjax){
20180                 Roo.applyIf(o, this.conn);
20181                 if(this.activeRequest){
20182                     Roo.Ajax.abort(this.activeRequest);
20183                 }
20184                 this.activeRequest = Roo.Ajax.request(o);
20185             }else{
20186                 this.conn.request(o);
20187             }
20188         }else{
20189             callback.call(scope||this, null, arg, false);
20190         }
20191     },
20192
20193     // private
20194     loadResponse : function(o, success, response){
20195         delete this.activeRequest;
20196         if(!success){
20197             this.fireEvent("loadexception", this, o, response);
20198             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20199             return;
20200         }
20201         var result;
20202         try {
20203             result = o.reader.read(response);
20204         }catch(e){
20205             this.fireEvent("loadexception", this, o, response, e);
20206             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20207             return;
20208         }
20209         
20210         this.fireEvent("load", this, o, o.request.arg);
20211         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20212     },
20213
20214     // private
20215     update : function(dataSet){
20216
20217     },
20218
20219     // private
20220     updateResponse : function(dataSet){
20221
20222     }
20223 });/*
20224  * Based on:
20225  * Ext JS Library 1.1.1
20226  * Copyright(c) 2006-2007, Ext JS, LLC.
20227  *
20228  * Originally Released Under LGPL - original licence link has changed is not relivant.
20229  *
20230  * Fork - LGPL
20231  * <script type="text/javascript">
20232  */
20233
20234 /**
20235  * @class Roo.data.ScriptTagProxy
20236  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20237  * other than the originating domain of the running page.<br><br>
20238  * <p>
20239  * <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
20240  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20241  * <p>
20242  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20243  * source code that is used as the source inside a &lt;script> tag.<br><br>
20244  * <p>
20245  * In order for the browser to process the returned data, the server must wrap the data object
20246  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20247  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20248  * depending on whether the callback name was passed:
20249  * <p>
20250  * <pre><code>
20251 boolean scriptTag = false;
20252 String cb = request.getParameter("callback");
20253 if (cb != null) {
20254     scriptTag = true;
20255     response.setContentType("text/javascript");
20256 } else {
20257     response.setContentType("application/x-json");
20258 }
20259 Writer out = response.getWriter();
20260 if (scriptTag) {
20261     out.write(cb + "(");
20262 }
20263 out.print(dataBlock.toJsonString());
20264 if (scriptTag) {
20265     out.write(");");
20266 }
20267 </pre></code>
20268  *
20269  * @constructor
20270  * @param {Object} config A configuration object.
20271  */
20272 Roo.data.ScriptTagProxy = function(config){
20273     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20274     Roo.apply(this, config);
20275     this.head = document.getElementsByTagName("head")[0];
20276 };
20277
20278 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20279
20280 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20281     /**
20282      * @cfg {String} url The URL from which to request the data object.
20283      */
20284     /**
20285      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20286      */
20287     timeout : 30000,
20288     /**
20289      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20290      * the server the name of the callback function set up by the load call to process the returned data object.
20291      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20292      * javascript output which calls this named function passing the data object as its only parameter.
20293      */
20294     callbackParam : "callback",
20295     /**
20296      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20297      * name to the request.
20298      */
20299     nocache : true,
20300
20301     /**
20302      * Load data from the configured URL, read the data object into
20303      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20304      * process that block using the passed callback.
20305      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20306      * for the request to the remote server.
20307      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20308      * object into a block of Roo.data.Records.
20309      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20310      * The function must be passed <ul>
20311      * <li>The Record block object</li>
20312      * <li>The "arg" argument from the load function</li>
20313      * <li>A boolean success indicator</li>
20314      * </ul>
20315      * @param {Object} scope The scope in which to call the callback
20316      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20317      */
20318     load : function(params, reader, callback, scope, arg){
20319         if(this.fireEvent("beforeload", this, params) !== false){
20320
20321             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20322
20323             var url = this.url;
20324             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20325             if(this.nocache){
20326                 url += "&_dc=" + (new Date().getTime());
20327             }
20328             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20329             var trans = {
20330                 id : transId,
20331                 cb : "stcCallback"+transId,
20332                 scriptId : "stcScript"+transId,
20333                 params : params,
20334                 arg : arg,
20335                 url : url,
20336                 callback : callback,
20337                 scope : scope,
20338                 reader : reader
20339             };
20340             var conn = this;
20341
20342             window[trans.cb] = function(o){
20343                 conn.handleResponse(o, trans);
20344             };
20345
20346             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20347
20348             if(this.autoAbort !== false){
20349                 this.abort();
20350             }
20351
20352             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20353
20354             var script = document.createElement("script");
20355             script.setAttribute("src", url);
20356             script.setAttribute("type", "text/javascript");
20357             script.setAttribute("id", trans.scriptId);
20358             this.head.appendChild(script);
20359
20360             this.trans = trans;
20361         }else{
20362             callback.call(scope||this, null, arg, false);
20363         }
20364     },
20365
20366     // private
20367     isLoading : function(){
20368         return this.trans ? true : false;
20369     },
20370
20371     /**
20372      * Abort the current server request.
20373      */
20374     abort : function(){
20375         if(this.isLoading()){
20376             this.destroyTrans(this.trans);
20377         }
20378     },
20379
20380     // private
20381     destroyTrans : function(trans, isLoaded){
20382         this.head.removeChild(document.getElementById(trans.scriptId));
20383         clearTimeout(trans.timeoutId);
20384         if(isLoaded){
20385             window[trans.cb] = undefined;
20386             try{
20387                 delete window[trans.cb];
20388             }catch(e){}
20389         }else{
20390             // if hasn't been loaded, wait for load to remove it to prevent script error
20391             window[trans.cb] = function(){
20392                 window[trans.cb] = undefined;
20393                 try{
20394                     delete window[trans.cb];
20395                 }catch(e){}
20396             };
20397         }
20398     },
20399
20400     // private
20401     handleResponse : function(o, trans){
20402         this.trans = false;
20403         this.destroyTrans(trans, true);
20404         var result;
20405         try {
20406             result = trans.reader.readRecords(o);
20407         }catch(e){
20408             this.fireEvent("loadexception", this, o, trans.arg, e);
20409             trans.callback.call(trans.scope||window, null, trans.arg, false);
20410             return;
20411         }
20412         this.fireEvent("load", this, o, trans.arg);
20413         trans.callback.call(trans.scope||window, result, trans.arg, true);
20414     },
20415
20416     // private
20417     handleFailure : function(trans){
20418         this.trans = false;
20419         this.destroyTrans(trans, false);
20420         this.fireEvent("loadexception", this, null, trans.arg);
20421         trans.callback.call(trans.scope||window, null, trans.arg, false);
20422     }
20423 });/*
20424  * Based on:
20425  * Ext JS Library 1.1.1
20426  * Copyright(c) 2006-2007, Ext JS, LLC.
20427  *
20428  * Originally Released Under LGPL - original licence link has changed is not relivant.
20429  *
20430  * Fork - LGPL
20431  * <script type="text/javascript">
20432  */
20433
20434 /**
20435  * @class Roo.data.JsonReader
20436  * @extends Roo.data.DataReader
20437  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20438  * based on mappings in a provided Roo.data.Record constructor.
20439  * 
20440  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20441  * in the reply previously. 
20442  * 
20443  * <p>
20444  * Example code:
20445  * <pre><code>
20446 var RecordDef = Roo.data.Record.create([
20447     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20448     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20449 ]);
20450 var myReader = new Roo.data.JsonReader({
20451     totalProperty: "results",    // The property which contains the total dataset size (optional)
20452     root: "rows",                // The property which contains an Array of row objects
20453     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20454 }, RecordDef);
20455 </code></pre>
20456  * <p>
20457  * This would consume a JSON file like this:
20458  * <pre><code>
20459 { 'results': 2, 'rows': [
20460     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20461     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20462 }
20463 </code></pre>
20464  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20465  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20466  * paged from the remote server.
20467  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20468  * @cfg {String} root name of the property which contains the Array of row objects.
20469  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20470  * @constructor
20471  * Create a new JsonReader
20472  * @param {Object} meta Metadata configuration options
20473  * @param {Object} recordType Either an Array of field definition objects,
20474  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20475  */
20476 Roo.data.JsonReader = function(meta, recordType){
20477     
20478     meta = meta || {};
20479     // set some defaults:
20480     Roo.applyIf(meta, {
20481         totalProperty: 'total',
20482         successProperty : 'success',
20483         root : 'data',
20484         id : 'id'
20485     });
20486     
20487     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20488 };
20489 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20490     
20491     /**
20492      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20493      * Used by Store query builder to append _requestMeta to params.
20494      * 
20495      */
20496     metaFromRemote : false,
20497     /**
20498      * This method is only used by a DataProxy which has retrieved data from a remote server.
20499      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20500      * @return {Object} data A data block which is used by an Roo.data.Store object as
20501      * a cache of Roo.data.Records.
20502      */
20503     read : function(response){
20504         var json = response.responseText;
20505        
20506         var o = /* eval:var:o */ eval("("+json+")");
20507         if(!o) {
20508             throw {message: "JsonReader.read: Json object not found"};
20509         }
20510         
20511         if(o.metaData){
20512             
20513             delete this.ef;
20514             this.metaFromRemote = true;
20515             this.meta = o.metaData;
20516             this.recordType = Roo.data.Record.create(o.metaData.fields);
20517             this.onMetaChange(this.meta, this.recordType, o);
20518         }
20519         return this.readRecords(o);
20520     },
20521
20522     // private function a store will implement
20523     onMetaChange : function(meta, recordType, o){
20524
20525     },
20526
20527     /**
20528          * @ignore
20529          */
20530     simpleAccess: function(obj, subsc) {
20531         return obj[subsc];
20532     },
20533
20534         /**
20535          * @ignore
20536          */
20537     getJsonAccessor: function(){
20538         var re = /[\[\.]/;
20539         return function(expr) {
20540             try {
20541                 return(re.test(expr))
20542                     ? new Function("obj", "return obj." + expr)
20543                     : function(obj){
20544                         return obj[expr];
20545                     };
20546             } catch(e){}
20547             return Roo.emptyFn;
20548         };
20549     }(),
20550
20551     /**
20552      * Create a data block containing Roo.data.Records from an XML document.
20553      * @param {Object} o An object which contains an Array of row objects in the property specified
20554      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20555      * which contains the total size of the dataset.
20556      * @return {Object} data A data block which is used by an Roo.data.Store object as
20557      * a cache of Roo.data.Records.
20558      */
20559     readRecords : function(o){
20560         /**
20561          * After any data loads, the raw JSON data is available for further custom processing.
20562          * @type Object
20563          */
20564         this.jsonData = o;
20565         var s = this.meta, Record = this.recordType,
20566             f = Record.prototype.fields, fi = f.items, fl = f.length;
20567
20568 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20569         if (!this.ef) {
20570             if(s.totalProperty) {
20571                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20572                 }
20573                 if(s.successProperty) {
20574                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20575                 }
20576                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20577                 if (s.id) {
20578                         var g = this.getJsonAccessor(s.id);
20579                         this.getId = function(rec) {
20580                                 var r = g(rec);
20581                                 return (r === undefined || r === "") ? null : r;
20582                         };
20583                 } else {
20584                         this.getId = function(){return null;};
20585                 }
20586             this.ef = [];
20587             for(var jj = 0; jj < fl; jj++){
20588                 f = fi[jj];
20589                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20590                 this.ef[jj] = this.getJsonAccessor(map);
20591             }
20592         }
20593
20594         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20595         if(s.totalProperty){
20596             var vt = parseInt(this.getTotal(o), 10);
20597             if(!isNaN(vt)){
20598                 totalRecords = vt;
20599             }
20600         }
20601         if(s.successProperty){
20602             var vs = this.getSuccess(o);
20603             if(vs === false || vs === 'false'){
20604                 success = false;
20605             }
20606         }
20607         var records = [];
20608             for(var i = 0; i < c; i++){
20609                     var n = root[i];
20610                 var values = {};
20611                 var id = this.getId(n);
20612                 for(var j = 0; j < fl; j++){
20613                     f = fi[j];
20614                 var v = this.ef[j](n);
20615                 if (!f.convert) {
20616                     Roo.log('missing convert for ' + f.name);
20617                     Roo.log(f);
20618                     continue;
20619                 }
20620                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20621                 }
20622                 var record = new Record(values, id);
20623                 record.json = n;
20624                 records[i] = record;
20625             }
20626             return {
20627                 success : success,
20628                 records : records,
20629                 totalRecords : totalRecords
20630             };
20631     }
20632 });/*
20633  * Based on:
20634  * Ext JS Library 1.1.1
20635  * Copyright(c) 2006-2007, Ext JS, LLC.
20636  *
20637  * Originally Released Under LGPL - original licence link has changed is not relivant.
20638  *
20639  * Fork - LGPL
20640  * <script type="text/javascript">
20641  */
20642
20643 /**
20644  * @class Roo.data.XmlReader
20645  * @extends Roo.data.DataReader
20646  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20647  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20648  * <p>
20649  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20650  * header in the HTTP response must be set to "text/xml".</em>
20651  * <p>
20652  * Example code:
20653  * <pre><code>
20654 var RecordDef = Roo.data.Record.create([
20655    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20656    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20657 ]);
20658 var myReader = new Roo.data.XmlReader({
20659    totalRecords: "results", // The element which contains the total dataset size (optional)
20660    record: "row",           // The repeated element which contains row information
20661    id: "id"                 // The element within the row that provides an ID for the record (optional)
20662 }, RecordDef);
20663 </code></pre>
20664  * <p>
20665  * This would consume an XML file like this:
20666  * <pre><code>
20667 &lt;?xml?>
20668 &lt;dataset>
20669  &lt;results>2&lt;/results>
20670  &lt;row>
20671    &lt;id>1&lt;/id>
20672    &lt;name>Bill&lt;/name>
20673    &lt;occupation>Gardener&lt;/occupation>
20674  &lt;/row>
20675  &lt;row>
20676    &lt;id>2&lt;/id>
20677    &lt;name>Ben&lt;/name>
20678    &lt;occupation>Horticulturalist&lt;/occupation>
20679  &lt;/row>
20680 &lt;/dataset>
20681 </code></pre>
20682  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20683  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20684  * paged from the remote server.
20685  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20686  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20687  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20688  * a record identifier value.
20689  * @constructor
20690  * Create a new XmlReader
20691  * @param {Object} meta Metadata configuration options
20692  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20693  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20694  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20695  */
20696 Roo.data.XmlReader = function(meta, recordType){
20697     meta = meta || {};
20698     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20699 };
20700 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20701     /**
20702      * This method is only used by a DataProxy which has retrieved data from a remote server.
20703          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20704          * to contain a method called 'responseXML' that returns an XML document object.
20705      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20706      * a cache of Roo.data.Records.
20707      */
20708     read : function(response){
20709         var doc = response.responseXML;
20710         if(!doc) {
20711             throw {message: "XmlReader.read: XML Document not available"};
20712         }
20713         return this.readRecords(doc);
20714     },
20715
20716     /**
20717      * Create a data block containing Roo.data.Records from an XML document.
20718          * @param {Object} doc A parsed XML document.
20719      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20720      * a cache of Roo.data.Records.
20721      */
20722     readRecords : function(doc){
20723         /**
20724          * After any data loads/reads, the raw XML Document is available for further custom processing.
20725          * @type XMLDocument
20726          */
20727         this.xmlData = doc;
20728         var root = doc.documentElement || doc;
20729         var q = Roo.DomQuery;
20730         var recordType = this.recordType, fields = recordType.prototype.fields;
20731         var sid = this.meta.id;
20732         var totalRecords = 0, success = true;
20733         if(this.meta.totalRecords){
20734             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20735         }
20736         
20737         if(this.meta.success){
20738             var sv = q.selectValue(this.meta.success, root, true);
20739             success = sv !== false && sv !== 'false';
20740         }
20741         var records = [];
20742         var ns = q.select(this.meta.record, root);
20743         for(var i = 0, len = ns.length; i < len; i++) {
20744                 var n = ns[i];
20745                 var values = {};
20746                 var id = sid ? q.selectValue(sid, n) : undefined;
20747                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20748                     var f = fields.items[j];
20749                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20750                     v = f.convert(v);
20751                     values[f.name] = v;
20752                 }
20753                 var record = new recordType(values, id);
20754                 record.node = n;
20755                 records[records.length] = record;
20756             }
20757
20758             return {
20759                 success : success,
20760                 records : records,
20761                 totalRecords : totalRecords || records.length
20762             };
20763     }
20764 });/*
20765  * Based on:
20766  * Ext JS Library 1.1.1
20767  * Copyright(c) 2006-2007, Ext JS, LLC.
20768  *
20769  * Originally Released Under LGPL - original licence link has changed is not relivant.
20770  *
20771  * Fork - LGPL
20772  * <script type="text/javascript">
20773  */
20774
20775 /**
20776  * @class Roo.data.ArrayReader
20777  * @extends Roo.data.DataReader
20778  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20779  * Each element of that Array represents a row of data fields. The
20780  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20781  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20782  * <p>
20783  * Example code:.
20784  * <pre><code>
20785 var RecordDef = Roo.data.Record.create([
20786     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20787     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20788 ]);
20789 var myReader = new Roo.data.ArrayReader({
20790     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20791 }, RecordDef);
20792 </code></pre>
20793  * <p>
20794  * This would consume an Array like this:
20795  * <pre><code>
20796 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20797   </code></pre>
20798  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20799  * @constructor
20800  * Create a new JsonReader
20801  * @param {Object} meta Metadata configuration options.
20802  * @param {Object} recordType Either an Array of field definition objects
20803  * as specified to {@link Roo.data.Record#create},
20804  * or an {@link Roo.data.Record} object
20805  * created using {@link Roo.data.Record#create}.
20806  */
20807 Roo.data.ArrayReader = function(meta, recordType){
20808     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20809 };
20810
20811 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20812     /**
20813      * Create a data block containing Roo.data.Records from an XML document.
20814      * @param {Object} o An Array of row objects which represents the dataset.
20815      * @return {Object} data A data block which is used by an Roo.data.Store object as
20816      * a cache of Roo.data.Records.
20817      */
20818     readRecords : function(o){
20819         var sid = this.meta ? this.meta.id : null;
20820         var recordType = this.recordType, fields = recordType.prototype.fields;
20821         var records = [];
20822         var root = o;
20823             for(var i = 0; i < root.length; i++){
20824                     var n = root[i];
20825                 var values = {};
20826                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20827                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20828                 var f = fields.items[j];
20829                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20830                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20831                 v = f.convert(v);
20832                 values[f.name] = v;
20833             }
20834                 var record = new recordType(values, id);
20835                 record.json = n;
20836                 records[records.length] = record;
20837             }
20838             return {
20839                 records : records,
20840                 totalRecords : records.length
20841             };
20842     }
20843 });/*
20844  * Based on:
20845  * Ext JS Library 1.1.1
20846  * Copyright(c) 2006-2007, Ext JS, LLC.
20847  *
20848  * Originally Released Under LGPL - original licence link has changed is not relivant.
20849  *
20850  * Fork - LGPL
20851  * <script type="text/javascript">
20852  */
20853
20854
20855 /**
20856  * @class Roo.data.Tree
20857  * @extends Roo.util.Observable
20858  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20859  * in the tree have most standard DOM functionality.
20860  * @constructor
20861  * @param {Node} root (optional) The root node
20862  */
20863 Roo.data.Tree = function(root){
20864    this.nodeHash = {};
20865    /**
20866     * The root node for this tree
20867     * @type Node
20868     */
20869    this.root = null;
20870    if(root){
20871        this.setRootNode(root);
20872    }
20873    this.addEvents({
20874        /**
20875         * @event append
20876         * Fires when a new child node is appended to a node in this tree.
20877         * @param {Tree} tree The owner tree
20878         * @param {Node} parent The parent node
20879         * @param {Node} node The newly appended node
20880         * @param {Number} index The index of the newly appended node
20881         */
20882        "append" : true,
20883        /**
20884         * @event remove
20885         * Fires when a child node is removed from a node in this tree.
20886         * @param {Tree} tree The owner tree
20887         * @param {Node} parent The parent node
20888         * @param {Node} node The child node removed
20889         */
20890        "remove" : true,
20891        /**
20892         * @event move
20893         * Fires when a node is moved to a new location in the tree
20894         * @param {Tree} tree The owner tree
20895         * @param {Node} node The node moved
20896         * @param {Node} oldParent The old parent of this node
20897         * @param {Node} newParent The new parent of this node
20898         * @param {Number} index The index it was moved to
20899         */
20900        "move" : true,
20901        /**
20902         * @event insert
20903         * Fires when a new child node is inserted in a node in this tree.
20904         * @param {Tree} tree The owner tree
20905         * @param {Node} parent The parent node
20906         * @param {Node} node The child node inserted
20907         * @param {Node} refNode The child node the node was inserted before
20908         */
20909        "insert" : true,
20910        /**
20911         * @event beforeappend
20912         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20913         * @param {Tree} tree The owner tree
20914         * @param {Node} parent The parent node
20915         * @param {Node} node The child node to be appended
20916         */
20917        "beforeappend" : true,
20918        /**
20919         * @event beforeremove
20920         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20921         * @param {Tree} tree The owner tree
20922         * @param {Node} parent The parent node
20923         * @param {Node} node The child node to be removed
20924         */
20925        "beforeremove" : true,
20926        /**
20927         * @event beforemove
20928         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20929         * @param {Tree} tree The owner tree
20930         * @param {Node} node The node being moved
20931         * @param {Node} oldParent The parent of the node
20932         * @param {Node} newParent The new parent the node is moving to
20933         * @param {Number} index The index it is being moved to
20934         */
20935        "beforemove" : true,
20936        /**
20937         * @event beforeinsert
20938         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20939         * @param {Tree} tree The owner tree
20940         * @param {Node} parent The parent node
20941         * @param {Node} node The child node to be inserted
20942         * @param {Node} refNode The child node the node is being inserted before
20943         */
20944        "beforeinsert" : true
20945    });
20946
20947     Roo.data.Tree.superclass.constructor.call(this);
20948 };
20949
20950 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20951     pathSeparator: "/",
20952
20953     proxyNodeEvent : function(){
20954         return this.fireEvent.apply(this, arguments);
20955     },
20956
20957     /**
20958      * Returns the root node for this tree.
20959      * @return {Node}
20960      */
20961     getRootNode : function(){
20962         return this.root;
20963     },
20964
20965     /**
20966      * Sets the root node for this tree.
20967      * @param {Node} node
20968      * @return {Node}
20969      */
20970     setRootNode : function(node){
20971         this.root = node;
20972         node.ownerTree = this;
20973         node.isRoot = true;
20974         this.registerNode(node);
20975         return node;
20976     },
20977
20978     /**
20979      * Gets a node in this tree by its id.
20980      * @param {String} id
20981      * @return {Node}
20982      */
20983     getNodeById : function(id){
20984         return this.nodeHash[id];
20985     },
20986
20987     registerNode : function(node){
20988         this.nodeHash[node.id] = node;
20989     },
20990
20991     unregisterNode : function(node){
20992         delete this.nodeHash[node.id];
20993     },
20994
20995     toString : function(){
20996         return "[Tree"+(this.id?" "+this.id:"")+"]";
20997     }
20998 });
20999
21000 /**
21001  * @class Roo.data.Node
21002  * @extends Roo.util.Observable
21003  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21004  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21005  * @constructor
21006  * @param {Object} attributes The attributes/config for the node
21007  */
21008 Roo.data.Node = function(attributes){
21009     /**
21010      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21011      * @type {Object}
21012      */
21013     this.attributes = attributes || {};
21014     this.leaf = this.attributes.leaf;
21015     /**
21016      * The node id. @type String
21017      */
21018     this.id = this.attributes.id;
21019     if(!this.id){
21020         this.id = Roo.id(null, "ynode-");
21021         this.attributes.id = this.id;
21022     }
21023     /**
21024      * All child nodes of this node. @type Array
21025      */
21026     this.childNodes = [];
21027     if(!this.childNodes.indexOf){ // indexOf is a must
21028         this.childNodes.indexOf = function(o){
21029             for(var i = 0, len = this.length; i < len; i++){
21030                 if(this[i] == o) {
21031                     return i;
21032                 }
21033             }
21034             return -1;
21035         };
21036     }
21037     /**
21038      * The parent node for this node. @type Node
21039      */
21040     this.parentNode = null;
21041     /**
21042      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21043      */
21044     this.firstChild = null;
21045     /**
21046      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21047      */
21048     this.lastChild = null;
21049     /**
21050      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21051      */
21052     this.previousSibling = null;
21053     /**
21054      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21055      */
21056     this.nextSibling = null;
21057
21058     this.addEvents({
21059        /**
21060         * @event append
21061         * Fires when a new child node is appended
21062         * @param {Tree} tree The owner tree
21063         * @param {Node} this This node
21064         * @param {Node} node The newly appended node
21065         * @param {Number} index The index of the newly appended node
21066         */
21067        "append" : true,
21068        /**
21069         * @event remove
21070         * Fires when a child node is removed
21071         * @param {Tree} tree The owner tree
21072         * @param {Node} this This node
21073         * @param {Node} node The removed node
21074         */
21075        "remove" : true,
21076        /**
21077         * @event move
21078         * Fires when this node is moved to a new location in the tree
21079         * @param {Tree} tree The owner tree
21080         * @param {Node} this This node
21081         * @param {Node} oldParent The old parent of this node
21082         * @param {Node} newParent The new parent of this node
21083         * @param {Number} index The index it was moved to
21084         */
21085        "move" : true,
21086        /**
21087         * @event insert
21088         * Fires when a new child node is inserted.
21089         * @param {Tree} tree The owner tree
21090         * @param {Node} this This node
21091         * @param {Node} node The child node inserted
21092         * @param {Node} refNode The child node the node was inserted before
21093         */
21094        "insert" : true,
21095        /**
21096         * @event beforeappend
21097         * Fires before a new child is appended, return false to cancel the append.
21098         * @param {Tree} tree The owner tree
21099         * @param {Node} this This node
21100         * @param {Node} node The child node to be appended
21101         */
21102        "beforeappend" : true,
21103        /**
21104         * @event beforeremove
21105         * Fires before a child is removed, return false to cancel the remove.
21106         * @param {Tree} tree The owner tree
21107         * @param {Node} this This node
21108         * @param {Node} node The child node to be removed
21109         */
21110        "beforeremove" : true,
21111        /**
21112         * @event beforemove
21113         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21114         * @param {Tree} tree The owner tree
21115         * @param {Node} this This node
21116         * @param {Node} oldParent The parent of this node
21117         * @param {Node} newParent The new parent this node is moving to
21118         * @param {Number} index The index it is being moved to
21119         */
21120        "beforemove" : true,
21121        /**
21122         * @event beforeinsert
21123         * Fires before a new child is inserted, return false to cancel the insert.
21124         * @param {Tree} tree The owner tree
21125         * @param {Node} this This node
21126         * @param {Node} node The child node to be inserted
21127         * @param {Node} refNode The child node the node is being inserted before
21128         */
21129        "beforeinsert" : true
21130    });
21131     this.listeners = this.attributes.listeners;
21132     Roo.data.Node.superclass.constructor.call(this);
21133 };
21134
21135 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21136     fireEvent : function(evtName){
21137         // first do standard event for this node
21138         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21139             return false;
21140         }
21141         // then bubble it up to the tree if the event wasn't cancelled
21142         var ot = this.getOwnerTree();
21143         if(ot){
21144             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21145                 return false;
21146             }
21147         }
21148         return true;
21149     },
21150
21151     /**
21152      * Returns true if this node is a leaf
21153      * @return {Boolean}
21154      */
21155     isLeaf : function(){
21156         return this.leaf === true;
21157     },
21158
21159     // private
21160     setFirstChild : function(node){
21161         this.firstChild = node;
21162     },
21163
21164     //private
21165     setLastChild : function(node){
21166         this.lastChild = node;
21167     },
21168
21169
21170     /**
21171      * Returns true if this node is the last child of its parent
21172      * @return {Boolean}
21173      */
21174     isLast : function(){
21175        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21176     },
21177
21178     /**
21179      * Returns true if this node is the first child of its parent
21180      * @return {Boolean}
21181      */
21182     isFirst : function(){
21183        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21184     },
21185
21186     hasChildNodes : function(){
21187         return !this.isLeaf() && this.childNodes.length > 0;
21188     },
21189
21190     /**
21191      * Insert node(s) as the last child node of this node.
21192      * @param {Node/Array} node The node or Array of nodes to append
21193      * @return {Node} The appended node if single append, or null if an array was passed
21194      */
21195     appendChild : function(node){
21196         var multi = false;
21197         if(node instanceof Array){
21198             multi = node;
21199         }else if(arguments.length > 1){
21200             multi = arguments;
21201         }
21202         // if passed an array or multiple args do them one by one
21203         if(multi){
21204             for(var i = 0, len = multi.length; i < len; i++) {
21205                 this.appendChild(multi[i]);
21206             }
21207         }else{
21208             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21209                 return false;
21210             }
21211             var index = this.childNodes.length;
21212             var oldParent = node.parentNode;
21213             // it's a move, make sure we move it cleanly
21214             if(oldParent){
21215                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21216                     return false;
21217                 }
21218                 oldParent.removeChild(node);
21219             }
21220             index = this.childNodes.length;
21221             if(index == 0){
21222                 this.setFirstChild(node);
21223             }
21224             this.childNodes.push(node);
21225             node.parentNode = this;
21226             var ps = this.childNodes[index-1];
21227             if(ps){
21228                 node.previousSibling = ps;
21229                 ps.nextSibling = node;
21230             }else{
21231                 node.previousSibling = null;
21232             }
21233             node.nextSibling = null;
21234             this.setLastChild(node);
21235             node.setOwnerTree(this.getOwnerTree());
21236             this.fireEvent("append", this.ownerTree, this, node, index);
21237             if(oldParent){
21238                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21239             }
21240             return node;
21241         }
21242     },
21243
21244     /**
21245      * Removes a child node from this node.
21246      * @param {Node} node The node to remove
21247      * @return {Node} The removed node
21248      */
21249     removeChild : function(node){
21250         var index = this.childNodes.indexOf(node);
21251         if(index == -1){
21252             return false;
21253         }
21254         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21255             return false;
21256         }
21257
21258         // remove it from childNodes collection
21259         this.childNodes.splice(index, 1);
21260
21261         // update siblings
21262         if(node.previousSibling){
21263             node.previousSibling.nextSibling = node.nextSibling;
21264         }
21265         if(node.nextSibling){
21266             node.nextSibling.previousSibling = node.previousSibling;
21267         }
21268
21269         // update child refs
21270         if(this.firstChild == node){
21271             this.setFirstChild(node.nextSibling);
21272         }
21273         if(this.lastChild == node){
21274             this.setLastChild(node.previousSibling);
21275         }
21276
21277         node.setOwnerTree(null);
21278         // clear any references from the node
21279         node.parentNode = null;
21280         node.previousSibling = null;
21281         node.nextSibling = null;
21282         this.fireEvent("remove", this.ownerTree, this, node);
21283         return node;
21284     },
21285
21286     /**
21287      * Inserts the first node before the second node in this nodes childNodes collection.
21288      * @param {Node} node The node to insert
21289      * @param {Node} refNode The node to insert before (if null the node is appended)
21290      * @return {Node} The inserted node
21291      */
21292     insertBefore : function(node, refNode){
21293         if(!refNode){ // like standard Dom, refNode can be null for append
21294             return this.appendChild(node);
21295         }
21296         // nothing to do
21297         if(node == refNode){
21298             return false;
21299         }
21300
21301         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21302             return false;
21303         }
21304         var index = this.childNodes.indexOf(refNode);
21305         var oldParent = node.parentNode;
21306         var refIndex = index;
21307
21308         // when moving internally, indexes will change after remove
21309         if(oldParent == this && this.childNodes.indexOf(node) < index){
21310             refIndex--;
21311         }
21312
21313         // it's a move, make sure we move it cleanly
21314         if(oldParent){
21315             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21316                 return false;
21317             }
21318             oldParent.removeChild(node);
21319         }
21320         if(refIndex == 0){
21321             this.setFirstChild(node);
21322         }
21323         this.childNodes.splice(refIndex, 0, node);
21324         node.parentNode = this;
21325         var ps = this.childNodes[refIndex-1];
21326         if(ps){
21327             node.previousSibling = ps;
21328             ps.nextSibling = node;
21329         }else{
21330             node.previousSibling = null;
21331         }
21332         node.nextSibling = refNode;
21333         refNode.previousSibling = node;
21334         node.setOwnerTree(this.getOwnerTree());
21335         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21336         if(oldParent){
21337             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21338         }
21339         return node;
21340     },
21341
21342     /**
21343      * Returns the child node at the specified index.
21344      * @param {Number} index
21345      * @return {Node}
21346      */
21347     item : function(index){
21348         return this.childNodes[index];
21349     },
21350
21351     /**
21352      * Replaces one child node in this node with another.
21353      * @param {Node} newChild The replacement node
21354      * @param {Node} oldChild The node to replace
21355      * @return {Node} The replaced node
21356      */
21357     replaceChild : function(newChild, oldChild){
21358         this.insertBefore(newChild, oldChild);
21359         this.removeChild(oldChild);
21360         return oldChild;
21361     },
21362
21363     /**
21364      * Returns the index of a child node
21365      * @param {Node} node
21366      * @return {Number} The index of the node or -1 if it was not found
21367      */
21368     indexOf : function(child){
21369         return this.childNodes.indexOf(child);
21370     },
21371
21372     /**
21373      * Returns the tree this node is in.
21374      * @return {Tree}
21375      */
21376     getOwnerTree : function(){
21377         // if it doesn't have one, look for one
21378         if(!this.ownerTree){
21379             var p = this;
21380             while(p){
21381                 if(p.ownerTree){
21382                     this.ownerTree = p.ownerTree;
21383                     break;
21384                 }
21385                 p = p.parentNode;
21386             }
21387         }
21388         return this.ownerTree;
21389     },
21390
21391     /**
21392      * Returns depth of this node (the root node has a depth of 0)
21393      * @return {Number}
21394      */
21395     getDepth : function(){
21396         var depth = 0;
21397         var p = this;
21398         while(p.parentNode){
21399             ++depth;
21400             p = p.parentNode;
21401         }
21402         return depth;
21403     },
21404
21405     // private
21406     setOwnerTree : function(tree){
21407         // if it's move, we need to update everyone
21408         if(tree != this.ownerTree){
21409             if(this.ownerTree){
21410                 this.ownerTree.unregisterNode(this);
21411             }
21412             this.ownerTree = tree;
21413             var cs = this.childNodes;
21414             for(var i = 0, len = cs.length; i < len; i++) {
21415                 cs[i].setOwnerTree(tree);
21416             }
21417             if(tree){
21418                 tree.registerNode(this);
21419             }
21420         }
21421     },
21422
21423     /**
21424      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21425      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21426      * @return {String} The path
21427      */
21428     getPath : function(attr){
21429         attr = attr || "id";
21430         var p = this.parentNode;
21431         var b = [this.attributes[attr]];
21432         while(p){
21433             b.unshift(p.attributes[attr]);
21434             p = p.parentNode;
21435         }
21436         var sep = this.getOwnerTree().pathSeparator;
21437         return sep + b.join(sep);
21438     },
21439
21440     /**
21441      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21442      * function call will be the scope provided or the current node. The arguments to the function
21443      * will be the args provided or the current node. If the function returns false at any point,
21444      * the bubble is stopped.
21445      * @param {Function} fn The function to call
21446      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21447      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21448      */
21449     bubble : function(fn, scope, args){
21450         var p = this;
21451         while(p){
21452             if(fn.call(scope || p, args || p) === false){
21453                 break;
21454             }
21455             p = p.parentNode;
21456         }
21457     },
21458
21459     /**
21460      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21461      * function call will be the scope provided or the current node. The arguments to the function
21462      * will be the args provided or the current node. If the function returns false at any point,
21463      * the cascade is stopped on that branch.
21464      * @param {Function} fn The function to call
21465      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21466      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21467      */
21468     cascade : function(fn, scope, args){
21469         if(fn.call(scope || this, args || this) !== false){
21470             var cs = this.childNodes;
21471             for(var i = 0, len = cs.length; i < len; i++) {
21472                 cs[i].cascade(fn, scope, args);
21473             }
21474         }
21475     },
21476
21477     /**
21478      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21479      * function call will be the scope provided or the current node. The arguments to the function
21480      * will be the args provided or the current node. If the function returns false at any point,
21481      * the iteration stops.
21482      * @param {Function} fn The function to call
21483      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21484      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21485      */
21486     eachChild : function(fn, scope, args){
21487         var cs = this.childNodes;
21488         for(var i = 0, len = cs.length; i < len; i++) {
21489                 if(fn.call(scope || this, args || cs[i]) === false){
21490                     break;
21491                 }
21492         }
21493     },
21494
21495     /**
21496      * Finds the first child that has the attribute with the specified value.
21497      * @param {String} attribute The attribute name
21498      * @param {Mixed} value The value to search for
21499      * @return {Node} The found child or null if none was found
21500      */
21501     findChild : function(attribute, value){
21502         var cs = this.childNodes;
21503         for(var i = 0, len = cs.length; i < len; i++) {
21504                 if(cs[i].attributes[attribute] == value){
21505                     return cs[i];
21506                 }
21507         }
21508         return null;
21509     },
21510
21511     /**
21512      * Finds the first child by a custom function. The child matches if the function passed
21513      * returns true.
21514      * @param {Function} fn
21515      * @param {Object} scope (optional)
21516      * @return {Node} The found child or null if none was found
21517      */
21518     findChildBy : function(fn, scope){
21519         var cs = this.childNodes;
21520         for(var i = 0, len = cs.length; i < len; i++) {
21521                 if(fn.call(scope||cs[i], cs[i]) === true){
21522                     return cs[i];
21523                 }
21524         }
21525         return null;
21526     },
21527
21528     /**
21529      * Sorts this nodes children using the supplied sort function
21530      * @param {Function} fn
21531      * @param {Object} scope (optional)
21532      */
21533     sort : function(fn, scope){
21534         var cs = this.childNodes;
21535         var len = cs.length;
21536         if(len > 0){
21537             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21538             cs.sort(sortFn);
21539             for(var i = 0; i < len; i++){
21540                 var n = cs[i];
21541                 n.previousSibling = cs[i-1];
21542                 n.nextSibling = cs[i+1];
21543                 if(i == 0){
21544                     this.setFirstChild(n);
21545                 }
21546                 if(i == len-1){
21547                     this.setLastChild(n);
21548                 }
21549             }
21550         }
21551     },
21552
21553     /**
21554      * Returns true if this node is an ancestor (at any point) of the passed node.
21555      * @param {Node} node
21556      * @return {Boolean}
21557      */
21558     contains : function(node){
21559         return node.isAncestor(this);
21560     },
21561
21562     /**
21563      * Returns true if the passed node is an ancestor (at any point) of this node.
21564      * @param {Node} node
21565      * @return {Boolean}
21566      */
21567     isAncestor : function(node){
21568         var p = this.parentNode;
21569         while(p){
21570             if(p == node){
21571                 return true;
21572             }
21573             p = p.parentNode;
21574         }
21575         return false;
21576     },
21577
21578     toString : function(){
21579         return "[Node"+(this.id?" "+this.id:"")+"]";
21580     }
21581 });/*
21582  * Based on:
21583  * Ext JS Library 1.1.1
21584  * Copyright(c) 2006-2007, Ext JS, LLC.
21585  *
21586  * Originally Released Under LGPL - original licence link has changed is not relivant.
21587  *
21588  * Fork - LGPL
21589  * <script type="text/javascript">
21590  */
21591  
21592
21593 /**
21594  * @class Roo.ComponentMgr
21595  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21596  * @singleton
21597  */
21598 Roo.ComponentMgr = function(){
21599     var all = new Roo.util.MixedCollection();
21600
21601     return {
21602         /**
21603          * Registers a component.
21604          * @param {Roo.Component} c The component
21605          */
21606         register : function(c){
21607             all.add(c);
21608         },
21609
21610         /**
21611          * Unregisters a component.
21612          * @param {Roo.Component} c The component
21613          */
21614         unregister : function(c){
21615             all.remove(c);
21616         },
21617
21618         /**
21619          * Returns a component by id
21620          * @param {String} id The component id
21621          */
21622         get : function(id){
21623             return all.get(id);
21624         },
21625
21626         /**
21627          * Registers a function that will be called when a specified component is added to ComponentMgr
21628          * @param {String} id The component id
21629          * @param {Funtction} fn The callback function
21630          * @param {Object} scope The scope of the callback
21631          */
21632         onAvailable : function(id, fn, scope){
21633             all.on("add", function(index, o){
21634                 if(o.id == id){
21635                     fn.call(scope || o, o);
21636                     all.un("add", fn, scope);
21637                 }
21638             });
21639         }
21640     };
21641 }();/*
21642  * Based on:
21643  * Ext JS Library 1.1.1
21644  * Copyright(c) 2006-2007, Ext JS, LLC.
21645  *
21646  * Originally Released Under LGPL - original licence link has changed is not relivant.
21647  *
21648  * Fork - LGPL
21649  * <script type="text/javascript">
21650  */
21651  
21652 /**
21653  * @class Roo.Component
21654  * @extends Roo.util.Observable
21655  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21656  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21657  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21658  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21659  * All visual components (widgets) that require rendering into a layout should subclass Component.
21660  * @constructor
21661  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21662  * 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
21663  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21664  */
21665 Roo.Component = function(config){
21666     config = config || {};
21667     if(config.tagName || config.dom || typeof config == "string"){ // element object
21668         config = {el: config, id: config.id || config};
21669     }
21670     this.initialConfig = config;
21671
21672     Roo.apply(this, config);
21673     this.addEvents({
21674         /**
21675          * @event disable
21676          * Fires after the component is disabled.
21677              * @param {Roo.Component} this
21678              */
21679         disable : true,
21680         /**
21681          * @event enable
21682          * Fires after the component is enabled.
21683              * @param {Roo.Component} this
21684              */
21685         enable : true,
21686         /**
21687          * @event beforeshow
21688          * Fires before the component is shown.  Return false to stop the show.
21689              * @param {Roo.Component} this
21690              */
21691         beforeshow : true,
21692         /**
21693          * @event show
21694          * Fires after the component is shown.
21695              * @param {Roo.Component} this
21696              */
21697         show : true,
21698         /**
21699          * @event beforehide
21700          * Fires before the component is hidden. Return false to stop the hide.
21701              * @param {Roo.Component} this
21702              */
21703         beforehide : true,
21704         /**
21705          * @event hide
21706          * Fires after the component is hidden.
21707              * @param {Roo.Component} this
21708              */
21709         hide : true,
21710         /**
21711          * @event beforerender
21712          * Fires before the component is rendered. Return false to stop the render.
21713              * @param {Roo.Component} this
21714              */
21715         beforerender : true,
21716         /**
21717          * @event render
21718          * Fires after the component is rendered.
21719              * @param {Roo.Component} this
21720              */
21721         render : true,
21722         /**
21723          * @event beforedestroy
21724          * Fires before the component is destroyed. Return false to stop the destroy.
21725              * @param {Roo.Component} this
21726              */
21727         beforedestroy : true,
21728         /**
21729          * @event destroy
21730          * Fires after the component is destroyed.
21731              * @param {Roo.Component} this
21732              */
21733         destroy : true
21734     });
21735     if(!this.id){
21736         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21737     }
21738     Roo.ComponentMgr.register(this);
21739     Roo.Component.superclass.constructor.call(this);
21740     this.initComponent();
21741     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21742         this.render(this.renderTo);
21743         delete this.renderTo;
21744     }
21745 };
21746
21747 // private
21748 Roo.Component.AUTO_ID = 1000;
21749
21750 Roo.extend(Roo.Component, Roo.util.Observable, {
21751     /**
21752      * @property {Boolean} hidden
21753      * true if this component is hidden. Read-only.
21754      */
21755     hidden : false,
21756     /**
21757      * true if this component is disabled. Read-only.
21758      */
21759     disabled : false,
21760     /**
21761      * true if this component has been rendered. Read-only.
21762      */
21763     rendered : false,
21764     
21765     /** @cfg {String} disableClass
21766      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21767      */
21768     disabledClass : "x-item-disabled",
21769         /** @cfg {Boolean} allowDomMove
21770          * Whether the component can move the Dom node when rendering (defaults to true).
21771          */
21772     allowDomMove : true,
21773     /** @cfg {String} hideMode
21774      * How this component should hidden. Supported values are
21775      * "visibility" (css visibility), "offsets" (negative offset position) and
21776      * "display" (css display) - defaults to "display".
21777      */
21778     hideMode: 'display',
21779
21780     // private
21781     ctype : "Roo.Component",
21782
21783     /** @cfg {String} actionMode 
21784      * which property holds the element that used for  hide() / show() / disable() / enable()
21785      * default is 'el' 
21786      */
21787     actionMode : "el",
21788
21789     // private
21790     getActionEl : function(){
21791         return this[this.actionMode];
21792     },
21793
21794     initComponent : Roo.emptyFn,
21795     /**
21796      * If this is a lazy rendering component, render it to its container element.
21797      * @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.
21798      */
21799     render : function(container, position){
21800         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21801             if(!container && this.el){
21802                 this.el = Roo.get(this.el);
21803                 container = this.el.dom.parentNode;
21804                 this.allowDomMove = false;
21805             }
21806             this.container = Roo.get(container);
21807             this.rendered = true;
21808             if(position !== undefined){
21809                 if(typeof position == 'number'){
21810                     position = this.container.dom.childNodes[position];
21811                 }else{
21812                     position = Roo.getDom(position);
21813                 }
21814             }
21815             this.onRender(this.container, position || null);
21816             if(this.cls){
21817                 this.el.addClass(this.cls);
21818                 delete this.cls;
21819             }
21820             if(this.style){
21821                 this.el.applyStyles(this.style);
21822                 delete this.style;
21823             }
21824             this.fireEvent("render", this);
21825             this.afterRender(this.container);
21826             if(this.hidden){
21827                 this.hide();
21828             }
21829             if(this.disabled){
21830                 this.disable();
21831             }
21832         }
21833         return this;
21834     },
21835
21836     // private
21837     // default function is not really useful
21838     onRender : function(ct, position){
21839         if(this.el){
21840             this.el = Roo.get(this.el);
21841             if(this.allowDomMove !== false){
21842                 ct.dom.insertBefore(this.el.dom, position);
21843             }
21844         }
21845     },
21846
21847     // private
21848     getAutoCreate : function(){
21849         var cfg = typeof this.autoCreate == "object" ?
21850                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21851         if(this.id && !cfg.id){
21852             cfg.id = this.id;
21853         }
21854         return cfg;
21855     },
21856
21857     // private
21858     afterRender : Roo.emptyFn,
21859
21860     /**
21861      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21862      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21863      */
21864     destroy : function(){
21865         if(this.fireEvent("beforedestroy", this) !== false){
21866             this.purgeListeners();
21867             this.beforeDestroy();
21868             if(this.rendered){
21869                 this.el.removeAllListeners();
21870                 this.el.remove();
21871                 if(this.actionMode == "container"){
21872                     this.container.remove();
21873                 }
21874             }
21875             this.onDestroy();
21876             Roo.ComponentMgr.unregister(this);
21877             this.fireEvent("destroy", this);
21878         }
21879     },
21880
21881         // private
21882     beforeDestroy : function(){
21883
21884     },
21885
21886         // private
21887         onDestroy : function(){
21888
21889     },
21890
21891     /**
21892      * Returns the underlying {@link Roo.Element}.
21893      * @return {Roo.Element} The element
21894      */
21895     getEl : function(){
21896         return this.el;
21897     },
21898
21899     /**
21900      * Returns the id of this component.
21901      * @return {String}
21902      */
21903     getId : function(){
21904         return this.id;
21905     },
21906
21907     /**
21908      * Try to focus this component.
21909      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21910      * @return {Roo.Component} this
21911      */
21912     focus : function(selectText){
21913         if(this.rendered){
21914             this.el.focus();
21915             if(selectText === true){
21916                 this.el.dom.select();
21917             }
21918         }
21919         return this;
21920     },
21921
21922     // private
21923     blur : function(){
21924         if(this.rendered){
21925             this.el.blur();
21926         }
21927         return this;
21928     },
21929
21930     /**
21931      * Disable this component.
21932      * @return {Roo.Component} this
21933      */
21934     disable : function(){
21935         if(this.rendered){
21936             this.onDisable();
21937         }
21938         this.disabled = true;
21939         this.fireEvent("disable", this);
21940         return this;
21941     },
21942
21943         // private
21944     onDisable : function(){
21945         this.getActionEl().addClass(this.disabledClass);
21946         this.el.dom.disabled = true;
21947     },
21948
21949     /**
21950      * Enable this component.
21951      * @return {Roo.Component} this
21952      */
21953     enable : function(){
21954         if(this.rendered){
21955             this.onEnable();
21956         }
21957         this.disabled = false;
21958         this.fireEvent("enable", this);
21959         return this;
21960     },
21961
21962         // private
21963     onEnable : function(){
21964         this.getActionEl().removeClass(this.disabledClass);
21965         this.el.dom.disabled = false;
21966     },
21967
21968     /**
21969      * Convenience function for setting disabled/enabled by boolean.
21970      * @param {Boolean} disabled
21971      */
21972     setDisabled : function(disabled){
21973         this[disabled ? "disable" : "enable"]();
21974     },
21975
21976     /**
21977      * Show this component.
21978      * @return {Roo.Component} this
21979      */
21980     show: function(){
21981         if(this.fireEvent("beforeshow", this) !== false){
21982             this.hidden = false;
21983             if(this.rendered){
21984                 this.onShow();
21985             }
21986             this.fireEvent("show", this);
21987         }
21988         return this;
21989     },
21990
21991     // private
21992     onShow : function(){
21993         var ae = this.getActionEl();
21994         if(this.hideMode == 'visibility'){
21995             ae.dom.style.visibility = "visible";
21996         }else if(this.hideMode == 'offsets'){
21997             ae.removeClass('x-hidden');
21998         }else{
21999             ae.dom.style.display = "";
22000         }
22001     },
22002
22003     /**
22004      * Hide this component.
22005      * @return {Roo.Component} this
22006      */
22007     hide: function(){
22008         if(this.fireEvent("beforehide", this) !== false){
22009             this.hidden = true;
22010             if(this.rendered){
22011                 this.onHide();
22012             }
22013             this.fireEvent("hide", this);
22014         }
22015         return this;
22016     },
22017
22018     // private
22019     onHide : function(){
22020         var ae = this.getActionEl();
22021         if(this.hideMode == 'visibility'){
22022             ae.dom.style.visibility = "hidden";
22023         }else if(this.hideMode == 'offsets'){
22024             ae.addClass('x-hidden');
22025         }else{
22026             ae.dom.style.display = "none";
22027         }
22028     },
22029
22030     /**
22031      * Convenience function to hide or show this component by boolean.
22032      * @param {Boolean} visible True to show, false to hide
22033      * @return {Roo.Component} this
22034      */
22035     setVisible: function(visible){
22036         if(visible) {
22037             this.show();
22038         }else{
22039             this.hide();
22040         }
22041         return this;
22042     },
22043
22044     /**
22045      * Returns true if this component is visible.
22046      */
22047     isVisible : function(){
22048         return this.getActionEl().isVisible();
22049     },
22050
22051     cloneConfig : function(overrides){
22052         overrides = overrides || {};
22053         var id = overrides.id || Roo.id();
22054         var cfg = Roo.applyIf(overrides, this.initialConfig);
22055         cfg.id = id; // prevent dup id
22056         return new this.constructor(cfg);
22057     }
22058 });/*
22059  * Based on:
22060  * Ext JS Library 1.1.1
22061  * Copyright(c) 2006-2007, Ext JS, LLC.
22062  *
22063  * Originally Released Under LGPL - original licence link has changed is not relivant.
22064  *
22065  * Fork - LGPL
22066  * <script type="text/javascript">
22067  */
22068  (function(){ 
22069 /**
22070  * @class Roo.Layer
22071  * @extends Roo.Element
22072  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22073  * automatic maintaining of shadow/shim positions.
22074  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22075  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22076  * you can pass a string with a CSS class name. False turns off the shadow.
22077  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22078  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22079  * @cfg {String} cls CSS class to add to the element
22080  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22081  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22082  * @constructor
22083  * @param {Object} config An object with config options.
22084  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22085  */
22086
22087 Roo.Layer = function(config, existingEl){
22088     config = config || {};
22089     var dh = Roo.DomHelper;
22090     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22091     if(existingEl){
22092         this.dom = Roo.getDom(existingEl);
22093     }
22094     if(!this.dom){
22095         var o = config.dh || {tag: "div", cls: "x-layer"};
22096         this.dom = dh.append(pel, o);
22097     }
22098     if(config.cls){
22099         this.addClass(config.cls);
22100     }
22101     this.constrain = config.constrain !== false;
22102     this.visibilityMode = Roo.Element.VISIBILITY;
22103     if(config.id){
22104         this.id = this.dom.id = config.id;
22105     }else{
22106         this.id = Roo.id(this.dom);
22107     }
22108     this.zindex = config.zindex || this.getZIndex();
22109     this.position("absolute", this.zindex);
22110     if(config.shadow){
22111         this.shadowOffset = config.shadowOffset || 4;
22112         this.shadow = new Roo.Shadow({
22113             offset : this.shadowOffset,
22114             mode : config.shadow
22115         });
22116     }else{
22117         this.shadowOffset = 0;
22118     }
22119     this.useShim = config.shim !== false && Roo.useShims;
22120     this.useDisplay = config.useDisplay;
22121     this.hide();
22122 };
22123
22124 var supr = Roo.Element.prototype;
22125
22126 // shims are shared among layer to keep from having 100 iframes
22127 var shims = [];
22128
22129 Roo.extend(Roo.Layer, Roo.Element, {
22130
22131     getZIndex : function(){
22132         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22133     },
22134
22135     getShim : function(){
22136         if(!this.useShim){
22137             return null;
22138         }
22139         if(this.shim){
22140             return this.shim;
22141         }
22142         var shim = shims.shift();
22143         if(!shim){
22144             shim = this.createShim();
22145             shim.enableDisplayMode('block');
22146             shim.dom.style.display = 'none';
22147             shim.dom.style.visibility = 'visible';
22148         }
22149         var pn = this.dom.parentNode;
22150         if(shim.dom.parentNode != pn){
22151             pn.insertBefore(shim.dom, this.dom);
22152         }
22153         shim.setStyle('z-index', this.getZIndex()-2);
22154         this.shim = shim;
22155         return shim;
22156     },
22157
22158     hideShim : function(){
22159         if(this.shim){
22160             this.shim.setDisplayed(false);
22161             shims.push(this.shim);
22162             delete this.shim;
22163         }
22164     },
22165
22166     disableShadow : function(){
22167         if(this.shadow){
22168             this.shadowDisabled = true;
22169             this.shadow.hide();
22170             this.lastShadowOffset = this.shadowOffset;
22171             this.shadowOffset = 0;
22172         }
22173     },
22174
22175     enableShadow : function(show){
22176         if(this.shadow){
22177             this.shadowDisabled = false;
22178             this.shadowOffset = this.lastShadowOffset;
22179             delete this.lastShadowOffset;
22180             if(show){
22181                 this.sync(true);
22182             }
22183         }
22184     },
22185
22186     // private
22187     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22188     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22189     sync : function(doShow){
22190         var sw = this.shadow;
22191         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22192             var sh = this.getShim();
22193
22194             var w = this.getWidth(),
22195                 h = this.getHeight();
22196
22197             var l = this.getLeft(true),
22198                 t = this.getTop(true);
22199
22200             if(sw && !this.shadowDisabled){
22201                 if(doShow && !sw.isVisible()){
22202                     sw.show(this);
22203                 }else{
22204                     sw.realign(l, t, w, h);
22205                 }
22206                 if(sh){
22207                     if(doShow){
22208                        sh.show();
22209                     }
22210                     // fit the shim behind the shadow, so it is shimmed too
22211                     var a = sw.adjusts, s = sh.dom.style;
22212                     s.left = (Math.min(l, l+a.l))+"px";
22213                     s.top = (Math.min(t, t+a.t))+"px";
22214                     s.width = (w+a.w)+"px";
22215                     s.height = (h+a.h)+"px";
22216                 }
22217             }else if(sh){
22218                 if(doShow){
22219                    sh.show();
22220                 }
22221                 sh.setSize(w, h);
22222                 sh.setLeftTop(l, t);
22223             }
22224             
22225         }
22226     },
22227
22228     // private
22229     destroy : function(){
22230         this.hideShim();
22231         if(this.shadow){
22232             this.shadow.hide();
22233         }
22234         this.removeAllListeners();
22235         var pn = this.dom.parentNode;
22236         if(pn){
22237             pn.removeChild(this.dom);
22238         }
22239         Roo.Element.uncache(this.id);
22240     },
22241
22242     remove : function(){
22243         this.destroy();
22244     },
22245
22246     // private
22247     beginUpdate : function(){
22248         this.updating = true;
22249     },
22250
22251     // private
22252     endUpdate : function(){
22253         this.updating = false;
22254         this.sync(true);
22255     },
22256
22257     // private
22258     hideUnders : function(negOffset){
22259         if(this.shadow){
22260             this.shadow.hide();
22261         }
22262         this.hideShim();
22263     },
22264
22265     // private
22266     constrainXY : function(){
22267         if(this.constrain){
22268             var vw = Roo.lib.Dom.getViewWidth(),
22269                 vh = Roo.lib.Dom.getViewHeight();
22270             var s = Roo.get(document).getScroll();
22271
22272             var xy = this.getXY();
22273             var x = xy[0], y = xy[1];   
22274             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22275             // only move it if it needs it
22276             var moved = false;
22277             // first validate right/bottom
22278             if((x + w) > vw+s.left){
22279                 x = vw - w - this.shadowOffset;
22280                 moved = true;
22281             }
22282             if((y + h) > vh+s.top){
22283                 y = vh - h - this.shadowOffset;
22284                 moved = true;
22285             }
22286             // then make sure top/left isn't negative
22287             if(x < s.left){
22288                 x = s.left;
22289                 moved = true;
22290             }
22291             if(y < s.top){
22292                 y = s.top;
22293                 moved = true;
22294             }
22295             if(moved){
22296                 if(this.avoidY){
22297                     var ay = this.avoidY;
22298                     if(y <= ay && (y+h) >= ay){
22299                         y = ay-h-5;   
22300                     }
22301                 }
22302                 xy = [x, y];
22303                 this.storeXY(xy);
22304                 supr.setXY.call(this, xy);
22305                 this.sync();
22306             }
22307         }
22308     },
22309
22310     isVisible : function(){
22311         return this.visible;    
22312     },
22313
22314     // private
22315     showAction : function(){
22316         this.visible = true; // track visibility to prevent getStyle calls
22317         if(this.useDisplay === true){
22318             this.setDisplayed("");
22319         }else if(this.lastXY){
22320             supr.setXY.call(this, this.lastXY);
22321         }else if(this.lastLT){
22322             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22323         }
22324     },
22325
22326     // private
22327     hideAction : function(){
22328         this.visible = false;
22329         if(this.useDisplay === true){
22330             this.setDisplayed(false);
22331         }else{
22332             this.setLeftTop(-10000,-10000);
22333         }
22334     },
22335
22336     // overridden Element method
22337     setVisible : function(v, a, d, c, e){
22338         if(v){
22339             this.showAction();
22340         }
22341         if(a && v){
22342             var cb = function(){
22343                 this.sync(true);
22344                 if(c){
22345                     c();
22346                 }
22347             }.createDelegate(this);
22348             supr.setVisible.call(this, true, true, d, cb, e);
22349         }else{
22350             if(!v){
22351                 this.hideUnders(true);
22352             }
22353             var cb = c;
22354             if(a){
22355                 cb = function(){
22356                     this.hideAction();
22357                     if(c){
22358                         c();
22359                     }
22360                 }.createDelegate(this);
22361             }
22362             supr.setVisible.call(this, v, a, d, cb, e);
22363             if(v){
22364                 this.sync(true);
22365             }else if(!a){
22366                 this.hideAction();
22367             }
22368         }
22369     },
22370
22371     storeXY : function(xy){
22372         delete this.lastLT;
22373         this.lastXY = xy;
22374     },
22375
22376     storeLeftTop : function(left, top){
22377         delete this.lastXY;
22378         this.lastLT = [left, top];
22379     },
22380
22381     // private
22382     beforeFx : function(){
22383         this.beforeAction();
22384         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22385     },
22386
22387     // private
22388     afterFx : function(){
22389         Roo.Layer.superclass.afterFx.apply(this, arguments);
22390         this.sync(this.isVisible());
22391     },
22392
22393     // private
22394     beforeAction : function(){
22395         if(!this.updating && this.shadow){
22396             this.shadow.hide();
22397         }
22398     },
22399
22400     // overridden Element method
22401     setLeft : function(left){
22402         this.storeLeftTop(left, this.getTop(true));
22403         supr.setLeft.apply(this, arguments);
22404         this.sync();
22405     },
22406
22407     setTop : function(top){
22408         this.storeLeftTop(this.getLeft(true), top);
22409         supr.setTop.apply(this, arguments);
22410         this.sync();
22411     },
22412
22413     setLeftTop : function(left, top){
22414         this.storeLeftTop(left, top);
22415         supr.setLeftTop.apply(this, arguments);
22416         this.sync();
22417     },
22418
22419     setXY : function(xy, a, d, c, e){
22420         this.fixDisplay();
22421         this.beforeAction();
22422         this.storeXY(xy);
22423         var cb = this.createCB(c);
22424         supr.setXY.call(this, xy, a, d, cb, e);
22425         if(!a){
22426             cb();
22427         }
22428     },
22429
22430     // private
22431     createCB : function(c){
22432         var el = this;
22433         return function(){
22434             el.constrainXY();
22435             el.sync(true);
22436             if(c){
22437                 c();
22438             }
22439         };
22440     },
22441
22442     // overridden Element method
22443     setX : function(x, a, d, c, e){
22444         this.setXY([x, this.getY()], a, d, c, e);
22445     },
22446
22447     // overridden Element method
22448     setY : function(y, a, d, c, e){
22449         this.setXY([this.getX(), y], a, d, c, e);
22450     },
22451
22452     // overridden Element method
22453     setSize : function(w, h, a, d, c, e){
22454         this.beforeAction();
22455         var cb = this.createCB(c);
22456         supr.setSize.call(this, w, h, a, d, cb, e);
22457         if(!a){
22458             cb();
22459         }
22460     },
22461
22462     // overridden Element method
22463     setWidth : function(w, a, d, c, e){
22464         this.beforeAction();
22465         var cb = this.createCB(c);
22466         supr.setWidth.call(this, w, a, d, cb, e);
22467         if(!a){
22468             cb();
22469         }
22470     },
22471
22472     // overridden Element method
22473     setHeight : function(h, a, d, c, e){
22474         this.beforeAction();
22475         var cb = this.createCB(c);
22476         supr.setHeight.call(this, h, a, d, cb, e);
22477         if(!a){
22478             cb();
22479         }
22480     },
22481
22482     // overridden Element method
22483     setBounds : function(x, y, w, h, a, d, c, e){
22484         this.beforeAction();
22485         var cb = this.createCB(c);
22486         if(!a){
22487             this.storeXY([x, y]);
22488             supr.setXY.call(this, [x, y]);
22489             supr.setSize.call(this, w, h, a, d, cb, e);
22490             cb();
22491         }else{
22492             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22493         }
22494         return this;
22495     },
22496     
22497     /**
22498      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22499      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22500      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22501      * @param {Number} zindex The new z-index to set
22502      * @return {this} The Layer
22503      */
22504     setZIndex : function(zindex){
22505         this.zindex = zindex;
22506         this.setStyle("z-index", zindex + 2);
22507         if(this.shadow){
22508             this.shadow.setZIndex(zindex + 1);
22509         }
22510         if(this.shim){
22511             this.shim.setStyle("z-index", zindex);
22512         }
22513     }
22514 });
22515 })();/*
22516  * Based on:
22517  * Ext JS Library 1.1.1
22518  * Copyright(c) 2006-2007, Ext JS, LLC.
22519  *
22520  * Originally Released Under LGPL - original licence link has changed is not relivant.
22521  *
22522  * Fork - LGPL
22523  * <script type="text/javascript">
22524  */
22525
22526
22527 /**
22528  * @class Roo.Shadow
22529  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22530  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22531  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22532  * @constructor
22533  * Create a new Shadow
22534  * @param {Object} config The config object
22535  */
22536 Roo.Shadow = function(config){
22537     Roo.apply(this, config);
22538     if(typeof this.mode != "string"){
22539         this.mode = this.defaultMode;
22540     }
22541     var o = this.offset, a = {h: 0};
22542     var rad = Math.floor(this.offset/2);
22543     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22544         case "drop":
22545             a.w = 0;
22546             a.l = a.t = o;
22547             a.t -= 1;
22548             if(Roo.isIE){
22549                 a.l -= this.offset + rad;
22550                 a.t -= this.offset + rad;
22551                 a.w -= rad;
22552                 a.h -= rad;
22553                 a.t += 1;
22554             }
22555         break;
22556         case "sides":
22557             a.w = (o*2);
22558             a.l = -o;
22559             a.t = o-1;
22560             if(Roo.isIE){
22561                 a.l -= (this.offset - rad);
22562                 a.t -= this.offset + rad;
22563                 a.l += 1;
22564                 a.w -= (this.offset - rad)*2;
22565                 a.w -= rad + 1;
22566                 a.h -= 1;
22567             }
22568         break;
22569         case "frame":
22570             a.w = a.h = (o*2);
22571             a.l = a.t = -o;
22572             a.t += 1;
22573             a.h -= 2;
22574             if(Roo.isIE){
22575                 a.l -= (this.offset - rad);
22576                 a.t -= (this.offset - rad);
22577                 a.l += 1;
22578                 a.w -= (this.offset + rad + 1);
22579                 a.h -= (this.offset + rad);
22580                 a.h += 1;
22581             }
22582         break;
22583     };
22584
22585     this.adjusts = a;
22586 };
22587
22588 Roo.Shadow.prototype = {
22589     /**
22590      * @cfg {String} mode
22591      * The shadow display mode.  Supports the following options:<br />
22592      * sides: Shadow displays on both sides and bottom only<br />
22593      * frame: Shadow displays equally on all four sides<br />
22594      * drop: Traditional bottom-right drop shadow (default)
22595      */
22596     /**
22597      * @cfg {String} offset
22598      * The number of pixels to offset the shadow from the element (defaults to 4)
22599      */
22600     offset: 4,
22601
22602     // private
22603     defaultMode: "drop",
22604
22605     /**
22606      * Displays the shadow under the target element
22607      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22608      */
22609     show : function(target){
22610         target = Roo.get(target);
22611         if(!this.el){
22612             this.el = Roo.Shadow.Pool.pull();
22613             if(this.el.dom.nextSibling != target.dom){
22614                 this.el.insertBefore(target);
22615             }
22616         }
22617         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22618         if(Roo.isIE){
22619             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22620         }
22621         this.realign(
22622             target.getLeft(true),
22623             target.getTop(true),
22624             target.getWidth(),
22625             target.getHeight()
22626         );
22627         this.el.dom.style.display = "block";
22628     },
22629
22630     /**
22631      * Returns true if the shadow is visible, else false
22632      */
22633     isVisible : function(){
22634         return this.el ? true : false;  
22635     },
22636
22637     /**
22638      * Direct alignment when values are already available. Show must be called at least once before
22639      * calling this method to ensure it is initialized.
22640      * @param {Number} left The target element left position
22641      * @param {Number} top The target element top position
22642      * @param {Number} width The target element width
22643      * @param {Number} height The target element height
22644      */
22645     realign : function(l, t, w, h){
22646         if(!this.el){
22647             return;
22648         }
22649         var a = this.adjusts, d = this.el.dom, s = d.style;
22650         var iea = 0;
22651         s.left = (l+a.l)+"px";
22652         s.top = (t+a.t)+"px";
22653         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22654  
22655         if(s.width != sws || s.height != shs){
22656             s.width = sws;
22657             s.height = shs;
22658             if(!Roo.isIE){
22659                 var cn = d.childNodes;
22660                 var sww = Math.max(0, (sw-12))+"px";
22661                 cn[0].childNodes[1].style.width = sww;
22662                 cn[1].childNodes[1].style.width = sww;
22663                 cn[2].childNodes[1].style.width = sww;
22664                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22665             }
22666         }
22667     },
22668
22669     /**
22670      * Hides this shadow
22671      */
22672     hide : function(){
22673         if(this.el){
22674             this.el.dom.style.display = "none";
22675             Roo.Shadow.Pool.push(this.el);
22676             delete this.el;
22677         }
22678     },
22679
22680     /**
22681      * Adjust the z-index of this shadow
22682      * @param {Number} zindex The new z-index
22683      */
22684     setZIndex : function(z){
22685         this.zIndex = z;
22686         if(this.el){
22687             this.el.setStyle("z-index", z);
22688         }
22689     }
22690 };
22691
22692 // Private utility class that manages the internal Shadow cache
22693 Roo.Shadow.Pool = function(){
22694     var p = [];
22695     var markup = Roo.isIE ?
22696                  '<div class="x-ie-shadow"></div>' :
22697                  '<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>';
22698     return {
22699         pull : function(){
22700             var sh = p.shift();
22701             if(!sh){
22702                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22703                 sh.autoBoxAdjust = false;
22704             }
22705             return sh;
22706         },
22707
22708         push : function(sh){
22709             p.push(sh);
22710         }
22711     };
22712 }();/*
22713  * Based on:
22714  * Ext JS Library 1.1.1
22715  * Copyright(c) 2006-2007, Ext JS, LLC.
22716  *
22717  * Originally Released Under LGPL - original licence link has changed is not relivant.
22718  *
22719  * Fork - LGPL
22720  * <script type="text/javascript">
22721  */
22722
22723 /**
22724  * @class Roo.BoxComponent
22725  * @extends Roo.Component
22726  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22727  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22728  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22729  * layout containers.
22730  * @constructor
22731  * @param {Roo.Element/String/Object} config The configuration options.
22732  */
22733 Roo.BoxComponent = function(config){
22734     Roo.Component.call(this, config);
22735     this.addEvents({
22736         /**
22737          * @event resize
22738          * Fires after the component is resized.
22739              * @param {Roo.Component} this
22740              * @param {Number} adjWidth The box-adjusted width that was set
22741              * @param {Number} adjHeight The box-adjusted height that was set
22742              * @param {Number} rawWidth The width that was originally specified
22743              * @param {Number} rawHeight The height that was originally specified
22744              */
22745         resize : true,
22746         /**
22747          * @event move
22748          * Fires after the component is moved.
22749              * @param {Roo.Component} this
22750              * @param {Number} x The new x position
22751              * @param {Number} y The new y position
22752              */
22753         move : true
22754     });
22755 };
22756
22757 Roo.extend(Roo.BoxComponent, Roo.Component, {
22758     // private, set in afterRender to signify that the component has been rendered
22759     boxReady : false,
22760     // private, used to defer height settings to subclasses
22761     deferHeight: false,
22762     /** @cfg {Number} width
22763      * width (optional) size of component
22764      */
22765      /** @cfg {Number} height
22766      * height (optional) size of component
22767      */
22768      
22769     /**
22770      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22771      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22772      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22773      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22774      * @return {Roo.BoxComponent} this
22775      */
22776     setSize : function(w, h){
22777         // support for standard size objects
22778         if(typeof w == 'object'){
22779             h = w.height;
22780             w = w.width;
22781         }
22782         // not rendered
22783         if(!this.boxReady){
22784             this.width = w;
22785             this.height = h;
22786             return this;
22787         }
22788
22789         // prevent recalcs when not needed
22790         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22791             return this;
22792         }
22793         this.lastSize = {width: w, height: h};
22794
22795         var adj = this.adjustSize(w, h);
22796         var aw = adj.width, ah = adj.height;
22797         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22798             var rz = this.getResizeEl();
22799             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22800                 rz.setSize(aw, ah);
22801             }else if(!this.deferHeight && ah !== undefined){
22802                 rz.setHeight(ah);
22803             }else if(aw !== undefined){
22804                 rz.setWidth(aw);
22805             }
22806             this.onResize(aw, ah, w, h);
22807             this.fireEvent('resize', this, aw, ah, w, h);
22808         }
22809         return this;
22810     },
22811
22812     /**
22813      * Gets the current size of the component's underlying element.
22814      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22815      */
22816     getSize : function(){
22817         return this.el.getSize();
22818     },
22819
22820     /**
22821      * Gets the current XY position of the component's underlying element.
22822      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22823      * @return {Array} The XY position of the element (e.g., [100, 200])
22824      */
22825     getPosition : function(local){
22826         if(local === true){
22827             return [this.el.getLeft(true), this.el.getTop(true)];
22828         }
22829         return this.xy || this.el.getXY();
22830     },
22831
22832     /**
22833      * Gets the current box measurements of the component's underlying element.
22834      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22835      * @returns {Object} box An object in the format {x, y, width, height}
22836      */
22837     getBox : function(local){
22838         var s = this.el.getSize();
22839         if(local){
22840             s.x = this.el.getLeft(true);
22841             s.y = this.el.getTop(true);
22842         }else{
22843             var xy = this.xy || this.el.getXY();
22844             s.x = xy[0];
22845             s.y = xy[1];
22846         }
22847         return s;
22848     },
22849
22850     /**
22851      * Sets the current box measurements of the component's underlying element.
22852      * @param {Object} box An object in the format {x, y, width, height}
22853      * @returns {Roo.BoxComponent} this
22854      */
22855     updateBox : function(box){
22856         this.setSize(box.width, box.height);
22857         this.setPagePosition(box.x, box.y);
22858         return this;
22859     },
22860
22861     // protected
22862     getResizeEl : function(){
22863         return this.resizeEl || this.el;
22864     },
22865
22866     // protected
22867     getPositionEl : function(){
22868         return this.positionEl || this.el;
22869     },
22870
22871     /**
22872      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22873      * This method fires the move event.
22874      * @param {Number} left The new left
22875      * @param {Number} top The new top
22876      * @returns {Roo.BoxComponent} this
22877      */
22878     setPosition : function(x, y){
22879         this.x = x;
22880         this.y = y;
22881         if(!this.boxReady){
22882             return this;
22883         }
22884         var adj = this.adjustPosition(x, y);
22885         var ax = adj.x, ay = adj.y;
22886
22887         var el = this.getPositionEl();
22888         if(ax !== undefined || ay !== undefined){
22889             if(ax !== undefined && ay !== undefined){
22890                 el.setLeftTop(ax, ay);
22891             }else if(ax !== undefined){
22892                 el.setLeft(ax);
22893             }else if(ay !== undefined){
22894                 el.setTop(ay);
22895             }
22896             this.onPosition(ax, ay);
22897             this.fireEvent('move', this, ax, ay);
22898         }
22899         return this;
22900     },
22901
22902     /**
22903      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22904      * This method fires the move event.
22905      * @param {Number} x The new x position
22906      * @param {Number} y The new y position
22907      * @returns {Roo.BoxComponent} this
22908      */
22909     setPagePosition : function(x, y){
22910         this.pageX = x;
22911         this.pageY = y;
22912         if(!this.boxReady){
22913             return;
22914         }
22915         if(x === undefined || y === undefined){ // cannot translate undefined points
22916             return;
22917         }
22918         var p = this.el.translatePoints(x, y);
22919         this.setPosition(p.left, p.top);
22920         return this;
22921     },
22922
22923     // private
22924     onRender : function(ct, position){
22925         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22926         if(this.resizeEl){
22927             this.resizeEl = Roo.get(this.resizeEl);
22928         }
22929         if(this.positionEl){
22930             this.positionEl = Roo.get(this.positionEl);
22931         }
22932     },
22933
22934     // private
22935     afterRender : function(){
22936         Roo.BoxComponent.superclass.afterRender.call(this);
22937         this.boxReady = true;
22938         this.setSize(this.width, this.height);
22939         if(this.x || this.y){
22940             this.setPosition(this.x, this.y);
22941         }
22942         if(this.pageX || this.pageY){
22943             this.setPagePosition(this.pageX, this.pageY);
22944         }
22945     },
22946
22947     /**
22948      * Force the component's size to recalculate based on the underlying element's current height and width.
22949      * @returns {Roo.BoxComponent} this
22950      */
22951     syncSize : function(){
22952         delete this.lastSize;
22953         this.setSize(this.el.getWidth(), this.el.getHeight());
22954         return this;
22955     },
22956
22957     /**
22958      * Called after the component is resized, this method is empty by default but can be implemented by any
22959      * subclass that needs to perform custom logic after a resize occurs.
22960      * @param {Number} adjWidth The box-adjusted width that was set
22961      * @param {Number} adjHeight The box-adjusted height that was set
22962      * @param {Number} rawWidth The width that was originally specified
22963      * @param {Number} rawHeight The height that was originally specified
22964      */
22965     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22966
22967     },
22968
22969     /**
22970      * Called after the component is moved, this method is empty by default but can be implemented by any
22971      * subclass that needs to perform custom logic after a move occurs.
22972      * @param {Number} x The new x position
22973      * @param {Number} y The new y position
22974      */
22975     onPosition : function(x, y){
22976
22977     },
22978
22979     // private
22980     adjustSize : function(w, h){
22981         if(this.autoWidth){
22982             w = 'auto';
22983         }
22984         if(this.autoHeight){
22985             h = 'auto';
22986         }
22987         return {width : w, height: h};
22988     },
22989
22990     // private
22991     adjustPosition : function(x, y){
22992         return {x : x, y: y};
22993     }
22994 });/*
22995  * Based on:
22996  * Ext JS Library 1.1.1
22997  * Copyright(c) 2006-2007, Ext JS, LLC.
22998  *
22999  * Originally Released Under LGPL - original licence link has changed is not relivant.
23000  *
23001  * Fork - LGPL
23002  * <script type="text/javascript">
23003  */
23004
23005
23006 /**
23007  * @class Roo.SplitBar
23008  * @extends Roo.util.Observable
23009  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23010  * <br><br>
23011  * Usage:
23012  * <pre><code>
23013 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23014                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23015 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23016 split.minSize = 100;
23017 split.maxSize = 600;
23018 split.animate = true;
23019 split.on('moved', splitterMoved);
23020 </code></pre>
23021  * @constructor
23022  * Create a new SplitBar
23023  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23024  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23025  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23026  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23027                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23028                         position of the SplitBar).
23029  */
23030 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23031     
23032     /** @private */
23033     this.el = Roo.get(dragElement, true);
23034     this.el.dom.unselectable = "on";
23035     /** @private */
23036     this.resizingEl = Roo.get(resizingElement, true);
23037
23038     /**
23039      * @private
23040      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23041      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23042      * @type Number
23043      */
23044     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23045     
23046     /**
23047      * The minimum size of the resizing element. (Defaults to 0)
23048      * @type Number
23049      */
23050     this.minSize = 0;
23051     
23052     /**
23053      * The maximum size of the resizing element. (Defaults to 2000)
23054      * @type Number
23055      */
23056     this.maxSize = 2000;
23057     
23058     /**
23059      * Whether to animate the transition to the new size
23060      * @type Boolean
23061      */
23062     this.animate = false;
23063     
23064     /**
23065      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23066      * @type Boolean
23067      */
23068     this.useShim = false;
23069     
23070     /** @private */
23071     this.shim = null;
23072     
23073     if(!existingProxy){
23074         /** @private */
23075         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23076     }else{
23077         this.proxy = Roo.get(existingProxy).dom;
23078     }
23079     /** @private */
23080     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23081     
23082     /** @private */
23083     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23084     
23085     /** @private */
23086     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23087     
23088     /** @private */
23089     this.dragSpecs = {};
23090     
23091     /**
23092      * @private The adapter to use to positon and resize elements
23093      */
23094     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23095     this.adapter.init(this);
23096     
23097     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23098         /** @private */
23099         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23100         this.el.addClass("x-splitbar-h");
23101     }else{
23102         /** @private */
23103         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23104         this.el.addClass("x-splitbar-v");
23105     }
23106     
23107     this.addEvents({
23108         /**
23109          * @event resize
23110          * Fires when the splitter is moved (alias for {@link #event-moved})
23111          * @param {Roo.SplitBar} this
23112          * @param {Number} newSize the new width or height
23113          */
23114         "resize" : true,
23115         /**
23116          * @event moved
23117          * Fires when the splitter is moved
23118          * @param {Roo.SplitBar} this
23119          * @param {Number} newSize the new width or height
23120          */
23121         "moved" : true,
23122         /**
23123          * @event beforeresize
23124          * Fires before the splitter is dragged
23125          * @param {Roo.SplitBar} this
23126          */
23127         "beforeresize" : true,
23128
23129         "beforeapply" : true
23130     });
23131
23132     Roo.util.Observable.call(this);
23133 };
23134
23135 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23136     onStartProxyDrag : function(x, y){
23137         this.fireEvent("beforeresize", this);
23138         if(!this.overlay){
23139             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23140             o.unselectable();
23141             o.enableDisplayMode("block");
23142             // all splitbars share the same overlay
23143             Roo.SplitBar.prototype.overlay = o;
23144         }
23145         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23146         this.overlay.show();
23147         Roo.get(this.proxy).setDisplayed("block");
23148         var size = this.adapter.getElementSize(this);
23149         this.activeMinSize = this.getMinimumSize();;
23150         this.activeMaxSize = this.getMaximumSize();;
23151         var c1 = size - this.activeMinSize;
23152         var c2 = Math.max(this.activeMaxSize - size, 0);
23153         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23154             this.dd.resetConstraints();
23155             this.dd.setXConstraint(
23156                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23157                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23158             );
23159             this.dd.setYConstraint(0, 0);
23160         }else{
23161             this.dd.resetConstraints();
23162             this.dd.setXConstraint(0, 0);
23163             this.dd.setYConstraint(
23164                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23165                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23166             );
23167          }
23168         this.dragSpecs.startSize = size;
23169         this.dragSpecs.startPoint = [x, y];
23170         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23171     },
23172     
23173     /** 
23174      * @private Called after the drag operation by the DDProxy
23175      */
23176     onEndProxyDrag : function(e){
23177         Roo.get(this.proxy).setDisplayed(false);
23178         var endPoint = Roo.lib.Event.getXY(e);
23179         if(this.overlay){
23180             this.overlay.hide();
23181         }
23182         var newSize;
23183         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23184             newSize = this.dragSpecs.startSize + 
23185                 (this.placement == Roo.SplitBar.LEFT ?
23186                     endPoint[0] - this.dragSpecs.startPoint[0] :
23187                     this.dragSpecs.startPoint[0] - endPoint[0]
23188                 );
23189         }else{
23190             newSize = this.dragSpecs.startSize + 
23191                 (this.placement == Roo.SplitBar.TOP ?
23192                     endPoint[1] - this.dragSpecs.startPoint[1] :
23193                     this.dragSpecs.startPoint[1] - endPoint[1]
23194                 );
23195         }
23196         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23197         if(newSize != this.dragSpecs.startSize){
23198             if(this.fireEvent('beforeapply', this, newSize) !== false){
23199                 this.adapter.setElementSize(this, newSize);
23200                 this.fireEvent("moved", this, newSize);
23201                 this.fireEvent("resize", this, newSize);
23202             }
23203         }
23204     },
23205     
23206     /**
23207      * Get the adapter this SplitBar uses
23208      * @return The adapter object
23209      */
23210     getAdapter : function(){
23211         return this.adapter;
23212     },
23213     
23214     /**
23215      * Set the adapter this SplitBar uses
23216      * @param {Object} adapter A SplitBar adapter object
23217      */
23218     setAdapter : function(adapter){
23219         this.adapter = adapter;
23220         this.adapter.init(this);
23221     },
23222     
23223     /**
23224      * Gets the minimum size for the resizing element
23225      * @return {Number} The minimum size
23226      */
23227     getMinimumSize : function(){
23228         return this.minSize;
23229     },
23230     
23231     /**
23232      * Sets the minimum size for the resizing element
23233      * @param {Number} minSize The minimum size
23234      */
23235     setMinimumSize : function(minSize){
23236         this.minSize = minSize;
23237     },
23238     
23239     /**
23240      * Gets the maximum size for the resizing element
23241      * @return {Number} The maximum size
23242      */
23243     getMaximumSize : function(){
23244         return this.maxSize;
23245     },
23246     
23247     /**
23248      * Sets the maximum size for the resizing element
23249      * @param {Number} maxSize The maximum size
23250      */
23251     setMaximumSize : function(maxSize){
23252         this.maxSize = maxSize;
23253     },
23254     
23255     /**
23256      * Sets the initialize size for the resizing element
23257      * @param {Number} size The initial size
23258      */
23259     setCurrentSize : function(size){
23260         var oldAnimate = this.animate;
23261         this.animate = false;
23262         this.adapter.setElementSize(this, size);
23263         this.animate = oldAnimate;
23264     },
23265     
23266     /**
23267      * Destroy this splitbar. 
23268      * @param {Boolean} removeEl True to remove the element
23269      */
23270     destroy : function(removeEl){
23271         if(this.shim){
23272             this.shim.remove();
23273         }
23274         this.dd.unreg();
23275         this.proxy.parentNode.removeChild(this.proxy);
23276         if(removeEl){
23277             this.el.remove();
23278         }
23279     }
23280 });
23281
23282 /**
23283  * @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.
23284  */
23285 Roo.SplitBar.createProxy = function(dir){
23286     var proxy = new Roo.Element(document.createElement("div"));
23287     proxy.unselectable();
23288     var cls = 'x-splitbar-proxy';
23289     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23290     document.body.appendChild(proxy.dom);
23291     return proxy.dom;
23292 };
23293
23294 /** 
23295  * @class Roo.SplitBar.BasicLayoutAdapter
23296  * Default Adapter. It assumes the splitter and resizing element are not positioned
23297  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23298  */
23299 Roo.SplitBar.BasicLayoutAdapter = function(){
23300 };
23301
23302 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23303     // do nothing for now
23304     init : function(s){
23305     
23306     },
23307     /**
23308      * Called before drag operations to get the current size of the resizing element. 
23309      * @param {Roo.SplitBar} s The SplitBar using this adapter
23310      */
23311      getElementSize : function(s){
23312         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23313             return s.resizingEl.getWidth();
23314         }else{
23315             return s.resizingEl.getHeight();
23316         }
23317     },
23318     
23319     /**
23320      * Called after drag operations to set the size of the resizing element.
23321      * @param {Roo.SplitBar} s The SplitBar using this adapter
23322      * @param {Number} newSize The new size to set
23323      * @param {Function} onComplete A function to be invoked when resizing is complete
23324      */
23325     setElementSize : function(s, newSize, onComplete){
23326         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23327             if(!s.animate){
23328                 s.resizingEl.setWidth(newSize);
23329                 if(onComplete){
23330                     onComplete(s, newSize);
23331                 }
23332             }else{
23333                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23334             }
23335         }else{
23336             
23337             if(!s.animate){
23338                 s.resizingEl.setHeight(newSize);
23339                 if(onComplete){
23340                     onComplete(s, newSize);
23341                 }
23342             }else{
23343                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23344             }
23345         }
23346     }
23347 };
23348
23349 /** 
23350  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23351  * @extends Roo.SplitBar.BasicLayoutAdapter
23352  * Adapter that  moves the splitter element to align with the resized sizing element. 
23353  * Used with an absolute positioned SplitBar.
23354  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23355  * document.body, make sure you assign an id to the body element.
23356  */
23357 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23358     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23359     this.container = Roo.get(container);
23360 };
23361
23362 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23363     init : function(s){
23364         this.basic.init(s);
23365     },
23366     
23367     getElementSize : function(s){
23368         return this.basic.getElementSize(s);
23369     },
23370     
23371     setElementSize : function(s, newSize, onComplete){
23372         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23373     },
23374     
23375     moveSplitter : function(s){
23376         var yes = Roo.SplitBar;
23377         switch(s.placement){
23378             case yes.LEFT:
23379                 s.el.setX(s.resizingEl.getRight());
23380                 break;
23381             case yes.RIGHT:
23382                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23383                 break;
23384             case yes.TOP:
23385                 s.el.setY(s.resizingEl.getBottom());
23386                 break;
23387             case yes.BOTTOM:
23388                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23389                 break;
23390         }
23391     }
23392 };
23393
23394 /**
23395  * Orientation constant - Create a vertical SplitBar
23396  * @static
23397  * @type Number
23398  */
23399 Roo.SplitBar.VERTICAL = 1;
23400
23401 /**
23402  * Orientation constant - Create a horizontal SplitBar
23403  * @static
23404  * @type Number
23405  */
23406 Roo.SplitBar.HORIZONTAL = 2;
23407
23408 /**
23409  * Placement constant - The resizing element is to the left of the splitter element
23410  * @static
23411  * @type Number
23412  */
23413 Roo.SplitBar.LEFT = 1;
23414
23415 /**
23416  * Placement constant - The resizing element is to the right of the splitter element
23417  * @static
23418  * @type Number
23419  */
23420 Roo.SplitBar.RIGHT = 2;
23421
23422 /**
23423  * Placement constant - The resizing element is positioned above the splitter element
23424  * @static
23425  * @type Number
23426  */
23427 Roo.SplitBar.TOP = 3;
23428
23429 /**
23430  * Placement constant - The resizing element is positioned under splitter element
23431  * @static
23432  * @type Number
23433  */
23434 Roo.SplitBar.BOTTOM = 4;
23435 /*
23436  * Based on:
23437  * Ext JS Library 1.1.1
23438  * Copyright(c) 2006-2007, Ext JS, LLC.
23439  *
23440  * Originally Released Under LGPL - original licence link has changed is not relivant.
23441  *
23442  * Fork - LGPL
23443  * <script type="text/javascript">
23444  */
23445
23446 /**
23447  * @class Roo.View
23448  * @extends Roo.util.Observable
23449  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23450  * This class also supports single and multi selection modes. <br>
23451  * Create a data model bound view:
23452  <pre><code>
23453  var store = new Roo.data.Store(...);
23454
23455  var view = new Roo.View({
23456     el : "my-element",
23457     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23458  
23459     singleSelect: true,
23460     selectedClass: "ydataview-selected",
23461     store: store
23462  });
23463
23464  // listen for node click?
23465  view.on("click", function(vw, index, node, e){
23466  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23467  });
23468
23469  // load XML data
23470  dataModel.load("foobar.xml");
23471  </code></pre>
23472  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23473  * <br><br>
23474  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23475  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23476  * 
23477  * Note: old style constructor is still suported (container, template, config)
23478  * 
23479  * @constructor
23480  * Create a new View
23481  * @param {Object} config The config object
23482  * 
23483  */
23484 Roo.View = function(config, depreciated_tpl, depreciated_config){
23485     
23486     if (typeof(depreciated_tpl) == 'undefined') {
23487         // new way.. - universal constructor.
23488         Roo.apply(this, config);
23489         this.el  = Roo.get(this.el);
23490     } else {
23491         // old format..
23492         this.el  = Roo.get(config);
23493         this.tpl = depreciated_tpl;
23494         Roo.apply(this, depreciated_config);
23495     }
23496      
23497     
23498     if(typeof(this.tpl) == "string"){
23499         this.tpl = new Roo.Template(this.tpl);
23500     } else {
23501         // support xtype ctors..
23502         this.tpl = new Roo.factory(this.tpl, Roo);
23503     }
23504     
23505     
23506     this.tpl.compile();
23507    
23508
23509      
23510     /** @private */
23511     this.addEvents({
23512     /**
23513      * @event beforeclick
23514      * Fires before a click is processed. Returns false to cancel the default action.
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         "beforeclick" : true,
23521     /**
23522      * @event click
23523      * Fires when a template node is 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         "click" : true,
23530     /**
23531      * @event dblclick
23532      * Fires when a template node is double 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         "dblclick" : true,
23539     /**
23540      * @event contextmenu
23541      * Fires when a template node is right clicked.
23542      * @param {Roo.View} this
23543      * @param {Number} index The index of the target node
23544      * @param {HTMLElement} node The target node
23545      * @param {Roo.EventObject} e The raw event object
23546      */
23547         "contextmenu" : true,
23548     /**
23549      * @event selectionchange
23550      * Fires when the selected nodes change.
23551      * @param {Roo.View} this
23552      * @param {Array} selections Array of the selected nodes
23553      */
23554         "selectionchange" : true,
23555
23556     /**
23557      * @event beforeselect
23558      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23559      * @param {Roo.View} this
23560      * @param {HTMLElement} node The node to be selected
23561      * @param {Array} selections Array of currently selected nodes
23562      */
23563         "beforeselect" : true
23564     });
23565
23566     this.el.on({
23567         "click": this.onClick,
23568         "dblclick": this.onDblClick,
23569         "contextmenu": this.onContextMenu,
23570         scope:this
23571     });
23572
23573     this.selections = [];
23574     this.nodes = [];
23575     this.cmp = new Roo.CompositeElementLite([]);
23576     if(this.store){
23577         this.store = Roo.factory(this.store, Roo.data);
23578         this.setStore(this.store, true);
23579     }
23580     Roo.View.superclass.constructor.call(this);
23581 };
23582
23583 Roo.extend(Roo.View, Roo.util.Observable, {
23584     
23585      /**
23586      * @cfg {Roo.data.Store} store Data store to load data from.
23587      */
23588     store : false,
23589     
23590     /**
23591      * @cfg {String|Roo.Element} el The container element.
23592      */
23593     el : '',
23594     
23595     /**
23596      * @cfg {String|Roo.Template} tpl The template used by this View 
23597      */
23598     tpl : false,
23599     
23600     /**
23601      * @cfg {String} selectedClass The css class to add to selected nodes
23602      */
23603     selectedClass : "x-view-selected",
23604      /**
23605      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23606      */
23607     emptyText : "",
23608     /**
23609      * @cfg {Boolean} multiSelect Allow multiple selection
23610      */
23611     
23612     multiSelect : false,
23613     /**
23614      * @cfg {Boolean} singleSelect Allow single selection
23615      */
23616     singleSelect:  false,
23617     
23618     /**
23619      * Returns the element this view is bound to.
23620      * @return {Roo.Element}
23621      */
23622     getEl : function(){
23623         return this.el;
23624     },
23625
23626     /**
23627      * Refreshes the view.
23628      */
23629     refresh : function(){
23630         var t = this.tpl;
23631         this.clearSelections();
23632         this.el.update("");
23633         var html = [];
23634         var records = this.store.getRange();
23635         if(records.length < 1){
23636             this.el.update(this.emptyText);
23637             return;
23638         }
23639         for(var i = 0, len = records.length; i < len; i++){
23640             var data = this.prepareData(records[i].data, i, records[i]);
23641             html[html.length] = t.apply(data);
23642         }
23643         this.el.update(html.join(""));
23644         this.nodes = this.el.dom.childNodes;
23645         this.updateIndexes(0);
23646     },
23647
23648     /**
23649      * Function to override to reformat the data that is sent to
23650      * the template for each node.
23651      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23652      * a JSON object for an UpdateManager bound view).
23653      */
23654     prepareData : function(data){
23655         return data;
23656     },
23657
23658     onUpdate : function(ds, record){
23659         this.clearSelections();
23660         var index = this.store.indexOf(record);
23661         var n = this.nodes[index];
23662         this.tpl.insertBefore(n, this.prepareData(record.data));
23663         n.parentNode.removeChild(n);
23664         this.updateIndexes(index, index);
23665     },
23666
23667     onAdd : function(ds, records, index){
23668         this.clearSelections();
23669         if(this.nodes.length == 0){
23670             this.refresh();
23671             return;
23672         }
23673         var n = this.nodes[index];
23674         for(var i = 0, len = records.length; i < len; i++){
23675             var d = this.prepareData(records[i].data);
23676             if(n){
23677                 this.tpl.insertBefore(n, d);
23678             }else{
23679                 this.tpl.append(this.el, d);
23680             }
23681         }
23682         this.updateIndexes(index);
23683     },
23684
23685     onRemove : function(ds, record, index){
23686         this.clearSelections();
23687         this.el.dom.removeChild(this.nodes[index]);
23688         this.updateIndexes(index);
23689     },
23690
23691     /**
23692      * Refresh an individual node.
23693      * @param {Number} index
23694      */
23695     refreshNode : function(index){
23696         this.onUpdate(this.store, this.store.getAt(index));
23697     },
23698
23699     updateIndexes : function(startIndex, endIndex){
23700         var ns = this.nodes;
23701         startIndex = startIndex || 0;
23702         endIndex = endIndex || ns.length - 1;
23703         for(var i = startIndex; i <= endIndex; i++){
23704             ns[i].nodeIndex = i;
23705         }
23706     },
23707
23708     /**
23709      * Changes the data store this view uses and refresh the view.
23710      * @param {Store} store
23711      */
23712     setStore : function(store, initial){
23713         if(!initial && this.store){
23714             this.store.un("datachanged", this.refresh);
23715             this.store.un("add", this.onAdd);
23716             this.store.un("remove", this.onRemove);
23717             this.store.un("update", this.onUpdate);
23718             this.store.un("clear", this.refresh);
23719         }
23720         if(store){
23721           
23722             store.on("datachanged", this.refresh, this);
23723             store.on("add", this.onAdd, this);
23724             store.on("remove", this.onRemove, this);
23725             store.on("update", this.onUpdate, this);
23726             store.on("clear", this.refresh, this);
23727         }
23728         
23729         if(store){
23730             this.refresh();
23731         }
23732     },
23733
23734     /**
23735      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23736      * @param {HTMLElement} node
23737      * @return {HTMLElement} The template node
23738      */
23739     findItemFromChild : function(node){
23740         var el = this.el.dom;
23741         if(!node || node.parentNode == el){
23742                     return node;
23743             }
23744             var p = node.parentNode;
23745             while(p && p != el){
23746             if(p.parentNode == el){
23747                 return p;
23748             }
23749             p = p.parentNode;
23750         }
23751             return null;
23752     },
23753
23754     /** @ignore */
23755     onClick : function(e){
23756         var item = this.findItemFromChild(e.getTarget());
23757         if(item){
23758             var index = this.indexOf(item);
23759             if(this.onItemClick(item, index, e) !== false){
23760                 this.fireEvent("click", this, index, item, e);
23761             }
23762         }else{
23763             this.clearSelections();
23764         }
23765     },
23766
23767     /** @ignore */
23768     onContextMenu : function(e){
23769         var item = this.findItemFromChild(e.getTarget());
23770         if(item){
23771             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23772         }
23773     },
23774
23775     /** @ignore */
23776     onDblClick : function(e){
23777         var item = this.findItemFromChild(e.getTarget());
23778         if(item){
23779             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23780         }
23781     },
23782
23783     onItemClick : function(item, index, e){
23784         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23785             return false;
23786         }
23787         if(this.multiSelect || this.singleSelect){
23788             if(this.multiSelect && e.shiftKey && this.lastSelection){
23789                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23790             }else{
23791                 this.select(item, this.multiSelect && e.ctrlKey);
23792                 this.lastSelection = item;
23793             }
23794             e.preventDefault();
23795         }
23796         return true;
23797     },
23798
23799     /**
23800      * Get the number of selected nodes.
23801      * @return {Number}
23802      */
23803     getSelectionCount : function(){
23804         return this.selections.length;
23805     },
23806
23807     /**
23808      * Get the currently selected nodes.
23809      * @return {Array} An array of HTMLElements
23810      */
23811     getSelectedNodes : function(){
23812         return this.selections;
23813     },
23814
23815     /**
23816      * Get the indexes of the selected nodes.
23817      * @return {Array}
23818      */
23819     getSelectedIndexes : function(){
23820         var indexes = [], s = this.selections;
23821         for(var i = 0, len = s.length; i < len; i++){
23822             indexes.push(s[i].nodeIndex);
23823         }
23824         return indexes;
23825     },
23826
23827     /**
23828      * Clear all selections
23829      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23830      */
23831     clearSelections : function(suppressEvent){
23832         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23833             this.cmp.elements = this.selections;
23834             this.cmp.removeClass(this.selectedClass);
23835             this.selections = [];
23836             if(!suppressEvent){
23837                 this.fireEvent("selectionchange", this, this.selections);
23838             }
23839         }
23840     },
23841
23842     /**
23843      * Returns true if the passed node is selected
23844      * @param {HTMLElement/Number} node The node or node index
23845      * @return {Boolean}
23846      */
23847     isSelected : function(node){
23848         var s = this.selections;
23849         if(s.length < 1){
23850             return false;
23851         }
23852         node = this.getNode(node);
23853         return s.indexOf(node) !== -1;
23854     },
23855
23856     /**
23857      * Selects nodes.
23858      * @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
23859      * @param {Boolean} keepExisting (optional) true to keep existing selections
23860      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23861      */
23862     select : function(nodeInfo, keepExisting, suppressEvent){
23863         if(nodeInfo instanceof Array){
23864             if(!keepExisting){
23865                 this.clearSelections(true);
23866             }
23867             for(var i = 0, len = nodeInfo.length; i < len; i++){
23868                 this.select(nodeInfo[i], true, true);
23869             }
23870         } else{
23871             var node = this.getNode(nodeInfo);
23872             if(node && !this.isSelected(node)){
23873                 if(!keepExisting){
23874                     this.clearSelections(true);
23875                 }
23876                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23877                     Roo.fly(node).addClass(this.selectedClass);
23878                     this.selections.push(node);
23879                     if(!suppressEvent){
23880                         this.fireEvent("selectionchange", this, this.selections);
23881                     }
23882                 }
23883             }
23884         }
23885     },
23886
23887     /**
23888      * Gets a template node.
23889      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23890      * @return {HTMLElement} The node or null if it wasn't found
23891      */
23892     getNode : function(nodeInfo){
23893         if(typeof nodeInfo == "string"){
23894             return document.getElementById(nodeInfo);
23895         }else if(typeof nodeInfo == "number"){
23896             return this.nodes[nodeInfo];
23897         }
23898         return nodeInfo;
23899     },
23900
23901     /**
23902      * Gets a range template nodes.
23903      * @param {Number} startIndex
23904      * @param {Number} endIndex
23905      * @return {Array} An array of nodes
23906      */
23907     getNodes : function(start, end){
23908         var ns = this.nodes;
23909         start = start || 0;
23910         end = typeof end == "undefined" ? ns.length - 1 : end;
23911         var nodes = [];
23912         if(start <= end){
23913             for(var i = start; i <= end; i++){
23914                 nodes.push(ns[i]);
23915             }
23916         } else{
23917             for(var i = start; i >= end; i--){
23918                 nodes.push(ns[i]);
23919             }
23920         }
23921         return nodes;
23922     },
23923
23924     /**
23925      * Finds the index of the passed node
23926      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23927      * @return {Number} The index of the node or -1
23928      */
23929     indexOf : function(node){
23930         node = this.getNode(node);
23931         if(typeof node.nodeIndex == "number"){
23932             return node.nodeIndex;
23933         }
23934         var ns = this.nodes;
23935         for(var i = 0, len = ns.length; i < len; i++){
23936             if(ns[i] == node){
23937                 return i;
23938             }
23939         }
23940         return -1;
23941     }
23942 });
23943 /*
23944  * Based on:
23945  * Ext JS Library 1.1.1
23946  * Copyright(c) 2006-2007, Ext JS, LLC.
23947  *
23948  * Originally Released Under LGPL - original licence link has changed is not relivant.
23949  *
23950  * Fork - LGPL
23951  * <script type="text/javascript">
23952  */
23953
23954 /**
23955  * @class Roo.JsonView
23956  * @extends Roo.View
23957  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23958 <pre><code>
23959 var view = new Roo.JsonView({
23960     container: "my-element",
23961     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23962     multiSelect: true, 
23963     jsonRoot: "data" 
23964 });
23965
23966 // listen for node click?
23967 view.on("click", function(vw, index, node, e){
23968     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23969 });
23970
23971 // direct load of JSON data
23972 view.load("foobar.php");
23973
23974 // Example from my blog list
23975 var tpl = new Roo.Template(
23976     '&lt;div class="entry"&gt;' +
23977     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23978     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23979     "&lt;/div&gt;&lt;hr /&gt;"
23980 );
23981
23982 var moreView = new Roo.JsonView({
23983     container :  "entry-list", 
23984     template : tpl,
23985     jsonRoot: "posts"
23986 });
23987 moreView.on("beforerender", this.sortEntries, this);
23988 moreView.load({
23989     url: "/blog/get-posts.php",
23990     params: "allposts=true",
23991     text: "Loading Blog Entries..."
23992 });
23993 </code></pre>
23994
23995 * Note: old code is supported with arguments : (container, template, config)
23996
23997
23998  * @constructor
23999  * Create a new JsonView
24000  * 
24001  * @param {Object} config The config object
24002  * 
24003  */
24004 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24005     
24006     
24007     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24008
24009     var um = this.el.getUpdateManager();
24010     um.setRenderer(this);
24011     um.on("update", this.onLoad, this);
24012     um.on("failure", this.onLoadException, this);
24013
24014     /**
24015      * @event beforerender
24016      * Fires before rendering of the downloaded JSON data.
24017      * @param {Roo.JsonView} this
24018      * @param {Object} data The JSON data loaded
24019      */
24020     /**
24021      * @event load
24022      * Fires when data is loaded.
24023      * @param {Roo.JsonView} this
24024      * @param {Object} data The JSON data loaded
24025      * @param {Object} response The raw Connect response object
24026      */
24027     /**
24028      * @event loadexception
24029      * Fires when loading fails.
24030      * @param {Roo.JsonView} this
24031      * @param {Object} response The raw Connect response object
24032      */
24033     this.addEvents({
24034         'beforerender' : true,
24035         'load' : true,
24036         'loadexception' : true
24037     });
24038 };
24039 Roo.extend(Roo.JsonView, Roo.View, {
24040     /**
24041      * @type {String} The root property in the loaded JSON object that contains the data
24042      */
24043     jsonRoot : "",
24044
24045     /**
24046      * Refreshes the view.
24047      */
24048     refresh : function(){
24049         this.clearSelections();
24050         this.el.update("");
24051         var html = [];
24052         var o = this.jsonData;
24053         if(o && o.length > 0){
24054             for(var i = 0, len = o.length; i < len; i++){
24055                 var data = this.prepareData(o[i], i, o);
24056                 html[html.length] = this.tpl.apply(data);
24057             }
24058         }else{
24059             html.push(this.emptyText);
24060         }
24061         this.el.update(html.join(""));
24062         this.nodes = this.el.dom.childNodes;
24063         this.updateIndexes(0);
24064     },
24065
24066     /**
24067      * 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.
24068      * @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:
24069      <pre><code>
24070      view.load({
24071          url: "your-url.php",
24072          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24073          callback: yourFunction,
24074          scope: yourObject, //(optional scope)
24075          discardUrl: false,
24076          nocache: false,
24077          text: "Loading...",
24078          timeout: 30,
24079          scripts: false
24080      });
24081      </code></pre>
24082      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24083      * 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.
24084      * @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}
24085      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24086      * @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.
24087      */
24088     load : function(){
24089         var um = this.el.getUpdateManager();
24090         um.update.apply(um, arguments);
24091     },
24092
24093     render : function(el, response){
24094         this.clearSelections();
24095         this.el.update("");
24096         var o;
24097         try{
24098             o = Roo.util.JSON.decode(response.responseText);
24099             if(this.jsonRoot){
24100                 
24101                 o = o[this.jsonRoot];
24102             }
24103         } catch(e){
24104         }
24105         /**
24106          * The current JSON data or null
24107          */
24108         this.jsonData = o;
24109         this.beforeRender();
24110         this.refresh();
24111     },
24112
24113 /**
24114  * Get the number of records in the current JSON dataset
24115  * @return {Number}
24116  */
24117     getCount : function(){
24118         return this.jsonData ? this.jsonData.length : 0;
24119     },
24120
24121 /**
24122  * Returns the JSON object for the specified node(s)
24123  * @param {HTMLElement/Array} node The node or an array of nodes
24124  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24125  * you get the JSON object for the node
24126  */
24127     getNodeData : function(node){
24128         if(node instanceof Array){
24129             var data = [];
24130             for(var i = 0, len = node.length; i < len; i++){
24131                 data.push(this.getNodeData(node[i]));
24132             }
24133             return data;
24134         }
24135         return this.jsonData[this.indexOf(node)] || null;
24136     },
24137
24138     beforeRender : function(){
24139         this.snapshot = this.jsonData;
24140         if(this.sortInfo){
24141             this.sort.apply(this, this.sortInfo);
24142         }
24143         this.fireEvent("beforerender", this, this.jsonData);
24144     },
24145
24146     onLoad : function(el, o){
24147         this.fireEvent("load", this, this.jsonData, o);
24148     },
24149
24150     onLoadException : function(el, o){
24151         this.fireEvent("loadexception", this, o);
24152     },
24153
24154 /**
24155  * Filter the data by a specific property.
24156  * @param {String} property A property on your JSON objects
24157  * @param {String/RegExp} value Either string that the property values
24158  * should start with, or a RegExp to test against the property
24159  */
24160     filter : function(property, value){
24161         if(this.jsonData){
24162             var data = [];
24163             var ss = this.snapshot;
24164             if(typeof value == "string"){
24165                 var vlen = value.length;
24166                 if(vlen == 0){
24167                     this.clearFilter();
24168                     return;
24169                 }
24170                 value = value.toLowerCase();
24171                 for(var i = 0, len = ss.length; i < len; i++){
24172                     var o = ss[i];
24173                     if(o[property].substr(0, vlen).toLowerCase() == value){
24174                         data.push(o);
24175                     }
24176                 }
24177             } else if(value.exec){ // regex?
24178                 for(var i = 0, len = ss.length; i < len; i++){
24179                     var o = ss[i];
24180                     if(value.test(o[property])){
24181                         data.push(o);
24182                     }
24183                 }
24184             } else{
24185                 return;
24186             }
24187             this.jsonData = data;
24188             this.refresh();
24189         }
24190     },
24191
24192 /**
24193  * Filter by a function. The passed function will be called with each
24194  * object in the current dataset. If the function returns true the value is kept,
24195  * otherwise it is filtered.
24196  * @param {Function} fn
24197  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24198  */
24199     filterBy : function(fn, scope){
24200         if(this.jsonData){
24201             var data = [];
24202             var ss = this.snapshot;
24203             for(var i = 0, len = ss.length; i < len; i++){
24204                 var o = ss[i];
24205                 if(fn.call(scope || this, o)){
24206                     data.push(o);
24207                 }
24208             }
24209             this.jsonData = data;
24210             this.refresh();
24211         }
24212     },
24213
24214 /**
24215  * Clears the current filter.
24216  */
24217     clearFilter : function(){
24218         if(this.snapshot && this.jsonData != this.snapshot){
24219             this.jsonData = this.snapshot;
24220             this.refresh();
24221         }
24222     },
24223
24224
24225 /**
24226  * Sorts the data for this view and refreshes it.
24227  * @param {String} property A property on your JSON objects to sort on
24228  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24229  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24230  */
24231     sort : function(property, dir, sortType){
24232         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24233         if(this.jsonData){
24234             var p = property;
24235             var dsc = dir && dir.toLowerCase() == "desc";
24236             var f = function(o1, o2){
24237                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24238                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24239                 ;
24240                 if(v1 < v2){
24241                     return dsc ? +1 : -1;
24242                 } else if(v1 > v2){
24243                     return dsc ? -1 : +1;
24244                 } else{
24245                     return 0;
24246                 }
24247             };
24248             this.jsonData.sort(f);
24249             this.refresh();
24250             if(this.jsonData != this.snapshot){
24251                 this.snapshot.sort(f);
24252             }
24253         }
24254     }
24255 });/*
24256  * Based on:
24257  * Ext JS Library 1.1.1
24258  * Copyright(c) 2006-2007, Ext JS, LLC.
24259  *
24260  * Originally Released Under LGPL - original licence link has changed is not relivant.
24261  *
24262  * Fork - LGPL
24263  * <script type="text/javascript">
24264  */
24265  
24266
24267 /**
24268  * @class Roo.ColorPalette
24269  * @extends Roo.Component
24270  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24271  * Here's an example of typical usage:
24272  * <pre><code>
24273 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24274 cp.render('my-div');
24275
24276 cp.on('select', function(palette, selColor){
24277     // do something with selColor
24278 });
24279 </code></pre>
24280  * @constructor
24281  * Create a new ColorPalette
24282  * @param {Object} config The config object
24283  */
24284 Roo.ColorPalette = function(config){
24285     Roo.ColorPalette.superclass.constructor.call(this, config);
24286     this.addEvents({
24287         /**
24288              * @event select
24289              * Fires when a color is selected
24290              * @param {ColorPalette} this
24291              * @param {String} color The 6-digit color hex code (without the # symbol)
24292              */
24293         select: true
24294     });
24295
24296     if(this.handler){
24297         this.on("select", this.handler, this.scope, true);
24298     }
24299 };
24300 Roo.extend(Roo.ColorPalette, Roo.Component, {
24301     /**
24302      * @cfg {String} itemCls
24303      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24304      */
24305     itemCls : "x-color-palette",
24306     /**
24307      * @cfg {String} value
24308      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24309      * the hex codes are case-sensitive.
24310      */
24311     value : null,
24312     clickEvent:'click',
24313     // private
24314     ctype: "Roo.ColorPalette",
24315
24316     /**
24317      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24318      */
24319     allowReselect : false,
24320
24321     /**
24322      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24323      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24324      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24325      * of colors with the width setting until the box is symmetrical.</p>
24326      * <p>You can override individual colors if needed:</p>
24327      * <pre><code>
24328 var cp = new Roo.ColorPalette();
24329 cp.colors[0] = "FF0000";  // change the first box to red
24330 </code></pre>
24331
24332 Or you can provide a custom array of your own for complete control:
24333 <pre><code>
24334 var cp = new Roo.ColorPalette();
24335 cp.colors = ["000000", "993300", "333300"];
24336 </code></pre>
24337      * @type Array
24338      */
24339     colors : [
24340         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24341         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24342         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24343         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24344         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24345     ],
24346
24347     // private
24348     onRender : function(container, position){
24349         var t = new Roo.MasterTemplate(
24350             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24351         );
24352         var c = this.colors;
24353         for(var i = 0, len = c.length; i < len; i++){
24354             t.add([c[i]]);
24355         }
24356         var el = document.createElement("div");
24357         el.className = this.itemCls;
24358         t.overwrite(el);
24359         container.dom.insertBefore(el, position);
24360         this.el = Roo.get(el);
24361         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24362         if(this.clickEvent != 'click'){
24363             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24364         }
24365     },
24366
24367     // private
24368     afterRender : function(){
24369         Roo.ColorPalette.superclass.afterRender.call(this);
24370         if(this.value){
24371             var s = this.value;
24372             this.value = null;
24373             this.select(s);
24374         }
24375     },
24376
24377     // private
24378     handleClick : function(e, t){
24379         e.preventDefault();
24380         if(!this.disabled){
24381             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24382             this.select(c.toUpperCase());
24383         }
24384     },
24385
24386     /**
24387      * Selects the specified color in the palette (fires the select event)
24388      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24389      */
24390     select : function(color){
24391         color = color.replace("#", "");
24392         if(color != this.value || this.allowReselect){
24393             var el = this.el;
24394             if(this.value){
24395                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24396             }
24397             el.child("a.color-"+color).addClass("x-color-palette-sel");
24398             this.value = color;
24399             this.fireEvent("select", this, color);
24400         }
24401     }
24402 });/*
24403  * Based on:
24404  * Ext JS Library 1.1.1
24405  * Copyright(c) 2006-2007, Ext JS, LLC.
24406  *
24407  * Originally Released Under LGPL - original licence link has changed is not relivant.
24408  *
24409  * Fork - LGPL
24410  * <script type="text/javascript">
24411  */
24412  
24413 /**
24414  * @class Roo.DatePicker
24415  * @extends Roo.Component
24416  * Simple date picker class.
24417  * @constructor
24418  * Create a new DatePicker
24419  * @param {Object} config The config object
24420  */
24421 Roo.DatePicker = function(config){
24422     Roo.DatePicker.superclass.constructor.call(this, config);
24423
24424     this.value = config && config.value ?
24425                  config.value.clearTime() : new Date().clearTime();
24426
24427     this.addEvents({
24428         /**
24429              * @event select
24430              * Fires when a date is selected
24431              * @param {DatePicker} this
24432              * @param {Date} date The selected date
24433              */
24434         select: true
24435     });
24436
24437     if(this.handler){
24438         this.on("select", this.handler,  this.scope || this);
24439     }
24440     // build the disabledDatesRE
24441     if(!this.disabledDatesRE && this.disabledDates){
24442         var dd = this.disabledDates;
24443         var re = "(?:";
24444         for(var i = 0; i < dd.length; i++){
24445             re += dd[i];
24446             if(i != dd.length-1) re += "|";
24447         }
24448         this.disabledDatesRE = new RegExp(re + ")");
24449     }
24450 };
24451
24452 Roo.extend(Roo.DatePicker, Roo.Component, {
24453     /**
24454      * @cfg {String} todayText
24455      * The text to display on the button that selects the current date (defaults to "Today")
24456      */
24457     todayText : "Today",
24458     /**
24459      * @cfg {String} okText
24460      * The text to display on the ok button
24461      */
24462     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24463     /**
24464      * @cfg {String} cancelText
24465      * The text to display on the cancel button
24466      */
24467     cancelText : "Cancel",
24468     /**
24469      * @cfg {String} todayTip
24470      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24471      */
24472     todayTip : "{0} (Spacebar)",
24473     /**
24474      * @cfg {Date} minDate
24475      * Minimum allowable date (JavaScript date object, defaults to null)
24476      */
24477     minDate : null,
24478     /**
24479      * @cfg {Date} maxDate
24480      * Maximum allowable date (JavaScript date object, defaults to null)
24481      */
24482     maxDate : null,
24483     /**
24484      * @cfg {String} minText
24485      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24486      */
24487     minText : "This date is before the minimum date",
24488     /**
24489      * @cfg {String} maxText
24490      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24491      */
24492     maxText : "This date is after the maximum date",
24493     /**
24494      * @cfg {String} format
24495      * The default date format string which can be overriden for localization support.  The format must be
24496      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24497      */
24498     format : "m/d/y",
24499     /**
24500      * @cfg {Array} disabledDays
24501      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24502      */
24503     disabledDays : null,
24504     /**
24505      * @cfg {String} disabledDaysText
24506      * The tooltip to display when the date falls on a disabled day (defaults to "")
24507      */
24508     disabledDaysText : "",
24509     /**
24510      * @cfg {RegExp} disabledDatesRE
24511      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24512      */
24513     disabledDatesRE : null,
24514     /**
24515      * @cfg {String} disabledDatesText
24516      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24517      */
24518     disabledDatesText : "",
24519     /**
24520      * @cfg {Boolean} constrainToViewport
24521      * True to constrain the date picker to the viewport (defaults to true)
24522      */
24523     constrainToViewport : true,
24524     /**
24525      * @cfg {Array} monthNames
24526      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24527      */
24528     monthNames : Date.monthNames,
24529     /**
24530      * @cfg {Array} dayNames
24531      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24532      */
24533     dayNames : Date.dayNames,
24534     /**
24535      * @cfg {String} nextText
24536      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24537      */
24538     nextText: 'Next Month (Control+Right)',
24539     /**
24540      * @cfg {String} prevText
24541      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24542      */
24543     prevText: 'Previous Month (Control+Left)',
24544     /**
24545      * @cfg {String} monthYearText
24546      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24547      */
24548     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24549     /**
24550      * @cfg {Number} startDay
24551      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24552      */
24553     startDay : 0,
24554     /**
24555      * @cfg {Bool} showClear
24556      * Show a clear button (usefull for date form elements that can be blank.)
24557      */
24558     
24559     showClear: false,
24560     
24561     /**
24562      * Sets the value of the date field
24563      * @param {Date} value The date to set
24564      */
24565     setValue : function(value){
24566         var old = this.value;
24567         this.value = value.clearTime(true);
24568         if(this.el){
24569             this.update(this.value);
24570         }
24571     },
24572
24573     /**
24574      * Gets the current selected value of the date field
24575      * @return {Date} The selected date
24576      */
24577     getValue : function(){
24578         return this.value;
24579     },
24580
24581     // private
24582     focus : function(){
24583         if(this.el){
24584             this.update(this.activeDate);
24585         }
24586     },
24587
24588     // private
24589     onRender : function(container, position){
24590         var m = [
24591              '<table cellspacing="0">',
24592                 '<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>',
24593                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24594         var dn = this.dayNames;
24595         for(var i = 0; i < 7; i++){
24596             var d = this.startDay+i;
24597             if(d > 6){
24598                 d = d-7;
24599             }
24600             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24601         }
24602         m[m.length] = "</tr></thead><tbody><tr>";
24603         for(var i = 0; i < 42; i++) {
24604             if(i % 7 == 0 && i != 0){
24605                 m[m.length] = "</tr><tr>";
24606             }
24607             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24608         }
24609         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24610             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24611
24612         var el = document.createElement("div");
24613         el.className = "x-date-picker";
24614         el.innerHTML = m.join("");
24615
24616         container.dom.insertBefore(el, position);
24617
24618         this.el = Roo.get(el);
24619         this.eventEl = Roo.get(el.firstChild);
24620
24621         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24622             handler: this.showPrevMonth,
24623             scope: this,
24624             preventDefault:true,
24625             stopDefault:true
24626         });
24627
24628         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24629             handler: this.showNextMonth,
24630             scope: this,
24631             preventDefault:true,
24632             stopDefault:true
24633         });
24634
24635         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24636
24637         this.monthPicker = this.el.down('div.x-date-mp');
24638         this.monthPicker.enableDisplayMode('block');
24639         
24640         var kn = new Roo.KeyNav(this.eventEl, {
24641             "left" : function(e){
24642                 e.ctrlKey ?
24643                     this.showPrevMonth() :
24644                     this.update(this.activeDate.add("d", -1));
24645             },
24646
24647             "right" : function(e){
24648                 e.ctrlKey ?
24649                     this.showNextMonth() :
24650                     this.update(this.activeDate.add("d", 1));
24651             },
24652
24653             "up" : function(e){
24654                 e.ctrlKey ?
24655                     this.showNextYear() :
24656                     this.update(this.activeDate.add("d", -7));
24657             },
24658
24659             "down" : function(e){
24660                 e.ctrlKey ?
24661                     this.showPrevYear() :
24662                     this.update(this.activeDate.add("d", 7));
24663             },
24664
24665             "pageUp" : function(e){
24666                 this.showNextMonth();
24667             },
24668
24669             "pageDown" : function(e){
24670                 this.showPrevMonth();
24671             },
24672
24673             "enter" : function(e){
24674                 e.stopPropagation();
24675                 return true;
24676             },
24677
24678             scope : this
24679         });
24680
24681         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24682
24683         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24684
24685         this.el.unselectable();
24686         
24687         this.cells = this.el.select("table.x-date-inner tbody td");
24688         this.textNodes = this.el.query("table.x-date-inner tbody span");
24689
24690         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24691             text: "&#160;",
24692             tooltip: this.monthYearText
24693         });
24694
24695         this.mbtn.on('click', this.showMonthPicker, this);
24696         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24697
24698
24699         var today = (new Date()).dateFormat(this.format);
24700         
24701         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24702         if (this.showClear) {
24703             baseTb.add( new Roo.Toolbar.Fill());
24704         }
24705         baseTb.add({
24706             text: String.format(this.todayText, today),
24707             tooltip: String.format(this.todayTip, today),
24708             handler: this.selectToday,
24709             scope: this
24710         });
24711         
24712         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24713             
24714         //});
24715         if (this.showClear) {
24716             
24717             baseTb.add( new Roo.Toolbar.Fill());
24718             baseTb.add({
24719                 text: '&#160;',
24720                 cls: 'x-btn-icon x-btn-clear',
24721                 handler: function() {
24722                     //this.value = '';
24723                     this.fireEvent("select", this, '');
24724                 },
24725                 scope: this
24726             });
24727         }
24728         
24729         
24730         if(Roo.isIE){
24731             this.el.repaint();
24732         }
24733         this.update(this.value);
24734     },
24735
24736     createMonthPicker : function(){
24737         if(!this.monthPicker.dom.firstChild){
24738             var buf = ['<table border="0" cellspacing="0">'];
24739             for(var i = 0; i < 6; i++){
24740                 buf.push(
24741                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24742                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24743                     i == 0 ?
24744                     '<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>' :
24745                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24746                 );
24747             }
24748             buf.push(
24749                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24750                     this.okText,
24751                     '</button><button type="button" class="x-date-mp-cancel">',
24752                     this.cancelText,
24753                     '</button></td></tr>',
24754                 '</table>'
24755             );
24756             this.monthPicker.update(buf.join(''));
24757             this.monthPicker.on('click', this.onMonthClick, this);
24758             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24759
24760             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24761             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24762
24763             this.mpMonths.each(function(m, a, i){
24764                 i += 1;
24765                 if((i%2) == 0){
24766                     m.dom.xmonth = 5 + Math.round(i * .5);
24767                 }else{
24768                     m.dom.xmonth = Math.round((i-1) * .5);
24769                 }
24770             });
24771         }
24772     },
24773
24774     showMonthPicker : function(){
24775         this.createMonthPicker();
24776         var size = this.el.getSize();
24777         this.monthPicker.setSize(size);
24778         this.monthPicker.child('table').setSize(size);
24779
24780         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24781         this.updateMPMonth(this.mpSelMonth);
24782         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24783         this.updateMPYear(this.mpSelYear);
24784
24785         this.monthPicker.slideIn('t', {duration:.2});
24786     },
24787
24788     updateMPYear : function(y){
24789         this.mpyear = y;
24790         var ys = this.mpYears.elements;
24791         for(var i = 1; i <= 10; i++){
24792             var td = ys[i-1], y2;
24793             if((i%2) == 0){
24794                 y2 = y + Math.round(i * .5);
24795                 td.firstChild.innerHTML = y2;
24796                 td.xyear = y2;
24797             }else{
24798                 y2 = y - (5-Math.round(i * .5));
24799                 td.firstChild.innerHTML = y2;
24800                 td.xyear = y2;
24801             }
24802             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24803         }
24804     },
24805
24806     updateMPMonth : function(sm){
24807         this.mpMonths.each(function(m, a, i){
24808             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24809         });
24810     },
24811
24812     selectMPMonth: function(m){
24813         
24814     },
24815
24816     onMonthClick : function(e, t){
24817         e.stopEvent();
24818         var el = new Roo.Element(t), pn;
24819         if(el.is('button.x-date-mp-cancel')){
24820             this.hideMonthPicker();
24821         }
24822         else if(el.is('button.x-date-mp-ok')){
24823             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24824             this.hideMonthPicker();
24825         }
24826         else if(pn = el.up('td.x-date-mp-month', 2)){
24827             this.mpMonths.removeClass('x-date-mp-sel');
24828             pn.addClass('x-date-mp-sel');
24829             this.mpSelMonth = pn.dom.xmonth;
24830         }
24831         else if(pn = el.up('td.x-date-mp-year', 2)){
24832             this.mpYears.removeClass('x-date-mp-sel');
24833             pn.addClass('x-date-mp-sel');
24834             this.mpSelYear = pn.dom.xyear;
24835         }
24836         else if(el.is('a.x-date-mp-prev')){
24837             this.updateMPYear(this.mpyear-10);
24838         }
24839         else if(el.is('a.x-date-mp-next')){
24840             this.updateMPYear(this.mpyear+10);
24841         }
24842     },
24843
24844     onMonthDblClick : function(e, t){
24845         e.stopEvent();
24846         var el = new Roo.Element(t), pn;
24847         if(pn = el.up('td.x-date-mp-month', 2)){
24848             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24849             this.hideMonthPicker();
24850         }
24851         else if(pn = el.up('td.x-date-mp-year', 2)){
24852             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24853             this.hideMonthPicker();
24854         }
24855     },
24856
24857     hideMonthPicker : function(disableAnim){
24858         if(this.monthPicker){
24859             if(disableAnim === true){
24860                 this.monthPicker.hide();
24861             }else{
24862                 this.monthPicker.slideOut('t', {duration:.2});
24863             }
24864         }
24865     },
24866
24867     // private
24868     showPrevMonth : function(e){
24869         this.update(this.activeDate.add("mo", -1));
24870     },
24871
24872     // private
24873     showNextMonth : function(e){
24874         this.update(this.activeDate.add("mo", 1));
24875     },
24876
24877     // private
24878     showPrevYear : function(){
24879         this.update(this.activeDate.add("y", -1));
24880     },
24881
24882     // private
24883     showNextYear : function(){
24884         this.update(this.activeDate.add("y", 1));
24885     },
24886
24887     // private
24888     handleMouseWheel : function(e){
24889         var delta = e.getWheelDelta();
24890         if(delta > 0){
24891             this.showPrevMonth();
24892             e.stopEvent();
24893         } else if(delta < 0){
24894             this.showNextMonth();
24895             e.stopEvent();
24896         }
24897     },
24898
24899     // private
24900     handleDateClick : function(e, t){
24901         e.stopEvent();
24902         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24903             this.setValue(new Date(t.dateValue));
24904             this.fireEvent("select", this, this.value);
24905         }
24906     },
24907
24908     // private
24909     selectToday : function(){
24910         this.setValue(new Date().clearTime());
24911         this.fireEvent("select", this, this.value);
24912     },
24913
24914     // private
24915     update : function(date){
24916         var vd = this.activeDate;
24917         this.activeDate = date;
24918         if(vd && this.el){
24919             var t = date.getTime();
24920             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24921                 this.cells.removeClass("x-date-selected");
24922                 this.cells.each(function(c){
24923                    if(c.dom.firstChild.dateValue == t){
24924                        c.addClass("x-date-selected");
24925                        setTimeout(function(){
24926                             try{c.dom.firstChild.focus();}catch(e){}
24927                        }, 50);
24928                        return false;
24929                    }
24930                 });
24931                 return;
24932             }
24933         }
24934         var days = date.getDaysInMonth();
24935         var firstOfMonth = date.getFirstDateOfMonth();
24936         var startingPos = firstOfMonth.getDay()-this.startDay;
24937
24938         if(startingPos <= this.startDay){
24939             startingPos += 7;
24940         }
24941
24942         var pm = date.add("mo", -1);
24943         var prevStart = pm.getDaysInMonth()-startingPos;
24944
24945         var cells = this.cells.elements;
24946         var textEls = this.textNodes;
24947         days += startingPos;
24948
24949         // convert everything to numbers so it's fast
24950         var day = 86400000;
24951         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24952         var today = new Date().clearTime().getTime();
24953         var sel = date.clearTime().getTime();
24954         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24955         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24956         var ddMatch = this.disabledDatesRE;
24957         var ddText = this.disabledDatesText;
24958         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24959         var ddaysText = this.disabledDaysText;
24960         var format = this.format;
24961
24962         var setCellClass = function(cal, cell){
24963             cell.title = "";
24964             var t = d.getTime();
24965             cell.firstChild.dateValue = t;
24966             if(t == today){
24967                 cell.className += " x-date-today";
24968                 cell.title = cal.todayText;
24969             }
24970             if(t == sel){
24971                 cell.className += " x-date-selected";
24972                 setTimeout(function(){
24973                     try{cell.firstChild.focus();}catch(e){}
24974                 }, 50);
24975             }
24976             // disabling
24977             if(t < min) {
24978                 cell.className = " x-date-disabled";
24979                 cell.title = cal.minText;
24980                 return;
24981             }
24982             if(t > max) {
24983                 cell.className = " x-date-disabled";
24984                 cell.title = cal.maxText;
24985                 return;
24986             }
24987             if(ddays){
24988                 if(ddays.indexOf(d.getDay()) != -1){
24989                     cell.title = ddaysText;
24990                     cell.className = " x-date-disabled";
24991                 }
24992             }
24993             if(ddMatch && format){
24994                 var fvalue = d.dateFormat(format);
24995                 if(ddMatch.test(fvalue)){
24996                     cell.title = ddText.replace("%0", fvalue);
24997                     cell.className = " x-date-disabled";
24998                 }
24999             }
25000         };
25001
25002         var i = 0;
25003         for(; i < startingPos; i++) {
25004             textEls[i].innerHTML = (++prevStart);
25005             d.setDate(d.getDate()+1);
25006             cells[i].className = "x-date-prevday";
25007             setCellClass(this, cells[i]);
25008         }
25009         for(; i < days; i++){
25010             intDay = i - startingPos + 1;
25011             textEls[i].innerHTML = (intDay);
25012             d.setDate(d.getDate()+1);
25013             cells[i].className = "x-date-active";
25014             setCellClass(this, cells[i]);
25015         }
25016         var extraDays = 0;
25017         for(; i < 42; i++) {
25018              textEls[i].innerHTML = (++extraDays);
25019              d.setDate(d.getDate()+1);
25020              cells[i].className = "x-date-nextday";
25021              setCellClass(this, cells[i]);
25022         }
25023
25024         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25025
25026         if(!this.internalRender){
25027             var main = this.el.dom.firstChild;
25028             var w = main.offsetWidth;
25029             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25030             Roo.fly(main).setWidth(w);
25031             this.internalRender = true;
25032             // opera does not respect the auto grow header center column
25033             // then, after it gets a width opera refuses to recalculate
25034             // without a second pass
25035             if(Roo.isOpera && !this.secondPass){
25036                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25037                 this.secondPass = true;
25038                 this.update.defer(10, this, [date]);
25039             }
25040         }
25041     }
25042 });/*
25043  * Based on:
25044  * Ext JS Library 1.1.1
25045  * Copyright(c) 2006-2007, Ext JS, LLC.
25046  *
25047  * Originally Released Under LGPL - original licence link has changed is not relivant.
25048  *
25049  * Fork - LGPL
25050  * <script type="text/javascript">
25051  */
25052 /**
25053  * @class Roo.TabPanel
25054  * @extends Roo.util.Observable
25055  * A lightweight tab container.
25056  * <br><br>
25057  * Usage:
25058  * <pre><code>
25059 // basic tabs 1, built from existing content
25060 var tabs = new Roo.TabPanel("tabs1");
25061 tabs.addTab("script", "View Script");
25062 tabs.addTab("markup", "View Markup");
25063 tabs.activate("script");
25064
25065 // more advanced tabs, built from javascript
25066 var jtabs = new Roo.TabPanel("jtabs");
25067 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25068
25069 // set up the UpdateManager
25070 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25071 var updater = tab2.getUpdateManager();
25072 updater.setDefaultUrl("ajax1.htm");
25073 tab2.on('activate', updater.refresh, updater, true);
25074
25075 // Use setUrl for Ajax loading
25076 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25077 tab3.setUrl("ajax2.htm", null, true);
25078
25079 // Disabled tab
25080 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25081 tab4.disable();
25082
25083 jtabs.activate("jtabs-1");
25084  * </code></pre>
25085  * @constructor
25086  * Create a new TabPanel.
25087  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25088  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25089  */
25090 Roo.TabPanel = function(container, config){
25091     /**
25092     * The container element for this TabPanel.
25093     * @type Roo.Element
25094     */
25095     this.el = Roo.get(container, true);
25096     if(config){
25097         if(typeof config == "boolean"){
25098             this.tabPosition = config ? "bottom" : "top";
25099         }else{
25100             Roo.apply(this, config);
25101         }
25102     }
25103     if(this.tabPosition == "bottom"){
25104         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25105         this.el.addClass("x-tabs-bottom");
25106     }
25107     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25108     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25109     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25110     if(Roo.isIE){
25111         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25112     }
25113     if(this.tabPosition != "bottom"){
25114     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25115      * @type Roo.Element
25116      */
25117       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25118       this.el.addClass("x-tabs-top");
25119     }
25120     this.items = [];
25121
25122     this.bodyEl.setStyle("position", "relative");
25123
25124     this.active = null;
25125     this.activateDelegate = this.activate.createDelegate(this);
25126
25127     this.addEvents({
25128         /**
25129          * @event tabchange
25130          * Fires when the active tab changes
25131          * @param {Roo.TabPanel} this
25132          * @param {Roo.TabPanelItem} activePanel The new active tab
25133          */
25134         "tabchange": true,
25135         /**
25136          * @event beforetabchange
25137          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25138          * @param {Roo.TabPanel} this
25139          * @param {Object} e Set cancel to true on this object to cancel the tab change
25140          * @param {Roo.TabPanelItem} tab The tab being changed to
25141          */
25142         "beforetabchange" : true
25143     });
25144
25145     Roo.EventManager.onWindowResize(this.onResize, this);
25146     this.cpad = this.el.getPadding("lr");
25147     this.hiddenCount = 0;
25148
25149     Roo.TabPanel.superclass.constructor.call(this);
25150 };
25151
25152 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25153         /*
25154          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25155          */
25156     tabPosition : "top",
25157         /*
25158          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25159          */
25160     currentTabWidth : 0,
25161         /*
25162          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25163          */
25164     minTabWidth : 40,
25165         /*
25166          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25167          */
25168     maxTabWidth : 250,
25169         /*
25170          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25171          */
25172     preferredTabWidth : 175,
25173         /*
25174          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25175          */
25176     resizeTabs : false,
25177         /*
25178          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25179          */
25180     monitorResize : true,
25181
25182     /**
25183      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25184      * @param {String} id The id of the div to use <b>or create</b>
25185      * @param {String} text The text for the tab
25186      * @param {String} content (optional) Content to put in the TabPanelItem body
25187      * @param {Boolean} closable (optional) True to create a close icon on the tab
25188      * @return {Roo.TabPanelItem} The created TabPanelItem
25189      */
25190     addTab : function(id, text, content, closable){
25191         var item = new Roo.TabPanelItem(this, id, text, closable);
25192         this.addTabItem(item);
25193         if(content){
25194             item.setContent(content);
25195         }
25196         return item;
25197     },
25198
25199     /**
25200      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25201      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25202      * @return {Roo.TabPanelItem}
25203      */
25204     getTab : function(id){
25205         return this.items[id];
25206     },
25207
25208     /**
25209      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25210      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25211      */
25212     hideTab : function(id){
25213         var t = this.items[id];
25214         if(!t.isHidden()){
25215            t.setHidden(true);
25216            this.hiddenCount++;
25217            this.autoSizeTabs();
25218         }
25219     },
25220
25221     /**
25222      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25223      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25224      */
25225     unhideTab : function(id){
25226         var t = this.items[id];
25227         if(t.isHidden()){
25228            t.setHidden(false);
25229            this.hiddenCount--;
25230            this.autoSizeTabs();
25231         }
25232     },
25233
25234     /**
25235      * Adds an existing {@link Roo.TabPanelItem}.
25236      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25237      */
25238     addTabItem : function(item){
25239         this.items[item.id] = item;
25240         this.items.push(item);
25241         if(this.resizeTabs){
25242            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25243            this.autoSizeTabs();
25244         }else{
25245             item.autoSize();
25246         }
25247     },
25248
25249     /**
25250      * Removes a {@link Roo.TabPanelItem}.
25251      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25252      */
25253     removeTab : function(id){
25254         var items = this.items;
25255         var tab = items[id];
25256         if(!tab) { return; }
25257         var index = items.indexOf(tab);
25258         if(this.active == tab && items.length > 1){
25259             var newTab = this.getNextAvailable(index);
25260             if(newTab) {
25261                 newTab.activate();
25262             }
25263         }
25264         this.stripEl.dom.removeChild(tab.pnode.dom);
25265         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25266             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25267         }
25268         items.splice(index, 1);
25269         delete this.items[tab.id];
25270         tab.fireEvent("close", tab);
25271         tab.purgeListeners();
25272         this.autoSizeTabs();
25273     },
25274
25275     getNextAvailable : function(start){
25276         var items = this.items;
25277         var index = start;
25278         // look for a next tab that will slide over to
25279         // replace the one being removed
25280         while(index < items.length){
25281             var item = items[++index];
25282             if(item && !item.isHidden()){
25283                 return item;
25284             }
25285         }
25286         // if one isn't found select the previous tab (on the left)
25287         index = start;
25288         while(index >= 0){
25289             var item = items[--index];
25290             if(item && !item.isHidden()){
25291                 return item;
25292             }
25293         }
25294         return null;
25295     },
25296
25297     /**
25298      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25299      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25300      */
25301     disableTab : function(id){
25302         var tab = this.items[id];
25303         if(tab && this.active != tab){
25304             tab.disable();
25305         }
25306     },
25307
25308     /**
25309      * Enables a {@link Roo.TabPanelItem} that is disabled.
25310      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25311      */
25312     enableTab : function(id){
25313         var tab = this.items[id];
25314         tab.enable();
25315     },
25316
25317     /**
25318      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25319      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25320      * @return {Roo.TabPanelItem} The TabPanelItem.
25321      */
25322     activate : function(id){
25323         var tab = this.items[id];
25324         if(!tab){
25325             return null;
25326         }
25327         if(tab == this.active || tab.disabled){
25328             return tab;
25329         }
25330         var e = {};
25331         this.fireEvent("beforetabchange", this, e, tab);
25332         if(e.cancel !== true && !tab.disabled){
25333             if(this.active){
25334                 this.active.hide();
25335             }
25336             this.active = this.items[id];
25337             this.active.show();
25338             this.fireEvent("tabchange", this, this.active);
25339         }
25340         return tab;
25341     },
25342
25343     /**
25344      * Gets the active {@link Roo.TabPanelItem}.
25345      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25346      */
25347     getActiveTab : function(){
25348         return this.active;
25349     },
25350
25351     /**
25352      * Updates the tab body element to fit the height of the container element
25353      * for overflow scrolling
25354      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25355      */
25356     syncHeight : function(targetHeight){
25357         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25358         var bm = this.bodyEl.getMargins();
25359         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25360         this.bodyEl.setHeight(newHeight);
25361         return newHeight;
25362     },
25363
25364     onResize : function(){
25365         if(this.monitorResize){
25366             this.autoSizeTabs();
25367         }
25368     },
25369
25370     /**
25371      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25372      */
25373     beginUpdate : function(){
25374         this.updating = true;
25375     },
25376
25377     /**
25378      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25379      */
25380     endUpdate : function(){
25381         this.updating = false;
25382         this.autoSizeTabs();
25383     },
25384
25385     /**
25386      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25387      */
25388     autoSizeTabs : function(){
25389         var count = this.items.length;
25390         var vcount = count - this.hiddenCount;
25391         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25392         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25393         var availWidth = Math.floor(w / vcount);
25394         var b = this.stripBody;
25395         if(b.getWidth() > w){
25396             var tabs = this.items;
25397             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25398             if(availWidth < this.minTabWidth){
25399                 /*if(!this.sleft){    // incomplete scrolling code
25400                     this.createScrollButtons();
25401                 }
25402                 this.showScroll();
25403                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25404             }
25405         }else{
25406             if(this.currentTabWidth < this.preferredTabWidth){
25407                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25408             }
25409         }
25410     },
25411
25412     /**
25413      * Returns the number of tabs in this TabPanel.
25414      * @return {Number}
25415      */
25416      getCount : function(){
25417          return this.items.length;
25418      },
25419
25420     /**
25421      * Resizes all the tabs to the passed width
25422      * @param {Number} The new width
25423      */
25424     setTabWidth : function(width){
25425         this.currentTabWidth = width;
25426         for(var i = 0, len = this.items.length; i < len; i++) {
25427                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25428         }
25429     },
25430
25431     /**
25432      * Destroys this TabPanel
25433      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25434      */
25435     destroy : function(removeEl){
25436         Roo.EventManager.removeResizeListener(this.onResize, this);
25437         for(var i = 0, len = this.items.length; i < len; i++){
25438             this.items[i].purgeListeners();
25439         }
25440         if(removeEl === true){
25441             this.el.update("");
25442             this.el.remove();
25443         }
25444     }
25445 });
25446
25447 /**
25448  * @class Roo.TabPanelItem
25449  * @extends Roo.util.Observable
25450  * Represents an individual item (tab plus body) in a TabPanel.
25451  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25452  * @param {String} id The id of this TabPanelItem
25453  * @param {String} text The text for the tab of this TabPanelItem
25454  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25455  */
25456 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25457     /**
25458      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25459      * @type Roo.TabPanel
25460      */
25461     this.tabPanel = tabPanel;
25462     /**
25463      * The id for this TabPanelItem
25464      * @type String
25465      */
25466     this.id = id;
25467     /** @private */
25468     this.disabled = false;
25469     /** @private */
25470     this.text = text;
25471     /** @private */
25472     this.loaded = false;
25473     this.closable = closable;
25474
25475     /**
25476      * The body element for this TabPanelItem.
25477      * @type Roo.Element
25478      */
25479     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25480     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25481     this.bodyEl.setStyle("display", "block");
25482     this.bodyEl.setStyle("zoom", "1");
25483     this.hideAction();
25484
25485     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25486     /** @private */
25487     this.el = Roo.get(els.el, true);
25488     this.inner = Roo.get(els.inner, true);
25489     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25490     this.pnode = Roo.get(els.el.parentNode, true);
25491     this.el.on("mousedown", this.onTabMouseDown, this);
25492     this.el.on("click", this.onTabClick, this);
25493     /** @private */
25494     if(closable){
25495         var c = Roo.get(els.close, true);
25496         c.dom.title = this.closeText;
25497         c.addClassOnOver("close-over");
25498         c.on("click", this.closeClick, this);
25499      }
25500
25501     this.addEvents({
25502          /**
25503          * @event activate
25504          * Fires when this tab becomes the active tab.
25505          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25506          * @param {Roo.TabPanelItem} this
25507          */
25508         "activate": true,
25509         /**
25510          * @event beforeclose
25511          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25512          * @param {Roo.TabPanelItem} this
25513          * @param {Object} e Set cancel to true on this object to cancel the close.
25514          */
25515         "beforeclose": true,
25516         /**
25517          * @event close
25518          * Fires when this tab is closed.
25519          * @param {Roo.TabPanelItem} this
25520          */
25521          "close": true,
25522         /**
25523          * @event deactivate
25524          * Fires when this tab is no longer the active tab.
25525          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25526          * @param {Roo.TabPanelItem} this
25527          */
25528          "deactivate" : true
25529     });
25530     this.hidden = false;
25531
25532     Roo.TabPanelItem.superclass.constructor.call(this);
25533 };
25534
25535 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25536     purgeListeners : function(){
25537        Roo.util.Observable.prototype.purgeListeners.call(this);
25538        this.el.removeAllListeners();
25539     },
25540     /**
25541      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25542      */
25543     show : function(){
25544         this.pnode.addClass("on");
25545         this.showAction();
25546         if(Roo.isOpera){
25547             this.tabPanel.stripWrap.repaint();
25548         }
25549         this.fireEvent("activate", this.tabPanel, this);
25550     },
25551
25552     /**
25553      * Returns true if this tab is the active tab.
25554      * @return {Boolean}
25555      */
25556     isActive : function(){
25557         return this.tabPanel.getActiveTab() == this;
25558     },
25559
25560     /**
25561      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25562      */
25563     hide : function(){
25564         this.pnode.removeClass("on");
25565         this.hideAction();
25566         this.fireEvent("deactivate", this.tabPanel, this);
25567     },
25568
25569     hideAction : function(){
25570         this.bodyEl.hide();
25571         this.bodyEl.setStyle("position", "absolute");
25572         this.bodyEl.setLeft("-20000px");
25573         this.bodyEl.setTop("-20000px");
25574     },
25575
25576     showAction : function(){
25577         this.bodyEl.setStyle("position", "relative");
25578         this.bodyEl.setTop("");
25579         this.bodyEl.setLeft("");
25580         this.bodyEl.show();
25581     },
25582
25583     /**
25584      * Set the tooltip for the tab.
25585      * @param {String} tooltip The tab's tooltip
25586      */
25587     setTooltip : function(text){
25588         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25589             this.textEl.dom.qtip = text;
25590             this.textEl.dom.removeAttribute('title');
25591         }else{
25592             this.textEl.dom.title = text;
25593         }
25594     },
25595
25596     onTabClick : function(e){
25597         e.preventDefault();
25598         this.tabPanel.activate(this.id);
25599     },
25600
25601     onTabMouseDown : function(e){
25602         e.preventDefault();
25603         this.tabPanel.activate(this.id);
25604     },
25605
25606     getWidth : function(){
25607         return this.inner.getWidth();
25608     },
25609
25610     setWidth : function(width){
25611         var iwidth = width - this.pnode.getPadding("lr");
25612         this.inner.setWidth(iwidth);
25613         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25614         this.pnode.setWidth(width);
25615     },
25616
25617     /**
25618      * Show or hide the tab
25619      * @param {Boolean} hidden True to hide or false to show.
25620      */
25621     setHidden : function(hidden){
25622         this.hidden = hidden;
25623         this.pnode.setStyle("display", hidden ? "none" : "");
25624     },
25625
25626     /**
25627      * Returns true if this tab is "hidden"
25628      * @return {Boolean}
25629      */
25630     isHidden : function(){
25631         return this.hidden;
25632     },
25633
25634     /**
25635      * Returns the text for this tab
25636      * @return {String}
25637      */
25638     getText : function(){
25639         return this.text;
25640     },
25641
25642     autoSize : function(){
25643         //this.el.beginMeasure();
25644         this.textEl.setWidth(1);
25645         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25646         //this.el.endMeasure();
25647     },
25648
25649     /**
25650      * Sets the text for the tab (Note: this also sets the tooltip text)
25651      * @param {String} text The tab's text and tooltip
25652      */
25653     setText : function(text){
25654         this.text = text;
25655         this.textEl.update(text);
25656         this.setTooltip(text);
25657         if(!this.tabPanel.resizeTabs){
25658             this.autoSize();
25659         }
25660     },
25661     /**
25662      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25663      */
25664     activate : function(){
25665         this.tabPanel.activate(this.id);
25666     },
25667
25668     /**
25669      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25670      */
25671     disable : function(){
25672         if(this.tabPanel.active != this){
25673             this.disabled = true;
25674             this.pnode.addClass("disabled");
25675         }
25676     },
25677
25678     /**
25679      * Enables this TabPanelItem if it was previously disabled.
25680      */
25681     enable : function(){
25682         this.disabled = false;
25683         this.pnode.removeClass("disabled");
25684     },
25685
25686     /**
25687      * Sets the content for this TabPanelItem.
25688      * @param {String} content The content
25689      * @param {Boolean} loadScripts true to look for and load scripts
25690      */
25691     setContent : function(content, loadScripts){
25692         this.bodyEl.update(content, loadScripts);
25693     },
25694
25695     /**
25696      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25697      * @return {Roo.UpdateManager} The UpdateManager
25698      */
25699     getUpdateManager : function(){
25700         return this.bodyEl.getUpdateManager();
25701     },
25702
25703     /**
25704      * Set a URL to be used to load the content for this TabPanelItem.
25705      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25706      * @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)
25707      * @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)
25708      * @return {Roo.UpdateManager} The UpdateManager
25709      */
25710     setUrl : function(url, params, loadOnce){
25711         if(this.refreshDelegate){
25712             this.un('activate', this.refreshDelegate);
25713         }
25714         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25715         this.on("activate", this.refreshDelegate);
25716         return this.bodyEl.getUpdateManager();
25717     },
25718
25719     /** @private */
25720     _handleRefresh : function(url, params, loadOnce){
25721         if(!loadOnce || !this.loaded){
25722             var updater = this.bodyEl.getUpdateManager();
25723             updater.update(url, params, this._setLoaded.createDelegate(this));
25724         }
25725     },
25726
25727     /**
25728      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25729      *   Will fail silently if the setUrl method has not been called.
25730      *   This does not activate the panel, just updates its content.
25731      */
25732     refresh : function(){
25733         if(this.refreshDelegate){
25734            this.loaded = false;
25735            this.refreshDelegate();
25736         }
25737     },
25738
25739     /** @private */
25740     _setLoaded : function(){
25741         this.loaded = true;
25742     },
25743
25744     /** @private */
25745     closeClick : function(e){
25746         var o = {};
25747         e.stopEvent();
25748         this.fireEvent("beforeclose", this, o);
25749         if(o.cancel !== true){
25750             this.tabPanel.removeTab(this.id);
25751         }
25752     },
25753     /**
25754      * The text displayed in the tooltip for the close icon.
25755      * @type String
25756      */
25757     closeText : "Close this tab"
25758 });
25759
25760 /** @private */
25761 Roo.TabPanel.prototype.createStrip = function(container){
25762     var strip = document.createElement("div");
25763     strip.className = "x-tabs-wrap";
25764     container.appendChild(strip);
25765     return strip;
25766 };
25767 /** @private */
25768 Roo.TabPanel.prototype.createStripList = function(strip){
25769     // div wrapper for retard IE
25770     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>';
25771     return strip.firstChild.firstChild.firstChild.firstChild;
25772 };
25773 /** @private */
25774 Roo.TabPanel.prototype.createBody = function(container){
25775     var body = document.createElement("div");
25776     Roo.id(body, "tab-body");
25777     Roo.fly(body).addClass("x-tabs-body");
25778     container.appendChild(body);
25779     return body;
25780 };
25781 /** @private */
25782 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25783     var body = Roo.getDom(id);
25784     if(!body){
25785         body = document.createElement("div");
25786         body.id = id;
25787     }
25788     Roo.fly(body).addClass("x-tabs-item-body");
25789     bodyEl.insertBefore(body, bodyEl.firstChild);
25790     return body;
25791 };
25792 /** @private */
25793 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25794     var td = document.createElement("td");
25795     stripEl.appendChild(td);
25796     if(closable){
25797         td.className = "x-tabs-closable";
25798         if(!this.closeTpl){
25799             this.closeTpl = new Roo.Template(
25800                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25801                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25802                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25803             );
25804         }
25805         var el = this.closeTpl.overwrite(td, {"text": text});
25806         var close = el.getElementsByTagName("div")[0];
25807         var inner = el.getElementsByTagName("em")[0];
25808         return {"el": el, "close": close, "inner": inner};
25809     } else {
25810         if(!this.tabTpl){
25811             this.tabTpl = new Roo.Template(
25812                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25813                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25814             );
25815         }
25816         var el = this.tabTpl.overwrite(td, {"text": text});
25817         var inner = el.getElementsByTagName("em")[0];
25818         return {"el": el, "inner": inner};
25819     }
25820 };/*
25821  * Based on:
25822  * Ext JS Library 1.1.1
25823  * Copyright(c) 2006-2007, Ext JS, LLC.
25824  *
25825  * Originally Released Under LGPL - original licence link has changed is not relivant.
25826  *
25827  * Fork - LGPL
25828  * <script type="text/javascript">
25829  */
25830
25831 /**
25832  * @class Roo.Button
25833  * @extends Roo.util.Observable
25834  * Simple Button class
25835  * @cfg {String} text The button text
25836  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25837  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25838  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25839  * @cfg {Object} scope The scope of the handler
25840  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25841  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25842  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25843  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25844  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25845  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25846    applies if enableToggle = true)
25847  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25848  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25849   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25850  * @constructor
25851  * Create a new button
25852  * @param {Object} config The config object
25853  */
25854 Roo.Button = function(renderTo, config)
25855 {
25856     if (!config) {
25857         config = renderTo;
25858         renderTo = config.renderTo || false;
25859     }
25860     
25861     Roo.apply(this, config);
25862     this.addEvents({
25863         /**
25864              * @event click
25865              * Fires when this button is clicked
25866              * @param {Button} this
25867              * @param {EventObject} e The click event
25868              */
25869             "click" : true,
25870         /**
25871              * @event toggle
25872              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25873              * @param {Button} this
25874              * @param {Boolean} pressed
25875              */
25876             "toggle" : true,
25877         /**
25878              * @event mouseover
25879              * Fires when the mouse hovers over the button
25880              * @param {Button} this
25881              * @param {Event} e The event object
25882              */
25883         'mouseover' : true,
25884         /**
25885              * @event mouseout
25886              * Fires when the mouse exits the button
25887              * @param {Button} this
25888              * @param {Event} e The event object
25889              */
25890         'mouseout': true,
25891          /**
25892              * @event render
25893              * Fires when the button is rendered
25894              * @param {Button} this
25895              */
25896         'render': true
25897     });
25898     if(this.menu){
25899         this.menu = Roo.menu.MenuMgr.get(this.menu);
25900     }
25901     // register listeners first!!  - so render can be captured..
25902     Roo.util.Observable.call(this);
25903     if(renderTo){
25904         this.render(renderTo);
25905     }
25906     
25907   
25908 };
25909
25910 Roo.extend(Roo.Button, Roo.util.Observable, {
25911     /**
25912      * 
25913      */
25914     
25915     /**
25916      * Read-only. True if this button is hidden
25917      * @type Boolean
25918      */
25919     hidden : false,
25920     /**
25921      * Read-only. True if this button is disabled
25922      * @type Boolean
25923      */
25924     disabled : false,
25925     /**
25926      * Read-only. True if this button is pressed (only if enableToggle = true)
25927      * @type Boolean
25928      */
25929     pressed : false,
25930
25931     /**
25932      * @cfg {Number} tabIndex 
25933      * The DOM tabIndex for this button (defaults to undefined)
25934      */
25935     tabIndex : undefined,
25936
25937     /**
25938      * @cfg {Boolean} enableToggle
25939      * True to enable pressed/not pressed toggling (defaults to false)
25940      */
25941     enableToggle: false,
25942     /**
25943      * @cfg {Mixed} menu
25944      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25945      */
25946     menu : undefined,
25947     /**
25948      * @cfg {String} menuAlign
25949      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25950      */
25951     menuAlign : "tl-bl?",
25952
25953     /**
25954      * @cfg {String} iconCls
25955      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25956      */
25957     iconCls : undefined,
25958     /**
25959      * @cfg {String} type
25960      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25961      */
25962     type : 'button',
25963
25964     // private
25965     menuClassTarget: 'tr',
25966
25967     /**
25968      * @cfg {String} clickEvent
25969      * The type of event to map to the button's event handler (defaults to 'click')
25970      */
25971     clickEvent : 'click',
25972
25973     /**
25974      * @cfg {Boolean} handleMouseEvents
25975      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25976      */
25977     handleMouseEvents : true,
25978
25979     /**
25980      * @cfg {String} tooltipType
25981      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25982      */
25983     tooltipType : 'qtip',
25984
25985     /**
25986      * @cfg {String} cls
25987      * A CSS class to apply to the button's main element.
25988      */
25989     
25990     /**
25991      * @cfg {Roo.Template} template (Optional)
25992      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25993      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25994      * require code modifications if required elements (e.g. a button) aren't present.
25995      */
25996
25997     // private
25998     render : function(renderTo){
25999         var btn;
26000         if(this.hideParent){
26001             this.parentEl = Roo.get(renderTo);
26002         }
26003         if(!this.dhconfig){
26004             if(!this.template){
26005                 if(!Roo.Button.buttonTemplate){
26006                     // hideous table template
26007                     Roo.Button.buttonTemplate = new Roo.Template(
26008                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26009                         '<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>',
26010                         "</tr></tbody></table>");
26011                 }
26012                 this.template = Roo.Button.buttonTemplate;
26013             }
26014             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26015             var btnEl = btn.child("button:first");
26016             btnEl.on('focus', this.onFocus, this);
26017             btnEl.on('blur', this.onBlur, this);
26018             if(this.cls){
26019                 btn.addClass(this.cls);
26020             }
26021             if(this.icon){
26022                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26023             }
26024             if(this.iconCls){
26025                 btnEl.addClass(this.iconCls);
26026                 if(!this.cls){
26027                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26028                 }
26029             }
26030             if(this.tabIndex !== undefined){
26031                 btnEl.dom.tabIndex = this.tabIndex;
26032             }
26033             if(this.tooltip){
26034                 if(typeof this.tooltip == 'object'){
26035                     Roo.QuickTips.tips(Roo.apply({
26036                           target: btnEl.id
26037                     }, this.tooltip));
26038                 } else {
26039                     btnEl.dom[this.tooltipType] = this.tooltip;
26040                 }
26041             }
26042         }else{
26043             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26044         }
26045         this.el = btn;
26046         if(this.id){
26047             this.el.dom.id = this.el.id = this.id;
26048         }
26049         if(this.menu){
26050             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26051             this.menu.on("show", this.onMenuShow, this);
26052             this.menu.on("hide", this.onMenuHide, this);
26053         }
26054         btn.addClass("x-btn");
26055         if(Roo.isIE && !Roo.isIE7){
26056             this.autoWidth.defer(1, this);
26057         }else{
26058             this.autoWidth();
26059         }
26060         if(this.handleMouseEvents){
26061             btn.on("mouseover", this.onMouseOver, this);
26062             btn.on("mouseout", this.onMouseOut, this);
26063             btn.on("mousedown", this.onMouseDown, this);
26064         }
26065         btn.on(this.clickEvent, this.onClick, this);
26066         //btn.on("mouseup", this.onMouseUp, this);
26067         if(this.hidden){
26068             this.hide();
26069         }
26070         if(this.disabled){
26071             this.disable();
26072         }
26073         Roo.ButtonToggleMgr.register(this);
26074         if(this.pressed){
26075             this.el.addClass("x-btn-pressed");
26076         }
26077         if(this.repeat){
26078             var repeater = new Roo.util.ClickRepeater(btn,
26079                 typeof this.repeat == "object" ? this.repeat : {}
26080             );
26081             repeater.on("click", this.onClick,  this);
26082         }
26083         
26084         this.fireEvent('render', this);
26085         
26086     },
26087     /**
26088      * Returns the button's underlying element
26089      * @return {Roo.Element} The element
26090      */
26091     getEl : function(){
26092         return this.el;  
26093     },
26094     
26095     /**
26096      * Destroys this Button and removes any listeners.
26097      */
26098     destroy : function(){
26099         Roo.ButtonToggleMgr.unregister(this);
26100         this.el.removeAllListeners();
26101         this.purgeListeners();
26102         this.el.remove();
26103     },
26104
26105     // private
26106     autoWidth : function(){
26107         if(this.el){
26108             this.el.setWidth("auto");
26109             if(Roo.isIE7 && Roo.isStrict){
26110                 var ib = this.el.child('button');
26111                 if(ib && ib.getWidth() > 20){
26112                     ib.clip();
26113                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26114                 }
26115             }
26116             if(this.minWidth){
26117                 if(this.hidden){
26118                     this.el.beginMeasure();
26119                 }
26120                 if(this.el.getWidth() < this.minWidth){
26121                     this.el.setWidth(this.minWidth);
26122                 }
26123                 if(this.hidden){
26124                     this.el.endMeasure();
26125                 }
26126             }
26127         }
26128     },
26129
26130     /**
26131      * Assigns this button's click handler
26132      * @param {Function} handler The function to call when the button is clicked
26133      * @param {Object} scope (optional) Scope for the function passed in
26134      */
26135     setHandler : function(handler, scope){
26136         this.handler = handler;
26137         this.scope = scope;  
26138     },
26139     
26140     /**
26141      * Sets this button's text
26142      * @param {String} text The button text
26143      */
26144     setText : function(text){
26145         this.text = text;
26146         if(this.el){
26147             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26148         }
26149         this.autoWidth();
26150     },
26151     
26152     /**
26153      * Gets the text for this button
26154      * @return {String} The button text
26155      */
26156     getText : function(){
26157         return this.text;  
26158     },
26159     
26160     /**
26161      * Show this button
26162      */
26163     show: function(){
26164         this.hidden = false;
26165         if(this.el){
26166             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26167         }
26168     },
26169     
26170     /**
26171      * Hide this button
26172      */
26173     hide: function(){
26174         this.hidden = true;
26175         if(this.el){
26176             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26177         }
26178     },
26179     
26180     /**
26181      * Convenience function for boolean show/hide
26182      * @param {Boolean} visible True to show, false to hide
26183      */
26184     setVisible: function(visible){
26185         if(visible) {
26186             this.show();
26187         }else{
26188             this.hide();
26189         }
26190     },
26191     
26192     /**
26193      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26194      * @param {Boolean} state (optional) Force a particular state
26195      */
26196     toggle : function(state){
26197         state = state === undefined ? !this.pressed : state;
26198         if(state != this.pressed){
26199             if(state){
26200                 this.el.addClass("x-btn-pressed");
26201                 this.pressed = true;
26202                 this.fireEvent("toggle", this, true);
26203             }else{
26204                 this.el.removeClass("x-btn-pressed");
26205                 this.pressed = false;
26206                 this.fireEvent("toggle", this, false);
26207             }
26208             if(this.toggleHandler){
26209                 this.toggleHandler.call(this.scope || this, this, state);
26210             }
26211         }
26212     },
26213     
26214     /**
26215      * Focus the button
26216      */
26217     focus : function(){
26218         this.el.child('button:first').focus();
26219     },
26220     
26221     /**
26222      * Disable this button
26223      */
26224     disable : function(){
26225         if(this.el){
26226             this.el.addClass("x-btn-disabled");
26227         }
26228         this.disabled = true;
26229     },
26230     
26231     /**
26232      * Enable this button
26233      */
26234     enable : function(){
26235         if(this.el){
26236             this.el.removeClass("x-btn-disabled");
26237         }
26238         this.disabled = false;
26239     },
26240
26241     /**
26242      * Convenience function for boolean enable/disable
26243      * @param {Boolean} enabled True to enable, false to disable
26244      */
26245     setDisabled : function(v){
26246         this[v !== true ? "enable" : "disable"]();
26247     },
26248
26249     // private
26250     onClick : function(e){
26251         if(e){
26252             e.preventDefault();
26253         }
26254         if(e.button != 0){
26255             return;
26256         }
26257         if(!this.disabled){
26258             if(this.enableToggle){
26259                 this.toggle();
26260             }
26261             if(this.menu && !this.menu.isVisible()){
26262                 this.menu.show(this.el, this.menuAlign);
26263             }
26264             this.fireEvent("click", this, e);
26265             if(this.handler){
26266                 this.el.removeClass("x-btn-over");
26267                 this.handler.call(this.scope || this, this, e);
26268             }
26269         }
26270     },
26271     // private
26272     onMouseOver : function(e){
26273         if(!this.disabled){
26274             this.el.addClass("x-btn-over");
26275             this.fireEvent('mouseover', this, e);
26276         }
26277     },
26278     // private
26279     onMouseOut : function(e){
26280         if(!e.within(this.el,  true)){
26281             this.el.removeClass("x-btn-over");
26282             this.fireEvent('mouseout', this, e);
26283         }
26284     },
26285     // private
26286     onFocus : function(e){
26287         if(!this.disabled){
26288             this.el.addClass("x-btn-focus");
26289         }
26290     },
26291     // private
26292     onBlur : function(e){
26293         this.el.removeClass("x-btn-focus");
26294     },
26295     // private
26296     onMouseDown : function(e){
26297         if(!this.disabled && e.button == 0){
26298             this.el.addClass("x-btn-click");
26299             Roo.get(document).on('mouseup', this.onMouseUp, this);
26300         }
26301     },
26302     // private
26303     onMouseUp : function(e){
26304         if(e.button == 0){
26305             this.el.removeClass("x-btn-click");
26306             Roo.get(document).un('mouseup', this.onMouseUp, this);
26307         }
26308     },
26309     // private
26310     onMenuShow : function(e){
26311         this.el.addClass("x-btn-menu-active");
26312     },
26313     // private
26314     onMenuHide : function(e){
26315         this.el.removeClass("x-btn-menu-active");
26316     }   
26317 });
26318
26319 // Private utility class used by Button
26320 Roo.ButtonToggleMgr = function(){
26321    var groups = {};
26322    
26323    function toggleGroup(btn, state){
26324        if(state){
26325            var g = groups[btn.toggleGroup];
26326            for(var i = 0, l = g.length; i < l; i++){
26327                if(g[i] != btn){
26328                    g[i].toggle(false);
26329                }
26330            }
26331        }
26332    }
26333    
26334    return {
26335        register : function(btn){
26336            if(!btn.toggleGroup){
26337                return;
26338            }
26339            var g = groups[btn.toggleGroup];
26340            if(!g){
26341                g = groups[btn.toggleGroup] = [];
26342            }
26343            g.push(btn);
26344            btn.on("toggle", toggleGroup);
26345        },
26346        
26347        unregister : function(btn){
26348            if(!btn.toggleGroup){
26349                return;
26350            }
26351            var g = groups[btn.toggleGroup];
26352            if(g){
26353                g.remove(btn);
26354                btn.un("toggle", toggleGroup);
26355            }
26356        }
26357    };
26358 }();/*
26359  * Based on:
26360  * Ext JS Library 1.1.1
26361  * Copyright(c) 2006-2007, Ext JS, LLC.
26362  *
26363  * Originally Released Under LGPL - original licence link has changed is not relivant.
26364  *
26365  * Fork - LGPL
26366  * <script type="text/javascript">
26367  */
26368  
26369 /**
26370  * @class Roo.SplitButton
26371  * @extends Roo.Button
26372  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26373  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26374  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26375  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26376  * @cfg {String} arrowTooltip The title attribute of the arrow
26377  * @constructor
26378  * Create a new menu button
26379  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26380  * @param {Object} config The config object
26381  */
26382 Roo.SplitButton = function(renderTo, config){
26383     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26384     /**
26385      * @event arrowclick
26386      * Fires when this button's arrow is clicked
26387      * @param {SplitButton} this
26388      * @param {EventObject} e The click event
26389      */
26390     this.addEvents({"arrowclick":true});
26391 };
26392
26393 Roo.extend(Roo.SplitButton, Roo.Button, {
26394     render : function(renderTo){
26395         // this is one sweet looking template!
26396         var tpl = new Roo.Template(
26397             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26398             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26399             '<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>',
26400             "</tbody></table></td><td>",
26401             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26402             '<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>',
26403             "</tbody></table></td></tr></table>"
26404         );
26405         var btn = tpl.append(renderTo, [this.text, this.type], true);
26406         var btnEl = btn.child("button");
26407         if(this.cls){
26408             btn.addClass(this.cls);
26409         }
26410         if(this.icon){
26411             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26412         }
26413         if(this.iconCls){
26414             btnEl.addClass(this.iconCls);
26415             if(!this.cls){
26416                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26417             }
26418         }
26419         this.el = btn;
26420         if(this.handleMouseEvents){
26421             btn.on("mouseover", this.onMouseOver, this);
26422             btn.on("mouseout", this.onMouseOut, this);
26423             btn.on("mousedown", this.onMouseDown, this);
26424             btn.on("mouseup", this.onMouseUp, this);
26425         }
26426         btn.on(this.clickEvent, this.onClick, this);
26427         if(this.tooltip){
26428             if(typeof this.tooltip == 'object'){
26429                 Roo.QuickTips.tips(Roo.apply({
26430                       target: btnEl.id
26431                 }, this.tooltip));
26432             } else {
26433                 btnEl.dom[this.tooltipType] = this.tooltip;
26434             }
26435         }
26436         if(this.arrowTooltip){
26437             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26438         }
26439         if(this.hidden){
26440             this.hide();
26441         }
26442         if(this.disabled){
26443             this.disable();
26444         }
26445         if(this.pressed){
26446             this.el.addClass("x-btn-pressed");
26447         }
26448         if(Roo.isIE && !Roo.isIE7){
26449             this.autoWidth.defer(1, this);
26450         }else{
26451             this.autoWidth();
26452         }
26453         if(this.menu){
26454             this.menu.on("show", this.onMenuShow, this);
26455             this.menu.on("hide", this.onMenuHide, this);
26456         }
26457         this.fireEvent('render', this);
26458     },
26459
26460     // private
26461     autoWidth : function(){
26462         if(this.el){
26463             var tbl = this.el.child("table:first");
26464             var tbl2 = this.el.child("table:last");
26465             this.el.setWidth("auto");
26466             tbl.setWidth("auto");
26467             if(Roo.isIE7 && Roo.isStrict){
26468                 var ib = this.el.child('button:first');
26469                 if(ib && ib.getWidth() > 20){
26470                     ib.clip();
26471                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26472                 }
26473             }
26474             if(this.minWidth){
26475                 if(this.hidden){
26476                     this.el.beginMeasure();
26477                 }
26478                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26479                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26480                 }
26481                 if(this.hidden){
26482                     this.el.endMeasure();
26483                 }
26484             }
26485             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26486         } 
26487     },
26488     /**
26489      * Sets this button's click handler
26490      * @param {Function} handler The function to call when the button is clicked
26491      * @param {Object} scope (optional) Scope for the function passed above
26492      */
26493     setHandler : function(handler, scope){
26494         this.handler = handler;
26495         this.scope = scope;  
26496     },
26497     
26498     /**
26499      * Sets this button's arrow click handler
26500      * @param {Function} handler The function to call when the arrow is clicked
26501      * @param {Object} scope (optional) Scope for the function passed above
26502      */
26503     setArrowHandler : function(handler, scope){
26504         this.arrowHandler = handler;
26505         this.scope = scope;  
26506     },
26507     
26508     /**
26509      * Focus the button
26510      */
26511     focus : function(){
26512         if(this.el){
26513             this.el.child("button:first").focus();
26514         }
26515     },
26516
26517     // private
26518     onClick : function(e){
26519         e.preventDefault();
26520         if(!this.disabled){
26521             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26522                 if(this.menu && !this.menu.isVisible()){
26523                     this.menu.show(this.el, this.menuAlign);
26524                 }
26525                 this.fireEvent("arrowclick", this, e);
26526                 if(this.arrowHandler){
26527                     this.arrowHandler.call(this.scope || this, this, e);
26528                 }
26529             }else{
26530                 this.fireEvent("click", this, e);
26531                 if(this.handler){
26532                     this.handler.call(this.scope || this, this, e);
26533                 }
26534             }
26535         }
26536     },
26537     // private
26538     onMouseDown : function(e){
26539         if(!this.disabled){
26540             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26541         }
26542     },
26543     // private
26544     onMouseUp : function(e){
26545         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26546     }   
26547 });
26548
26549
26550 // backwards compat
26551 Roo.MenuButton = Roo.SplitButton;/*
26552  * Based on:
26553  * Ext JS Library 1.1.1
26554  * Copyright(c) 2006-2007, Ext JS, LLC.
26555  *
26556  * Originally Released Under LGPL - original licence link has changed is not relivant.
26557  *
26558  * Fork - LGPL
26559  * <script type="text/javascript">
26560  */
26561
26562 /**
26563  * @class Roo.Toolbar
26564  * Basic Toolbar class.
26565  * @constructor
26566  * Creates a new Toolbar
26567  * @param {Object} config The config object
26568  */ 
26569 Roo.Toolbar = function(container, buttons, config)
26570 {
26571     /// old consturctor format still supported..
26572     if(container instanceof Array){ // omit the container for later rendering
26573         buttons = container;
26574         config = buttons;
26575         container = null;
26576     }
26577     if (typeof(container) == 'object' && container.xtype) {
26578         config = container;
26579         container = config.container;
26580         buttons = config.buttons; // not really - use items!!
26581     }
26582     var xitems = [];
26583     if (config && config.items) {
26584         xitems = config.items;
26585         delete config.items;
26586     }
26587     Roo.apply(this, config);
26588     this.buttons = buttons;
26589     
26590     if(container){
26591         this.render(container);
26592     }
26593     Roo.each(xitems, function(b) {
26594         this.add(b);
26595     }, this);
26596     
26597 };
26598
26599 Roo.Toolbar.prototype = {
26600     /**
26601      * @cfg {Roo.data.Store} items
26602      * array of button configs or elements to add
26603      */
26604     
26605     /**
26606      * @cfg {String/HTMLElement/Element} container
26607      * The id or element that will contain the toolbar
26608      */
26609     // private
26610     render : function(ct){
26611         this.el = Roo.get(ct);
26612         if(this.cls){
26613             this.el.addClass(this.cls);
26614         }
26615         // using a table allows for vertical alignment
26616         // 100% width is needed by Safari...
26617         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26618         this.tr = this.el.child("tr", true);
26619         var autoId = 0;
26620         this.items = new Roo.util.MixedCollection(false, function(o){
26621             return o.id || ("item" + (++autoId));
26622         });
26623         if(this.buttons){
26624             this.add.apply(this, this.buttons);
26625             delete this.buttons;
26626         }
26627     },
26628
26629     /**
26630      * Adds element(s) to the toolbar -- this function takes a variable number of 
26631      * arguments of mixed type and adds them to the toolbar.
26632      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26633      * <ul>
26634      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26635      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26636      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26637      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26638      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26639      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26640      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26641      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26642      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26643      * </ul>
26644      * @param {Mixed} arg2
26645      * @param {Mixed} etc.
26646      */
26647     add : function(){
26648         var a = arguments, l = a.length;
26649         for(var i = 0; i < l; i++){
26650             this._add(a[i]);
26651         }
26652     },
26653     // private..
26654     _add : function(el) {
26655         
26656         if (el.xtype) {
26657             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26658         }
26659         
26660         if (el.applyTo){ // some kind of form field
26661             return this.addField(el);
26662         } 
26663         if (el.render){ // some kind of Toolbar.Item
26664             return this.addItem(el);
26665         }
26666         if (typeof el == "string"){ // string
26667             if(el == "separator" || el == "-"){
26668                 return this.addSeparator();
26669             }
26670             if (el == " "){
26671                 return this.addSpacer();
26672             }
26673             if(el == "->"){
26674                 return this.addFill();
26675             }
26676             return this.addText(el);
26677             
26678         }
26679         if(el.tagName){ // element
26680             return this.addElement(el);
26681         }
26682         if(typeof el == "object"){ // must be button config?
26683             return this.addButton(el);
26684         }
26685         // and now what?!?!
26686         return false;
26687         
26688     },
26689     
26690     /**
26691      * Add an Xtype element
26692      * @param {Object} xtype Xtype Object
26693      * @return {Object} created Object
26694      */
26695     addxtype : function(e){
26696         return this.add(e);  
26697     },
26698     
26699     /**
26700      * Returns the Element for this toolbar.
26701      * @return {Roo.Element}
26702      */
26703     getEl : function(){
26704         return this.el;  
26705     },
26706     
26707     /**
26708      * Adds a separator
26709      * @return {Roo.Toolbar.Item} The separator item
26710      */
26711     addSeparator : function(){
26712         return this.addItem(new Roo.Toolbar.Separator());
26713     },
26714
26715     /**
26716      * Adds a spacer element
26717      * @return {Roo.Toolbar.Spacer} The spacer item
26718      */
26719     addSpacer : function(){
26720         return this.addItem(new Roo.Toolbar.Spacer());
26721     },
26722
26723     /**
26724      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26725      * @return {Roo.Toolbar.Fill} The fill item
26726      */
26727     addFill : function(){
26728         return this.addItem(new Roo.Toolbar.Fill());
26729     },
26730
26731     /**
26732      * Adds any standard HTML element to the toolbar
26733      * @param {String/HTMLElement/Element} el The element or id of the element to add
26734      * @return {Roo.Toolbar.Item} The element's item
26735      */
26736     addElement : function(el){
26737         return this.addItem(new Roo.Toolbar.Item(el));
26738     },
26739     /**
26740      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26741      * @type Roo.util.MixedCollection  
26742      */
26743     items : false,
26744      
26745     /**
26746      * Adds any Toolbar.Item or subclass
26747      * @param {Roo.Toolbar.Item} item
26748      * @return {Roo.Toolbar.Item} The item
26749      */
26750     addItem : function(item){
26751         var td = this.nextBlock();
26752         item.render(td);
26753         this.items.add(item);
26754         return item;
26755     },
26756     
26757     /**
26758      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26759      * @param {Object/Array} config A button config or array of configs
26760      * @return {Roo.Toolbar.Button/Array}
26761      */
26762     addButton : function(config){
26763         if(config instanceof Array){
26764             var buttons = [];
26765             for(var i = 0, len = config.length; i < len; i++) {
26766                 buttons.push(this.addButton(config[i]));
26767             }
26768             return buttons;
26769         }
26770         var b = config;
26771         if(!(config instanceof Roo.Toolbar.Button)){
26772             b = config.split ?
26773                 new Roo.Toolbar.SplitButton(config) :
26774                 new Roo.Toolbar.Button(config);
26775         }
26776         var td = this.nextBlock();
26777         b.render(td);
26778         this.items.add(b);
26779         return b;
26780     },
26781     
26782     /**
26783      * Adds text to the toolbar
26784      * @param {String} text The text to add
26785      * @return {Roo.Toolbar.Item} The element's item
26786      */
26787     addText : function(text){
26788         return this.addItem(new Roo.Toolbar.TextItem(text));
26789     },
26790     
26791     /**
26792      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26793      * @param {Number} index The index where the item is to be inserted
26794      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26795      * @return {Roo.Toolbar.Button/Item}
26796      */
26797     insertButton : function(index, item){
26798         if(item instanceof Array){
26799             var buttons = [];
26800             for(var i = 0, len = item.length; i < len; i++) {
26801                buttons.push(this.insertButton(index + i, item[i]));
26802             }
26803             return buttons;
26804         }
26805         if (!(item instanceof Roo.Toolbar.Button)){
26806            item = new Roo.Toolbar.Button(item);
26807         }
26808         var td = document.createElement("td");
26809         this.tr.insertBefore(td, this.tr.childNodes[index]);
26810         item.render(td);
26811         this.items.insert(index, item);
26812         return item;
26813     },
26814     
26815     /**
26816      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26817      * @param {Object} config
26818      * @return {Roo.Toolbar.Item} The element's item
26819      */
26820     addDom : function(config, returnEl){
26821         var td = this.nextBlock();
26822         Roo.DomHelper.overwrite(td, config);
26823         var ti = new Roo.Toolbar.Item(td.firstChild);
26824         ti.render(td);
26825         this.items.add(ti);
26826         return ti;
26827     },
26828
26829     /**
26830      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26831      * @type Roo.util.MixedCollection  
26832      */
26833     fields : false,
26834     
26835     /**
26836      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26837      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26838      * @param {Roo.form.Field} field
26839      * @return {Roo.ToolbarItem}
26840      */
26841      
26842       
26843     addField : function(field) {
26844         if (!this.fields) {
26845             var autoId = 0;
26846             this.fields = new Roo.util.MixedCollection(false, function(o){
26847                 return o.id || ("item" + (++autoId));
26848             });
26849
26850         }
26851         
26852         var td = this.nextBlock();
26853         field.render(td);
26854         var ti = new Roo.Toolbar.Item(td.firstChild);
26855         ti.render(td);
26856         this.items.add(ti);
26857         this.fields.add(field);
26858         return ti;
26859     },
26860     /**
26861      * Hide the toolbar
26862      * @method hide
26863      */
26864      
26865       
26866     hide : function()
26867     {
26868         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26869         this.el.child('div').hide();
26870     },
26871     /**
26872      * Show the toolbar
26873      * @method show
26874      */
26875     show : function()
26876     {
26877         this.el.child('div').show();
26878     },
26879       
26880     // private
26881     nextBlock : function(){
26882         var td = document.createElement("td");
26883         this.tr.appendChild(td);
26884         return td;
26885     },
26886
26887     // private
26888     destroy : function(){
26889         if(this.items){ // rendered?
26890             Roo.destroy.apply(Roo, this.items.items);
26891         }
26892         if(this.fields){ // rendered?
26893             Roo.destroy.apply(Roo, this.fields.items);
26894         }
26895         Roo.Element.uncache(this.el, this.tr);
26896     }
26897 };
26898
26899 /**
26900  * @class Roo.Toolbar.Item
26901  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26902  * @constructor
26903  * Creates a new Item
26904  * @param {HTMLElement} el 
26905  */
26906 Roo.Toolbar.Item = function(el){
26907     this.el = Roo.getDom(el);
26908     this.id = Roo.id(this.el);
26909     this.hidden = false;
26910 };
26911
26912 Roo.Toolbar.Item.prototype = {
26913     
26914     /**
26915      * Get this item's HTML Element
26916      * @return {HTMLElement}
26917      */
26918     getEl : function(){
26919        return this.el;  
26920     },
26921
26922     // private
26923     render : function(td){
26924         this.td = td;
26925         td.appendChild(this.el);
26926     },
26927     
26928     /**
26929      * Removes and destroys this item.
26930      */
26931     destroy : function(){
26932         this.td.parentNode.removeChild(this.td);
26933     },
26934     
26935     /**
26936      * Shows this item.
26937      */
26938     show: function(){
26939         this.hidden = false;
26940         this.td.style.display = "";
26941     },
26942     
26943     /**
26944      * Hides this item.
26945      */
26946     hide: function(){
26947         this.hidden = true;
26948         this.td.style.display = "none";
26949     },
26950     
26951     /**
26952      * Convenience function for boolean show/hide.
26953      * @param {Boolean} visible true to show/false to hide
26954      */
26955     setVisible: function(visible){
26956         if(visible) {
26957             this.show();
26958         }else{
26959             this.hide();
26960         }
26961     },
26962     
26963     /**
26964      * Try to focus this item.
26965      */
26966     focus : function(){
26967         Roo.fly(this.el).focus();
26968     },
26969     
26970     /**
26971      * Disables this item.
26972      */
26973     disable : function(){
26974         Roo.fly(this.td).addClass("x-item-disabled");
26975         this.disabled = true;
26976         this.el.disabled = true;
26977     },
26978     
26979     /**
26980      * Enables this item.
26981      */
26982     enable : function(){
26983         Roo.fly(this.td).removeClass("x-item-disabled");
26984         this.disabled = false;
26985         this.el.disabled = false;
26986     }
26987 };
26988
26989
26990 /**
26991  * @class Roo.Toolbar.Separator
26992  * @extends Roo.Toolbar.Item
26993  * A simple toolbar separator class
26994  * @constructor
26995  * Creates a new Separator
26996  */
26997 Roo.Toolbar.Separator = function(){
26998     var s = document.createElement("span");
26999     s.className = "ytb-sep";
27000     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27001 };
27002 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27003     enable:Roo.emptyFn,
27004     disable:Roo.emptyFn,
27005     focus:Roo.emptyFn
27006 });
27007
27008 /**
27009  * @class Roo.Toolbar.Spacer
27010  * @extends Roo.Toolbar.Item
27011  * A simple element that adds extra horizontal space to a toolbar.
27012  * @constructor
27013  * Creates a new Spacer
27014  */
27015 Roo.Toolbar.Spacer = function(){
27016     var s = document.createElement("div");
27017     s.className = "ytb-spacer";
27018     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27019 };
27020 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27021     enable:Roo.emptyFn,
27022     disable:Roo.emptyFn,
27023     focus:Roo.emptyFn
27024 });
27025
27026 /**
27027  * @class Roo.Toolbar.Fill
27028  * @extends Roo.Toolbar.Spacer
27029  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27030  * @constructor
27031  * Creates a new Spacer
27032  */
27033 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27034     // private
27035     render : function(td){
27036         td.style.width = '100%';
27037         Roo.Toolbar.Fill.superclass.render.call(this, td);
27038     }
27039 });
27040
27041 /**
27042  * @class Roo.Toolbar.TextItem
27043  * @extends Roo.Toolbar.Item
27044  * A simple class that renders text directly into a toolbar.
27045  * @constructor
27046  * Creates a new TextItem
27047  * @param {String} text
27048  */
27049 Roo.Toolbar.TextItem = function(text){
27050     if (typeof(text) == 'object') {
27051         text = text.text;
27052     }
27053     var s = document.createElement("span");
27054     s.className = "ytb-text";
27055     s.innerHTML = text;
27056     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27057 };
27058 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27059     enable:Roo.emptyFn,
27060     disable:Roo.emptyFn,
27061     focus:Roo.emptyFn
27062 });
27063
27064 /**
27065  * @class Roo.Toolbar.Button
27066  * @extends Roo.Button
27067  * A button that renders into a toolbar.
27068  * @constructor
27069  * Creates a new Button
27070  * @param {Object} config A standard {@link Roo.Button} config object
27071  */
27072 Roo.Toolbar.Button = function(config){
27073     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27074 };
27075 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27076     render : function(td){
27077         this.td = td;
27078         Roo.Toolbar.Button.superclass.render.call(this, td);
27079     },
27080     
27081     /**
27082      * Removes and destroys this button
27083      */
27084     destroy : function(){
27085         Roo.Toolbar.Button.superclass.destroy.call(this);
27086         this.td.parentNode.removeChild(this.td);
27087     },
27088     
27089     /**
27090      * Shows this button
27091      */
27092     show: function(){
27093         this.hidden = false;
27094         this.td.style.display = "";
27095     },
27096     
27097     /**
27098      * Hides this button
27099      */
27100     hide: function(){
27101         this.hidden = true;
27102         this.td.style.display = "none";
27103     },
27104
27105     /**
27106      * Disables this item
27107      */
27108     disable : function(){
27109         Roo.fly(this.td).addClass("x-item-disabled");
27110         this.disabled = true;
27111     },
27112
27113     /**
27114      * Enables this item
27115      */
27116     enable : function(){
27117         Roo.fly(this.td).removeClass("x-item-disabled");
27118         this.disabled = false;
27119     }
27120 });
27121 // backwards compat
27122 Roo.ToolbarButton = Roo.Toolbar.Button;
27123
27124 /**
27125  * @class Roo.Toolbar.SplitButton
27126  * @extends Roo.SplitButton
27127  * A menu button that renders into a toolbar.
27128  * @constructor
27129  * Creates a new SplitButton
27130  * @param {Object} config A standard {@link Roo.SplitButton} config object
27131  */
27132 Roo.Toolbar.SplitButton = function(config){
27133     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27134 };
27135 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27136     render : function(td){
27137         this.td = td;
27138         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27139     },
27140     
27141     /**
27142      * Removes and destroys this button
27143      */
27144     destroy : function(){
27145         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27146         this.td.parentNode.removeChild(this.td);
27147     },
27148     
27149     /**
27150      * Shows this button
27151      */
27152     show: function(){
27153         this.hidden = false;
27154         this.td.style.display = "";
27155     },
27156     
27157     /**
27158      * Hides this button
27159      */
27160     hide: function(){
27161         this.hidden = true;
27162         this.td.style.display = "none";
27163     }
27164 });
27165
27166 // backwards compat
27167 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27168  * Based on:
27169  * Ext JS Library 1.1.1
27170  * Copyright(c) 2006-2007, Ext JS, LLC.
27171  *
27172  * Originally Released Under LGPL - original licence link has changed is not relivant.
27173  *
27174  * Fork - LGPL
27175  * <script type="text/javascript">
27176  */
27177  
27178 /**
27179  * @class Roo.PagingToolbar
27180  * @extends Roo.Toolbar
27181  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27182  * @constructor
27183  * Create a new PagingToolbar
27184  * @param {Object} config The config object
27185  */
27186 Roo.PagingToolbar = function(el, ds, config)
27187 {
27188     // old args format still supported... - xtype is prefered..
27189     if (typeof(el) == 'object' && el.xtype) {
27190         // created from xtype...
27191         config = el;
27192         ds = el.dataSource;
27193         el = config.container;
27194     }
27195     var items = [];
27196     if (config.items) {
27197         items = config.items;
27198         config.items = [];
27199     }
27200     
27201     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27202     this.ds = ds;
27203     this.cursor = 0;
27204     this.renderButtons(this.el);
27205     this.bind(ds);
27206     
27207     // supprot items array.
27208    
27209     Roo.each(items, function(e) {
27210         this.add(Roo.factory(e));
27211     },this);
27212     
27213 };
27214
27215 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27216     /**
27217      * @cfg {Roo.data.Store} dataSource
27218      * The underlying data store providing the paged data
27219      */
27220     /**
27221      * @cfg {String/HTMLElement/Element} container
27222      * container The id or element that will contain the toolbar
27223      */
27224     /**
27225      * @cfg {Boolean} displayInfo
27226      * True to display the displayMsg (defaults to false)
27227      */
27228     /**
27229      * @cfg {Number} pageSize
27230      * The number of records to display per page (defaults to 20)
27231      */
27232     pageSize: 20,
27233     /**
27234      * @cfg {String} displayMsg
27235      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27236      */
27237     displayMsg : 'Displaying {0} - {1} of {2}',
27238     /**
27239      * @cfg {String} emptyMsg
27240      * The message to display when no records are found (defaults to "No data to display")
27241      */
27242     emptyMsg : 'No data to display',
27243     /**
27244      * Customizable piece of the default paging text (defaults to "Page")
27245      * @type String
27246      */
27247     beforePageText : "Page",
27248     /**
27249      * Customizable piece of the default paging text (defaults to "of %0")
27250      * @type String
27251      */
27252     afterPageText : "of {0}",
27253     /**
27254      * Customizable piece of the default paging text (defaults to "First Page")
27255      * @type String
27256      */
27257     firstText : "First Page",
27258     /**
27259      * Customizable piece of the default paging text (defaults to "Previous Page")
27260      * @type String
27261      */
27262     prevText : "Previous Page",
27263     /**
27264      * Customizable piece of the default paging text (defaults to "Next Page")
27265      * @type String
27266      */
27267     nextText : "Next Page",
27268     /**
27269      * Customizable piece of the default paging text (defaults to "Last Page")
27270      * @type String
27271      */
27272     lastText : "Last Page",
27273     /**
27274      * Customizable piece of the default paging text (defaults to "Refresh")
27275      * @type String
27276      */
27277     refreshText : "Refresh",
27278
27279     // private
27280     renderButtons : function(el){
27281         Roo.PagingToolbar.superclass.render.call(this, el);
27282         this.first = this.addButton({
27283             tooltip: this.firstText,
27284             cls: "x-btn-icon x-grid-page-first",
27285             disabled: true,
27286             handler: this.onClick.createDelegate(this, ["first"])
27287         });
27288         this.prev = this.addButton({
27289             tooltip: this.prevText,
27290             cls: "x-btn-icon x-grid-page-prev",
27291             disabled: true,
27292             handler: this.onClick.createDelegate(this, ["prev"])
27293         });
27294         //this.addSeparator();
27295         this.add(this.beforePageText);
27296         this.field = Roo.get(this.addDom({
27297            tag: "input",
27298            type: "text",
27299            size: "3",
27300            value: "1",
27301            cls: "x-grid-page-number"
27302         }).el);
27303         this.field.on("keydown", this.onPagingKeydown, this);
27304         this.field.on("focus", function(){this.dom.select();});
27305         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27306         this.field.setHeight(18);
27307         //this.addSeparator();
27308         this.next = this.addButton({
27309             tooltip: this.nextText,
27310             cls: "x-btn-icon x-grid-page-next",
27311             disabled: true,
27312             handler: this.onClick.createDelegate(this, ["next"])
27313         });
27314         this.last = this.addButton({
27315             tooltip: this.lastText,
27316             cls: "x-btn-icon x-grid-page-last",
27317             disabled: true,
27318             handler: this.onClick.createDelegate(this, ["last"])
27319         });
27320         //this.addSeparator();
27321         this.loading = this.addButton({
27322             tooltip: this.refreshText,
27323             cls: "x-btn-icon x-grid-loading",
27324             handler: this.onClick.createDelegate(this, ["refresh"])
27325         });
27326
27327         if(this.displayInfo){
27328             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27329         }
27330     },
27331
27332     // private
27333     updateInfo : function(){
27334         if(this.displayEl){
27335             var count = this.ds.getCount();
27336             var msg = count == 0 ?
27337                 this.emptyMsg :
27338                 String.format(
27339                     this.displayMsg,
27340                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27341                 );
27342             this.displayEl.update(msg);
27343         }
27344     },
27345
27346     // private
27347     onLoad : function(ds, r, o){
27348        this.cursor = o.params ? o.params.start : 0;
27349        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27350
27351        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27352        this.field.dom.value = ap;
27353        this.first.setDisabled(ap == 1);
27354        this.prev.setDisabled(ap == 1);
27355        this.next.setDisabled(ap == ps);
27356        this.last.setDisabled(ap == ps);
27357        this.loading.enable();
27358        this.updateInfo();
27359     },
27360
27361     // private
27362     getPageData : function(){
27363         var total = this.ds.getTotalCount();
27364         return {
27365             total : total,
27366             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27367             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27368         };
27369     },
27370
27371     // private
27372     onLoadError : function(){
27373         this.loading.enable();
27374     },
27375
27376     // private
27377     onPagingKeydown : function(e){
27378         var k = e.getKey();
27379         var d = this.getPageData();
27380         if(k == e.RETURN){
27381             var v = this.field.dom.value, pageNum;
27382             if(!v || isNaN(pageNum = parseInt(v, 10))){
27383                 this.field.dom.value = d.activePage;
27384                 return;
27385             }
27386             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27387             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27388             e.stopEvent();
27389         }
27390         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))
27391         {
27392           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27393           this.field.dom.value = pageNum;
27394           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27395           e.stopEvent();
27396         }
27397         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27398         {
27399           var v = this.field.dom.value, pageNum; 
27400           var increment = (e.shiftKey) ? 10 : 1;
27401           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27402             increment *= -1;
27403           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27404             this.field.dom.value = d.activePage;
27405             return;
27406           }
27407           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27408           {
27409             this.field.dom.value = parseInt(v, 10) + increment;
27410             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27411             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27412           }
27413           e.stopEvent();
27414         }
27415     },
27416
27417     // private
27418     beforeLoad : function(){
27419         if(this.loading){
27420             this.loading.disable();
27421         }
27422     },
27423
27424     // private
27425     onClick : function(which){
27426         var ds = this.ds;
27427         switch(which){
27428             case "first":
27429                 ds.load({params:{start: 0, limit: this.pageSize}});
27430             break;
27431             case "prev":
27432                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27433             break;
27434             case "next":
27435                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27436             break;
27437             case "last":
27438                 var total = ds.getTotalCount();
27439                 var extra = total % this.pageSize;
27440                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27441                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27442             break;
27443             case "refresh":
27444                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27445             break;
27446         }
27447     },
27448
27449     /**
27450      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27451      * @param {Roo.data.Store} store The data store to unbind
27452      */
27453     unbind : function(ds){
27454         ds.un("beforeload", this.beforeLoad, this);
27455         ds.un("load", this.onLoad, this);
27456         ds.un("loadexception", this.onLoadError, this);
27457         ds.un("remove", this.updateInfo, this);
27458         ds.un("add", this.updateInfo, this);
27459         this.ds = undefined;
27460     },
27461
27462     /**
27463      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27464      * @param {Roo.data.Store} store The data store to bind
27465      */
27466     bind : function(ds){
27467         ds.on("beforeload", this.beforeLoad, this);
27468         ds.on("load", this.onLoad, this);
27469         ds.on("loadexception", this.onLoadError, this);
27470         ds.on("remove", this.updateInfo, this);
27471         ds.on("add", this.updateInfo, this);
27472         this.ds = ds;
27473     }
27474 });/*
27475  * Based on:
27476  * Ext JS Library 1.1.1
27477  * Copyright(c) 2006-2007, Ext JS, LLC.
27478  *
27479  * Originally Released Under LGPL - original licence link has changed is not relivant.
27480  *
27481  * Fork - LGPL
27482  * <script type="text/javascript">
27483  */
27484
27485 /**
27486  * @class Roo.Resizable
27487  * @extends Roo.util.Observable
27488  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27489  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27490  * 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
27491  * the element will be wrapped for you automatically.</p>
27492  * <p>Here is the list of valid resize handles:</p>
27493  * <pre>
27494 Value   Description
27495 ------  -------------------
27496  'n'     north
27497  's'     south
27498  'e'     east
27499  'w'     west
27500  'nw'    northwest
27501  'sw'    southwest
27502  'se'    southeast
27503  'ne'    northeast
27504  'hd'    horizontal drag
27505  'all'   all
27506 </pre>
27507  * <p>Here's an example showing the creation of a typical Resizable:</p>
27508  * <pre><code>
27509 var resizer = new Roo.Resizable("element-id", {
27510     handles: 'all',
27511     minWidth: 200,
27512     minHeight: 100,
27513     maxWidth: 500,
27514     maxHeight: 400,
27515     pinned: true
27516 });
27517 resizer.on("resize", myHandler);
27518 </code></pre>
27519  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27520  * resizer.east.setDisplayed(false);</p>
27521  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27522  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27523  * resize operation's new size (defaults to [0, 0])
27524  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27525  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27526  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27527  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27528  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27529  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27530  * @cfg {Number} width The width of the element in pixels (defaults to null)
27531  * @cfg {Number} height The height of the element in pixels (defaults to null)
27532  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27533  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27534  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27535  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27536  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27537  * in favor of the handles config option (defaults to false)
27538  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27539  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27540  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27541  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27542  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27543  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27544  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27545  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27546  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27547  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27548  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27549  * @constructor
27550  * Create a new resizable component
27551  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27552  * @param {Object} config configuration options
27553   */
27554 Roo.Resizable = function(el, config)
27555 {
27556     this.el = Roo.get(el);
27557
27558     if(config && config.wrap){
27559         config.resizeChild = this.el;
27560         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27561         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27562         this.el.setStyle("overflow", "hidden");
27563         this.el.setPositioning(config.resizeChild.getPositioning());
27564         config.resizeChild.clearPositioning();
27565         if(!config.width || !config.height){
27566             var csize = config.resizeChild.getSize();
27567             this.el.setSize(csize.width, csize.height);
27568         }
27569         if(config.pinned && !config.adjustments){
27570             config.adjustments = "auto";
27571         }
27572     }
27573
27574     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27575     this.proxy.unselectable();
27576     this.proxy.enableDisplayMode('block');
27577
27578     Roo.apply(this, config);
27579
27580     if(this.pinned){
27581         this.disableTrackOver = true;
27582         this.el.addClass("x-resizable-pinned");
27583     }
27584     // if the element isn't positioned, make it relative
27585     var position = this.el.getStyle("position");
27586     if(position != "absolute" && position != "fixed"){
27587         this.el.setStyle("position", "relative");
27588     }
27589     if(!this.handles){ // no handles passed, must be legacy style
27590         this.handles = 's,e,se';
27591         if(this.multiDirectional){
27592             this.handles += ',n,w';
27593         }
27594     }
27595     if(this.handles == "all"){
27596         this.handles = "n s e w ne nw se sw";
27597     }
27598     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27599     var ps = Roo.Resizable.positions;
27600     for(var i = 0, len = hs.length; i < len; i++){
27601         if(hs[i] && ps[hs[i]]){
27602             var pos = ps[hs[i]];
27603             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27604         }
27605     }
27606     // legacy
27607     this.corner = this.southeast;
27608     
27609     // updateBox = the box can move..
27610     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27611         this.updateBox = true;
27612     }
27613
27614     this.activeHandle = null;
27615
27616     if(this.resizeChild){
27617         if(typeof this.resizeChild == "boolean"){
27618             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27619         }else{
27620             this.resizeChild = Roo.get(this.resizeChild, true);
27621         }
27622     }
27623     
27624     if(this.adjustments == "auto"){
27625         var rc = this.resizeChild;
27626         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27627         if(rc && (hw || hn)){
27628             rc.position("relative");
27629             rc.setLeft(hw ? hw.el.getWidth() : 0);
27630             rc.setTop(hn ? hn.el.getHeight() : 0);
27631         }
27632         this.adjustments = [
27633             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27634             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27635         ];
27636     }
27637
27638     if(this.draggable){
27639         this.dd = this.dynamic ?
27640             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27641         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27642     }
27643
27644     // public events
27645     this.addEvents({
27646         /**
27647          * @event beforeresize
27648          * Fired before resize is allowed. Set enabled to false to cancel resize.
27649          * @param {Roo.Resizable} this
27650          * @param {Roo.EventObject} e The mousedown event
27651          */
27652         "beforeresize" : true,
27653         /**
27654          * @event resize
27655          * Fired after a resize.
27656          * @param {Roo.Resizable} this
27657          * @param {Number} width The new width
27658          * @param {Number} height The new height
27659          * @param {Roo.EventObject} e The mouseup event
27660          */
27661         "resize" : true
27662     });
27663
27664     if(this.width !== null && this.height !== null){
27665         this.resizeTo(this.width, this.height);
27666     }else{
27667         this.updateChildSize();
27668     }
27669     if(Roo.isIE){
27670         this.el.dom.style.zoom = 1;
27671     }
27672     Roo.Resizable.superclass.constructor.call(this);
27673 };
27674
27675 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27676         resizeChild : false,
27677         adjustments : [0, 0],
27678         minWidth : 5,
27679         minHeight : 5,
27680         maxWidth : 10000,
27681         maxHeight : 10000,
27682         enabled : true,
27683         animate : false,
27684         duration : .35,
27685         dynamic : false,
27686         handles : false,
27687         multiDirectional : false,
27688         disableTrackOver : false,
27689         easing : 'easeOutStrong',
27690         widthIncrement : 0,
27691         heightIncrement : 0,
27692         pinned : false,
27693         width : null,
27694         height : null,
27695         preserveRatio : false,
27696         transparent: false,
27697         minX: 0,
27698         minY: 0,
27699         draggable: false,
27700
27701         /**
27702          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27703          */
27704         constrainTo: undefined,
27705         /**
27706          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27707          */
27708         resizeRegion: undefined,
27709
27710
27711     /**
27712      * Perform a manual resize
27713      * @param {Number} width
27714      * @param {Number} height
27715      */
27716     resizeTo : function(width, height){
27717         this.el.setSize(width, height);
27718         this.updateChildSize();
27719         this.fireEvent("resize", this, width, height, null);
27720     },
27721
27722     // private
27723     startSizing : function(e, handle){
27724         this.fireEvent("beforeresize", this, e);
27725         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27726
27727             if(!this.overlay){
27728                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27729                 this.overlay.unselectable();
27730                 this.overlay.enableDisplayMode("block");
27731                 this.overlay.on("mousemove", this.onMouseMove, this);
27732                 this.overlay.on("mouseup", this.onMouseUp, this);
27733             }
27734             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27735
27736             this.resizing = true;
27737             this.startBox = this.el.getBox();
27738             this.startPoint = e.getXY();
27739             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27740                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27741
27742             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27743             this.overlay.show();
27744
27745             if(this.constrainTo) {
27746                 var ct = Roo.get(this.constrainTo);
27747                 this.resizeRegion = ct.getRegion().adjust(
27748                     ct.getFrameWidth('t'),
27749                     ct.getFrameWidth('l'),
27750                     -ct.getFrameWidth('b'),
27751                     -ct.getFrameWidth('r')
27752                 );
27753             }
27754
27755             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27756             this.proxy.show();
27757             this.proxy.setBox(this.startBox);
27758             if(!this.dynamic){
27759                 this.proxy.setStyle('visibility', 'visible');
27760             }
27761         }
27762     },
27763
27764     // private
27765     onMouseDown : function(handle, e){
27766         if(this.enabled){
27767             e.stopEvent();
27768             this.activeHandle = handle;
27769             this.startSizing(e, handle);
27770         }
27771     },
27772
27773     // private
27774     onMouseUp : function(e){
27775         var size = this.resizeElement();
27776         this.resizing = false;
27777         this.handleOut();
27778         this.overlay.hide();
27779         this.proxy.hide();
27780         this.fireEvent("resize", this, size.width, size.height, e);
27781     },
27782
27783     // private
27784     updateChildSize : function(){
27785         if(this.resizeChild){
27786             var el = this.el;
27787             var child = this.resizeChild;
27788             var adj = this.adjustments;
27789             if(el.dom.offsetWidth){
27790                 var b = el.getSize(true);
27791                 child.setSize(b.width+adj[0], b.height+adj[1]);
27792             }
27793             // Second call here for IE
27794             // The first call enables instant resizing and
27795             // the second call corrects scroll bars if they
27796             // exist
27797             if(Roo.isIE){
27798                 setTimeout(function(){
27799                     if(el.dom.offsetWidth){
27800                         var b = el.getSize(true);
27801                         child.setSize(b.width+adj[0], b.height+adj[1]);
27802                     }
27803                 }, 10);
27804             }
27805         }
27806     },
27807
27808     // private
27809     snap : function(value, inc, min){
27810         if(!inc || !value) return value;
27811         var newValue = value;
27812         var m = value % inc;
27813         if(m > 0){
27814             if(m > (inc/2)){
27815                 newValue = value + (inc-m);
27816             }else{
27817                 newValue = value - m;
27818             }
27819         }
27820         return Math.max(min, newValue);
27821     },
27822
27823     // private
27824     resizeElement : function(){
27825         var box = this.proxy.getBox();
27826         if(this.updateBox){
27827             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27828         }else{
27829             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27830         }
27831         this.updateChildSize();
27832         if(!this.dynamic){
27833             this.proxy.hide();
27834         }
27835         return box;
27836     },
27837
27838     // private
27839     constrain : function(v, diff, m, mx){
27840         if(v - diff < m){
27841             diff = v - m;
27842         }else if(v - diff > mx){
27843             diff = mx - v;
27844         }
27845         return diff;
27846     },
27847
27848     // private
27849     onMouseMove : function(e){
27850         if(this.enabled){
27851             try{// try catch so if something goes wrong the user doesn't get hung
27852
27853             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27854                 return;
27855             }
27856
27857             //var curXY = this.startPoint;
27858             var curSize = this.curSize || this.startBox;
27859             var x = this.startBox.x, y = this.startBox.y;
27860             var ox = x, oy = y;
27861             var w = curSize.width, h = curSize.height;
27862             var ow = w, oh = h;
27863             var mw = this.minWidth, mh = this.minHeight;
27864             var mxw = this.maxWidth, mxh = this.maxHeight;
27865             var wi = this.widthIncrement;
27866             var hi = this.heightIncrement;
27867
27868             var eventXY = e.getXY();
27869             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27870             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27871
27872             var pos = this.activeHandle.position;
27873
27874             switch(pos){
27875                 case "east":
27876                     w += diffX;
27877                     w = Math.min(Math.max(mw, w), mxw);
27878                     break;
27879              
27880                 case "south":
27881                     h += diffY;
27882                     h = Math.min(Math.max(mh, h), mxh);
27883                     break;
27884                 case "southeast":
27885                     w += diffX;
27886                     h += diffY;
27887                     w = Math.min(Math.max(mw, w), mxw);
27888                     h = Math.min(Math.max(mh, h), mxh);
27889                     break;
27890                 case "north":
27891                     diffY = this.constrain(h, diffY, mh, mxh);
27892                     y += diffY;
27893                     h -= diffY;
27894                     break;
27895                 case "hdrag":
27896                     
27897                     if (wi) {
27898                         var adiffX = Math.abs(diffX);
27899                         var sub = (adiffX % wi); // how much 
27900                         if (sub > (wi/2)) { // far enough to snap
27901                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27902                         } else {
27903                             // remove difference.. 
27904                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27905                         }
27906                     }
27907                     x += diffX;
27908                     x = Math.max(this.minX, x);
27909                     break;
27910                 case "west":
27911                     diffX = this.constrain(w, diffX, mw, mxw);
27912                     x += diffX;
27913                     w -= diffX;
27914                     break;
27915                 case "northeast":
27916                     w += diffX;
27917                     w = Math.min(Math.max(mw, w), mxw);
27918                     diffY = this.constrain(h, diffY, mh, mxh);
27919                     y += diffY;
27920                     h -= diffY;
27921                     break;
27922                 case "northwest":
27923                     diffX = this.constrain(w, diffX, mw, mxw);
27924                     diffY = this.constrain(h, diffY, mh, mxh);
27925                     y += diffY;
27926                     h -= diffY;
27927                     x += diffX;
27928                     w -= diffX;
27929                     break;
27930                case "southwest":
27931                     diffX = this.constrain(w, diffX, mw, mxw);
27932                     h += diffY;
27933                     h = Math.min(Math.max(mh, h), mxh);
27934                     x += diffX;
27935                     w -= diffX;
27936                     break;
27937             }
27938
27939             var sw = this.snap(w, wi, mw);
27940             var sh = this.snap(h, hi, mh);
27941             if(sw != w || sh != h){
27942                 switch(pos){
27943                     case "northeast":
27944                         y -= sh - h;
27945                     break;
27946                     case "north":
27947                         y -= sh - h;
27948                         break;
27949                     case "southwest":
27950                         x -= sw - w;
27951                     break;
27952                     case "west":
27953                         x -= sw - w;
27954                         break;
27955                     case "northwest":
27956                         x -= sw - w;
27957                         y -= sh - h;
27958                     break;
27959                 }
27960                 w = sw;
27961                 h = sh;
27962             }
27963
27964             if(this.preserveRatio){
27965                 switch(pos){
27966                     case "southeast":
27967                     case "east":
27968                         h = oh * (w/ow);
27969                         h = Math.min(Math.max(mh, h), mxh);
27970                         w = ow * (h/oh);
27971                        break;
27972                     case "south":
27973                         w = ow * (h/oh);
27974                         w = Math.min(Math.max(mw, w), mxw);
27975                         h = oh * (w/ow);
27976                         break;
27977                     case "northeast":
27978                         w = ow * (h/oh);
27979                         w = Math.min(Math.max(mw, w), mxw);
27980                         h = oh * (w/ow);
27981                     break;
27982                     case "north":
27983                         var tw = w;
27984                         w = ow * (h/oh);
27985                         w = Math.min(Math.max(mw, w), mxw);
27986                         h = oh * (w/ow);
27987                         x += (tw - w) / 2;
27988                         break;
27989                     case "southwest":
27990                         h = oh * (w/ow);
27991                         h = Math.min(Math.max(mh, h), mxh);
27992                         var tw = w;
27993                         w = ow * (h/oh);
27994                         x += tw - w;
27995                         break;
27996                     case "west":
27997                         var th = h;
27998                         h = oh * (w/ow);
27999                         h = Math.min(Math.max(mh, h), mxh);
28000                         y += (th - h) / 2;
28001                         var tw = w;
28002                         w = ow * (h/oh);
28003                         x += tw - w;
28004                        break;
28005                     case "northwest":
28006                         var tw = w;
28007                         var th = h;
28008                         h = oh * (w/ow);
28009                         h = Math.min(Math.max(mh, h), mxh);
28010                         w = ow * (h/oh);
28011                         y += th - h;
28012                         x += tw - w;
28013                        break;
28014
28015                 }
28016             }
28017             if (pos == 'hdrag') {
28018                 w = ow;
28019             }
28020             this.proxy.setBounds(x, y, w, h);
28021             if(this.dynamic){
28022                 this.resizeElement();
28023             }
28024             }catch(e){}
28025         }
28026     },
28027
28028     // private
28029     handleOver : function(){
28030         if(this.enabled){
28031             this.el.addClass("x-resizable-over");
28032         }
28033     },
28034
28035     // private
28036     handleOut : function(){
28037         if(!this.resizing){
28038             this.el.removeClass("x-resizable-over");
28039         }
28040     },
28041
28042     /**
28043      * Returns the element this component is bound to.
28044      * @return {Roo.Element}
28045      */
28046     getEl : function(){
28047         return this.el;
28048     },
28049
28050     /**
28051      * Returns the resizeChild element (or null).
28052      * @return {Roo.Element}
28053      */
28054     getResizeChild : function(){
28055         return this.resizeChild;
28056     },
28057
28058     /**
28059      * Destroys this resizable. If the element was wrapped and
28060      * removeEl is not true then the element remains.
28061      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28062      */
28063     destroy : function(removeEl){
28064         this.proxy.remove();
28065         if(this.overlay){
28066             this.overlay.removeAllListeners();
28067             this.overlay.remove();
28068         }
28069         var ps = Roo.Resizable.positions;
28070         for(var k in ps){
28071             if(typeof ps[k] != "function" && this[ps[k]]){
28072                 var h = this[ps[k]];
28073                 h.el.removeAllListeners();
28074                 h.el.remove();
28075             }
28076         }
28077         if(removeEl){
28078             this.el.update("");
28079             this.el.remove();
28080         }
28081     }
28082 });
28083
28084 // private
28085 // hash to map config positions to true positions
28086 Roo.Resizable.positions = {
28087     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28088     hd: "hdrag"
28089 };
28090
28091 // private
28092 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28093     if(!this.tpl){
28094         // only initialize the template if resizable is used
28095         var tpl = Roo.DomHelper.createTemplate(
28096             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28097         );
28098         tpl.compile();
28099         Roo.Resizable.Handle.prototype.tpl = tpl;
28100     }
28101     this.position = pos;
28102     this.rz = rz;
28103     // show north drag fro topdra
28104     var handlepos = pos == 'hdrag' ? 'north' : pos;
28105     
28106     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28107     if (pos == 'hdrag') {
28108         this.el.setStyle('cursor', 'pointer');
28109     }
28110     this.el.unselectable();
28111     if(transparent){
28112         this.el.setOpacity(0);
28113     }
28114     this.el.on("mousedown", this.onMouseDown, this);
28115     if(!disableTrackOver){
28116         this.el.on("mouseover", this.onMouseOver, this);
28117         this.el.on("mouseout", this.onMouseOut, this);
28118     }
28119 };
28120
28121 // private
28122 Roo.Resizable.Handle.prototype = {
28123     afterResize : function(rz){
28124         // do nothing
28125     },
28126     // private
28127     onMouseDown : function(e){
28128         this.rz.onMouseDown(this, e);
28129     },
28130     // private
28131     onMouseOver : function(e){
28132         this.rz.handleOver(this, e);
28133     },
28134     // private
28135     onMouseOut : function(e){
28136         this.rz.handleOut(this, e);
28137     }
28138 };/*
28139  * Based on:
28140  * Ext JS Library 1.1.1
28141  * Copyright(c) 2006-2007, Ext JS, LLC.
28142  *
28143  * Originally Released Under LGPL - original licence link has changed is not relivant.
28144  *
28145  * Fork - LGPL
28146  * <script type="text/javascript">
28147  */
28148
28149 /**
28150  * @class Roo.Editor
28151  * @extends Roo.Component
28152  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28153  * @constructor
28154  * Create a new Editor
28155  * @param {Roo.form.Field} field The Field object (or descendant)
28156  * @param {Object} config The config object
28157  */
28158 Roo.Editor = function(field, config){
28159     Roo.Editor.superclass.constructor.call(this, config);
28160     this.field = field;
28161     this.addEvents({
28162         /**
28163              * @event beforestartedit
28164              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28165              * false from the handler of this event.
28166              * @param {Editor} this
28167              * @param {Roo.Element} boundEl The underlying element bound to this editor
28168              * @param {Mixed} value The field value being set
28169              */
28170         "beforestartedit" : true,
28171         /**
28172              * @event startedit
28173              * Fires when this editor is displayed
28174              * @param {Roo.Element} boundEl The underlying element bound to this editor
28175              * @param {Mixed} value The starting field value
28176              */
28177         "startedit" : true,
28178         /**
28179              * @event beforecomplete
28180              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28181              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28182              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28183              * event will not fire since no edit actually occurred.
28184              * @param {Editor} this
28185              * @param {Mixed} value The current field value
28186              * @param {Mixed} startValue The original field value
28187              */
28188         "beforecomplete" : true,
28189         /**
28190              * @event complete
28191              * Fires after editing is complete and any changed value has been written to the underlying field.
28192              * @param {Editor} this
28193              * @param {Mixed} value The current field value
28194              * @param {Mixed} startValue The original field value
28195              */
28196         "complete" : true,
28197         /**
28198          * @event specialkey
28199          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28200          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28201          * @param {Roo.form.Field} this
28202          * @param {Roo.EventObject} e The event object
28203          */
28204         "specialkey" : true
28205     });
28206 };
28207
28208 Roo.extend(Roo.Editor, Roo.Component, {
28209     /**
28210      * @cfg {Boolean/String} autosize
28211      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28212      * or "height" to adopt the height only (defaults to false)
28213      */
28214     /**
28215      * @cfg {Boolean} revertInvalid
28216      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28217      * validation fails (defaults to true)
28218      */
28219     /**
28220      * @cfg {Boolean} ignoreNoChange
28221      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28222      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28223      * will never be ignored.
28224      */
28225     /**
28226      * @cfg {Boolean} hideEl
28227      * False to keep the bound element visible while the editor is displayed (defaults to true)
28228      */
28229     /**
28230      * @cfg {Mixed} value
28231      * The data value of the underlying field (defaults to "")
28232      */
28233     value : "",
28234     /**
28235      * @cfg {String} alignment
28236      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28237      */
28238     alignment: "c-c?",
28239     /**
28240      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28241      * for bottom-right shadow (defaults to "frame")
28242      */
28243     shadow : "frame",
28244     /**
28245      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28246      */
28247     constrain : false,
28248     /**
28249      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28250      */
28251     completeOnEnter : false,
28252     /**
28253      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28254      */
28255     cancelOnEsc : false,
28256     /**
28257      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28258      */
28259     updateEl : false,
28260
28261     // private
28262     onRender : function(ct, position){
28263         this.el = new Roo.Layer({
28264             shadow: this.shadow,
28265             cls: "x-editor",
28266             parentEl : ct,
28267             shim : this.shim,
28268             shadowOffset:4,
28269             id: this.id,
28270             constrain: this.constrain
28271         });
28272         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28273         if(this.field.msgTarget != 'title'){
28274             this.field.msgTarget = 'qtip';
28275         }
28276         this.field.render(this.el);
28277         if(Roo.isGecko){
28278             this.field.el.dom.setAttribute('autocomplete', 'off');
28279         }
28280         this.field.on("specialkey", this.onSpecialKey, this);
28281         if(this.swallowKeys){
28282             this.field.el.swallowEvent(['keydown','keypress']);
28283         }
28284         this.field.show();
28285         this.field.on("blur", this.onBlur, this);
28286         if(this.field.grow){
28287             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28288         }
28289     },
28290
28291     onSpecialKey : function(field, e){
28292         //Roo.log('editor onSpecialKey');
28293         if(this.completeOnEnter && e.getKey() == e.ENTER){
28294             e.stopEvent();
28295             this.completeEdit();
28296         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28297             this.cancelEdit();
28298         }else{
28299             this.fireEvent('specialkey', field, e);
28300         }
28301     },
28302
28303     /**
28304      * Starts the editing process and shows the editor.
28305      * @param {String/HTMLElement/Element} el The element to edit
28306      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28307       * to the innerHTML of el.
28308      */
28309     startEdit : function(el, value){
28310         if(this.editing){
28311             this.completeEdit();
28312         }
28313         this.boundEl = Roo.get(el);
28314         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28315         if(!this.rendered){
28316             this.render(this.parentEl || document.body);
28317         }
28318         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28319             return;
28320         }
28321         this.startValue = v;
28322         this.field.setValue(v);
28323         if(this.autoSize){
28324             var sz = this.boundEl.getSize();
28325             switch(this.autoSize){
28326                 case "width":
28327                 this.setSize(sz.width,  "");
28328                 break;
28329                 case "height":
28330                 this.setSize("",  sz.height);
28331                 break;
28332                 default:
28333                 this.setSize(sz.width,  sz.height);
28334             }
28335         }
28336         this.el.alignTo(this.boundEl, this.alignment);
28337         this.editing = true;
28338         if(Roo.QuickTips){
28339             Roo.QuickTips.disable();
28340         }
28341         this.show();
28342     },
28343
28344     /**
28345      * Sets the height and width of this editor.
28346      * @param {Number} width The new width
28347      * @param {Number} height The new height
28348      */
28349     setSize : function(w, h){
28350         this.field.setSize(w, h);
28351         if(this.el){
28352             this.el.sync();
28353         }
28354     },
28355
28356     /**
28357      * Realigns the editor to the bound field based on the current alignment config value.
28358      */
28359     realign : function(){
28360         this.el.alignTo(this.boundEl, this.alignment);
28361     },
28362
28363     /**
28364      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28365      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28366      */
28367     completeEdit : function(remainVisible){
28368         if(!this.editing){
28369             return;
28370         }
28371         var v = this.getValue();
28372         if(this.revertInvalid !== false && !this.field.isValid()){
28373             v = this.startValue;
28374             this.cancelEdit(true);
28375         }
28376         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28377             this.editing = false;
28378             this.hide();
28379             return;
28380         }
28381         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28382             this.editing = false;
28383             if(this.updateEl && this.boundEl){
28384                 this.boundEl.update(v);
28385             }
28386             if(remainVisible !== true){
28387                 this.hide();
28388             }
28389             this.fireEvent("complete", this, v, this.startValue);
28390         }
28391     },
28392
28393     // private
28394     onShow : function(){
28395         this.el.show();
28396         if(this.hideEl !== false){
28397             this.boundEl.hide();
28398         }
28399         this.field.show();
28400         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28401             this.fixIEFocus = true;
28402             this.deferredFocus.defer(50, this);
28403         }else{
28404             this.field.focus();
28405         }
28406         this.fireEvent("startedit", this.boundEl, this.startValue);
28407     },
28408
28409     deferredFocus : function(){
28410         if(this.editing){
28411             this.field.focus();
28412         }
28413     },
28414
28415     /**
28416      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28417      * reverted to the original starting value.
28418      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28419      * cancel (defaults to false)
28420      */
28421     cancelEdit : function(remainVisible){
28422         if(this.editing){
28423             this.setValue(this.startValue);
28424             if(remainVisible !== true){
28425                 this.hide();
28426             }
28427         }
28428     },
28429
28430     // private
28431     onBlur : function(){
28432         if(this.allowBlur !== true && this.editing){
28433             this.completeEdit();
28434         }
28435     },
28436
28437     // private
28438     onHide : function(){
28439         if(this.editing){
28440             this.completeEdit();
28441             return;
28442         }
28443         this.field.blur();
28444         if(this.field.collapse){
28445             this.field.collapse();
28446         }
28447         this.el.hide();
28448         if(this.hideEl !== false){
28449             this.boundEl.show();
28450         }
28451         if(Roo.QuickTips){
28452             Roo.QuickTips.enable();
28453         }
28454     },
28455
28456     /**
28457      * Sets the data value of the editor
28458      * @param {Mixed} value Any valid value supported by the underlying field
28459      */
28460     setValue : function(v){
28461         this.field.setValue(v);
28462     },
28463
28464     /**
28465      * Gets the data value of the editor
28466      * @return {Mixed} The data value
28467      */
28468     getValue : function(){
28469         return this.field.getValue();
28470     }
28471 });/*
28472  * Based on:
28473  * Ext JS Library 1.1.1
28474  * Copyright(c) 2006-2007, Ext JS, LLC.
28475  *
28476  * Originally Released Under LGPL - original licence link has changed is not relivant.
28477  *
28478  * Fork - LGPL
28479  * <script type="text/javascript">
28480  */
28481  
28482 /**
28483  * @class Roo.BasicDialog
28484  * @extends Roo.util.Observable
28485  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28486  * <pre><code>
28487 var dlg = new Roo.BasicDialog("my-dlg", {
28488     height: 200,
28489     width: 300,
28490     minHeight: 100,
28491     minWidth: 150,
28492     modal: true,
28493     proxyDrag: true,
28494     shadow: true
28495 });
28496 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28497 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28498 dlg.addButton('Cancel', dlg.hide, dlg);
28499 dlg.show();
28500 </code></pre>
28501   <b>A Dialog should always be a direct child of the body element.</b>
28502  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28503  * @cfg {String} title Default text to display in the title bar (defaults to null)
28504  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28505  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28506  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28507  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28508  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28509  * (defaults to null with no animation)
28510  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28511  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28512  * property for valid values (defaults to 'all')
28513  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28514  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28515  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28516  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28517  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28518  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28519  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28520  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28521  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28522  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28523  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28524  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28525  * draggable = true (defaults to false)
28526  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28527  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28528  * shadow (defaults to false)
28529  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28530  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28531  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28532  * @cfg {Array} buttons Array of buttons
28533  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28534  * @constructor
28535  * Create a new BasicDialog.
28536  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28537  * @param {Object} config Configuration options
28538  */
28539 Roo.BasicDialog = function(el, config){
28540     this.el = Roo.get(el);
28541     var dh = Roo.DomHelper;
28542     if(!this.el && config && config.autoCreate){
28543         if(typeof config.autoCreate == "object"){
28544             if(!config.autoCreate.id){
28545                 config.autoCreate.id = el;
28546             }
28547             this.el = dh.append(document.body,
28548                         config.autoCreate, true);
28549         }else{
28550             this.el = dh.append(document.body,
28551                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28552         }
28553     }
28554     el = this.el;
28555     el.setDisplayed(true);
28556     el.hide = this.hideAction;
28557     this.id = el.id;
28558     el.addClass("x-dlg");
28559
28560     Roo.apply(this, config);
28561
28562     this.proxy = el.createProxy("x-dlg-proxy");
28563     this.proxy.hide = this.hideAction;
28564     this.proxy.setOpacity(.5);
28565     this.proxy.hide();
28566
28567     if(config.width){
28568         el.setWidth(config.width);
28569     }
28570     if(config.height){
28571         el.setHeight(config.height);
28572     }
28573     this.size = el.getSize();
28574     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28575         this.xy = [config.x,config.y];
28576     }else{
28577         this.xy = el.getCenterXY(true);
28578     }
28579     /** The header element @type Roo.Element */
28580     this.header = el.child("> .x-dlg-hd");
28581     /** The body element @type Roo.Element */
28582     this.body = el.child("> .x-dlg-bd");
28583     /** The footer element @type Roo.Element */
28584     this.footer = el.child("> .x-dlg-ft");
28585
28586     if(!this.header){
28587         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28588     }
28589     if(!this.body){
28590         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28591     }
28592
28593     this.header.unselectable();
28594     if(this.title){
28595         this.header.update(this.title);
28596     }
28597     // this element allows the dialog to be focused for keyboard event
28598     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28599     this.focusEl.swallowEvent("click", true);
28600
28601     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28602
28603     // wrap the body and footer for special rendering
28604     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28605     if(this.footer){
28606         this.bwrap.dom.appendChild(this.footer.dom);
28607     }
28608
28609     this.bg = this.el.createChild({
28610         tag: "div", cls:"x-dlg-bg",
28611         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28612     });
28613     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28614
28615
28616     if(this.autoScroll !== false && !this.autoTabs){
28617         this.body.setStyle("overflow", "auto");
28618     }
28619
28620     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28621
28622     if(this.closable !== false){
28623         this.el.addClass("x-dlg-closable");
28624         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28625         this.close.on("click", this.closeClick, this);
28626         this.close.addClassOnOver("x-dlg-close-over");
28627     }
28628     if(this.collapsible !== false){
28629         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28630         this.collapseBtn.on("click", this.collapseClick, this);
28631         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28632         this.header.on("dblclick", this.collapseClick, this);
28633     }
28634     if(this.resizable !== false){
28635         this.el.addClass("x-dlg-resizable");
28636         this.resizer = new Roo.Resizable(el, {
28637             minWidth: this.minWidth || 80,
28638             minHeight:this.minHeight || 80,
28639             handles: this.resizeHandles || "all",
28640             pinned: true
28641         });
28642         this.resizer.on("beforeresize", this.beforeResize, this);
28643         this.resizer.on("resize", this.onResize, this);
28644     }
28645     if(this.draggable !== false){
28646         el.addClass("x-dlg-draggable");
28647         if (!this.proxyDrag) {
28648             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28649         }
28650         else {
28651             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28652         }
28653         dd.setHandleElId(this.header.id);
28654         dd.endDrag = this.endMove.createDelegate(this);
28655         dd.startDrag = this.startMove.createDelegate(this);
28656         dd.onDrag = this.onDrag.createDelegate(this);
28657         dd.scroll = false;
28658         this.dd = dd;
28659     }
28660     if(this.modal){
28661         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28662         this.mask.enableDisplayMode("block");
28663         this.mask.hide();
28664         this.el.addClass("x-dlg-modal");
28665     }
28666     if(this.shadow){
28667         this.shadow = new Roo.Shadow({
28668             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28669             offset : this.shadowOffset
28670         });
28671     }else{
28672         this.shadowOffset = 0;
28673     }
28674     if(Roo.useShims && this.shim !== false){
28675         this.shim = this.el.createShim();
28676         this.shim.hide = this.hideAction;
28677         this.shim.hide();
28678     }else{
28679         this.shim = false;
28680     }
28681     if(this.autoTabs){
28682         this.initTabs();
28683     }
28684     if (this.buttons) { 
28685         var bts= this.buttons;
28686         this.buttons = [];
28687         Roo.each(bts, function(b) {
28688             this.addButton(b);
28689         }, this);
28690     }
28691     
28692     
28693     this.addEvents({
28694         /**
28695          * @event keydown
28696          * Fires when a key is pressed
28697          * @param {Roo.BasicDialog} this
28698          * @param {Roo.EventObject} e
28699          */
28700         "keydown" : true,
28701         /**
28702          * @event move
28703          * Fires when this dialog is moved by the user.
28704          * @param {Roo.BasicDialog} this
28705          * @param {Number} x The new page X
28706          * @param {Number} y The new page Y
28707          */
28708         "move" : true,
28709         /**
28710          * @event resize
28711          * Fires when this dialog is resized by the user.
28712          * @param {Roo.BasicDialog} this
28713          * @param {Number} width The new width
28714          * @param {Number} height The new height
28715          */
28716         "resize" : true,
28717         /**
28718          * @event beforehide
28719          * Fires before this dialog is hidden.
28720          * @param {Roo.BasicDialog} this
28721          */
28722         "beforehide" : true,
28723         /**
28724          * @event hide
28725          * Fires when this dialog is hidden.
28726          * @param {Roo.BasicDialog} this
28727          */
28728         "hide" : true,
28729         /**
28730          * @event beforeshow
28731          * Fires before this dialog is shown.
28732          * @param {Roo.BasicDialog} this
28733          */
28734         "beforeshow" : true,
28735         /**
28736          * @event show
28737          * Fires when this dialog is shown.
28738          * @param {Roo.BasicDialog} this
28739          */
28740         "show" : true
28741     });
28742     el.on("keydown", this.onKeyDown, this);
28743     el.on("mousedown", this.toFront, this);
28744     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28745     this.el.hide();
28746     Roo.DialogManager.register(this);
28747     Roo.BasicDialog.superclass.constructor.call(this);
28748 };
28749
28750 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28751     shadowOffset: Roo.isIE ? 6 : 5,
28752     minHeight: 80,
28753     minWidth: 200,
28754     minButtonWidth: 75,
28755     defaultButton: null,
28756     buttonAlign: "right",
28757     tabTag: 'div',
28758     firstShow: true,
28759
28760     /**
28761      * Sets the dialog title text
28762      * @param {String} text The title text to display
28763      * @return {Roo.BasicDialog} this
28764      */
28765     setTitle : function(text){
28766         this.header.update(text);
28767         return this;
28768     },
28769
28770     // private
28771     closeClick : function(){
28772         this.hide();
28773     },
28774
28775     // private
28776     collapseClick : function(){
28777         this[this.collapsed ? "expand" : "collapse"]();
28778     },
28779
28780     /**
28781      * Collapses the dialog to its minimized state (only the title bar is visible).
28782      * Equivalent to the user clicking the collapse dialog button.
28783      */
28784     collapse : function(){
28785         if(!this.collapsed){
28786             this.collapsed = true;
28787             this.el.addClass("x-dlg-collapsed");
28788             this.restoreHeight = this.el.getHeight();
28789             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28790         }
28791     },
28792
28793     /**
28794      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28795      * clicking the expand dialog button.
28796      */
28797     expand : function(){
28798         if(this.collapsed){
28799             this.collapsed = false;
28800             this.el.removeClass("x-dlg-collapsed");
28801             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28802         }
28803     },
28804
28805     /**
28806      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28807      * @return {Roo.TabPanel} The tabs component
28808      */
28809     initTabs : function(){
28810         var tabs = this.getTabs();
28811         while(tabs.getTab(0)){
28812             tabs.removeTab(0);
28813         }
28814         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28815             var dom = el.dom;
28816             tabs.addTab(Roo.id(dom), dom.title);
28817             dom.title = "";
28818         });
28819         tabs.activate(0);
28820         return tabs;
28821     },
28822
28823     // private
28824     beforeResize : function(){
28825         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28826     },
28827
28828     // private
28829     onResize : function(){
28830         this.refreshSize();
28831         this.syncBodyHeight();
28832         this.adjustAssets();
28833         this.focus();
28834         this.fireEvent("resize", this, this.size.width, this.size.height);
28835     },
28836
28837     // private
28838     onKeyDown : function(e){
28839         if(this.isVisible()){
28840             this.fireEvent("keydown", this, e);
28841         }
28842     },
28843
28844     /**
28845      * Resizes the dialog.
28846      * @param {Number} width
28847      * @param {Number} height
28848      * @return {Roo.BasicDialog} this
28849      */
28850     resizeTo : function(width, height){
28851         this.el.setSize(width, height);
28852         this.size = {width: width, height: height};
28853         this.syncBodyHeight();
28854         if(this.fixedcenter){
28855             this.center();
28856         }
28857         if(this.isVisible()){
28858             this.constrainXY();
28859             this.adjustAssets();
28860         }
28861         this.fireEvent("resize", this, width, height);
28862         return this;
28863     },
28864
28865
28866     /**
28867      * Resizes the dialog to fit the specified content size.
28868      * @param {Number} width
28869      * @param {Number} height
28870      * @return {Roo.BasicDialog} this
28871      */
28872     setContentSize : function(w, h){
28873         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28874         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28875         //if(!this.el.isBorderBox()){
28876             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28877             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28878         //}
28879         if(this.tabs){
28880             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28881             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28882         }
28883         this.resizeTo(w, h);
28884         return this;
28885     },
28886
28887     /**
28888      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28889      * executed in response to a particular key being pressed while the dialog is active.
28890      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28891      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28892      * @param {Function} fn The function to call
28893      * @param {Object} scope (optional) The scope of the function
28894      * @return {Roo.BasicDialog} this
28895      */
28896     addKeyListener : function(key, fn, scope){
28897         var keyCode, shift, ctrl, alt;
28898         if(typeof key == "object" && !(key instanceof Array)){
28899             keyCode = key["key"];
28900             shift = key["shift"];
28901             ctrl = key["ctrl"];
28902             alt = key["alt"];
28903         }else{
28904             keyCode = key;
28905         }
28906         var handler = function(dlg, e){
28907             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28908                 var k = e.getKey();
28909                 if(keyCode instanceof Array){
28910                     for(var i = 0, len = keyCode.length; i < len; i++){
28911                         if(keyCode[i] == k){
28912                           fn.call(scope || window, dlg, k, e);
28913                           return;
28914                         }
28915                     }
28916                 }else{
28917                     if(k == keyCode){
28918                         fn.call(scope || window, dlg, k, e);
28919                     }
28920                 }
28921             }
28922         };
28923         this.on("keydown", handler);
28924         return this;
28925     },
28926
28927     /**
28928      * Returns the TabPanel component (creates it if it doesn't exist).
28929      * Note: If you wish to simply check for the existence of tabs without creating them,
28930      * check for a null 'tabs' property.
28931      * @return {Roo.TabPanel} The tabs component
28932      */
28933     getTabs : function(){
28934         if(!this.tabs){
28935             this.el.addClass("x-dlg-auto-tabs");
28936             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28937             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28938         }
28939         return this.tabs;
28940     },
28941
28942     /**
28943      * Adds a button to the footer section of the dialog.
28944      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28945      * object or a valid Roo.DomHelper element config
28946      * @param {Function} handler The function called when the button is clicked
28947      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28948      * @return {Roo.Button} The new button
28949      */
28950     addButton : function(config, handler, scope){
28951         var dh = Roo.DomHelper;
28952         if(!this.footer){
28953             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28954         }
28955         if(!this.btnContainer){
28956             var tb = this.footer.createChild({
28957
28958                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28959                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28960             }, null, true);
28961             this.btnContainer = tb.firstChild.firstChild.firstChild;
28962         }
28963         var bconfig = {
28964             handler: handler,
28965             scope: scope,
28966             minWidth: this.minButtonWidth,
28967             hideParent:true
28968         };
28969         if(typeof config == "string"){
28970             bconfig.text = config;
28971         }else{
28972             if(config.tag){
28973                 bconfig.dhconfig = config;
28974             }else{
28975                 Roo.apply(bconfig, config);
28976             }
28977         }
28978         var fc = false;
28979         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28980             bconfig.position = Math.max(0, bconfig.position);
28981             fc = this.btnContainer.childNodes[bconfig.position];
28982         }
28983          
28984         var btn = new Roo.Button(
28985             fc ? 
28986                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28987                 : this.btnContainer.appendChild(document.createElement("td")),
28988             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28989             bconfig
28990         );
28991         this.syncBodyHeight();
28992         if(!this.buttons){
28993             /**
28994              * Array of all the buttons that have been added to this dialog via addButton
28995              * @type Array
28996              */
28997             this.buttons = [];
28998         }
28999         this.buttons.push(btn);
29000         return btn;
29001     },
29002
29003     /**
29004      * Sets the default button to be focused when the dialog is displayed.
29005      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29006      * @return {Roo.BasicDialog} this
29007      */
29008     setDefaultButton : function(btn){
29009         this.defaultButton = btn;
29010         return this;
29011     },
29012
29013     // private
29014     getHeaderFooterHeight : function(safe){
29015         var height = 0;
29016         if(this.header){
29017            height += this.header.getHeight();
29018         }
29019         if(this.footer){
29020            var fm = this.footer.getMargins();
29021             height += (this.footer.getHeight()+fm.top+fm.bottom);
29022         }
29023         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29024         height += this.centerBg.getPadding("tb");
29025         return height;
29026     },
29027
29028     // private
29029     syncBodyHeight : function(){
29030         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29031         var height = this.size.height - this.getHeaderFooterHeight(false);
29032         bd.setHeight(height-bd.getMargins("tb"));
29033         var hh = this.header.getHeight();
29034         var h = this.size.height-hh;
29035         cb.setHeight(h);
29036         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29037         bw.setHeight(h-cb.getPadding("tb"));
29038         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29039         bd.setWidth(bw.getWidth(true));
29040         if(this.tabs){
29041             this.tabs.syncHeight();
29042             if(Roo.isIE){
29043                 this.tabs.el.repaint();
29044             }
29045         }
29046     },
29047
29048     /**
29049      * Restores the previous state of the dialog if Roo.state is configured.
29050      * @return {Roo.BasicDialog} this
29051      */
29052     restoreState : function(){
29053         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29054         if(box && box.width){
29055             this.xy = [box.x, box.y];
29056             this.resizeTo(box.width, box.height);
29057         }
29058         return this;
29059     },
29060
29061     // private
29062     beforeShow : function(){
29063         this.expand();
29064         if(this.fixedcenter){
29065             this.xy = this.el.getCenterXY(true);
29066         }
29067         if(this.modal){
29068             Roo.get(document.body).addClass("x-body-masked");
29069             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29070             this.mask.show();
29071         }
29072         this.constrainXY();
29073     },
29074
29075     // private
29076     animShow : function(){
29077         var b = Roo.get(this.animateTarget).getBox();
29078         this.proxy.setSize(b.width, b.height);
29079         this.proxy.setLocation(b.x, b.y);
29080         this.proxy.show();
29081         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29082                     true, .35, this.showEl.createDelegate(this));
29083     },
29084
29085     /**
29086      * Shows the dialog.
29087      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29088      * @return {Roo.BasicDialog} this
29089      */
29090     show : function(animateTarget){
29091         if (this.fireEvent("beforeshow", this) === false){
29092             return;
29093         }
29094         if(this.syncHeightBeforeShow){
29095             this.syncBodyHeight();
29096         }else if(this.firstShow){
29097             this.firstShow = false;
29098             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29099         }
29100         this.animateTarget = animateTarget || this.animateTarget;
29101         if(!this.el.isVisible()){
29102             this.beforeShow();
29103             if(this.animateTarget && Roo.get(this.animateTarget)){
29104                 this.animShow();
29105             }else{
29106                 this.showEl();
29107             }
29108         }
29109         return this;
29110     },
29111
29112     // private
29113     showEl : function(){
29114         this.proxy.hide();
29115         this.el.setXY(this.xy);
29116         this.el.show();
29117         this.adjustAssets(true);
29118         this.toFront();
29119         this.focus();
29120         // IE peekaboo bug - fix found by Dave Fenwick
29121         if(Roo.isIE){
29122             this.el.repaint();
29123         }
29124         this.fireEvent("show", this);
29125     },
29126
29127     /**
29128      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29129      * dialog itself will receive focus.
29130      */
29131     focus : function(){
29132         if(this.defaultButton){
29133             this.defaultButton.focus();
29134         }else{
29135             this.focusEl.focus();
29136         }
29137     },
29138
29139     // private
29140     constrainXY : function(){
29141         if(this.constraintoviewport !== false){
29142             if(!this.viewSize){
29143                 if(this.container){
29144                     var s = this.container.getSize();
29145                     this.viewSize = [s.width, s.height];
29146                 }else{
29147                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29148                 }
29149             }
29150             var s = Roo.get(this.container||document).getScroll();
29151
29152             var x = this.xy[0], y = this.xy[1];
29153             var w = this.size.width, h = this.size.height;
29154             var vw = this.viewSize[0], vh = this.viewSize[1];
29155             // only move it if it needs it
29156             var moved = false;
29157             // first validate right/bottom
29158             if(x + w > vw+s.left){
29159                 x = vw - w;
29160                 moved = true;
29161             }
29162             if(y + h > vh+s.top){
29163                 y = vh - h;
29164                 moved = true;
29165             }
29166             // then make sure top/left isn't negative
29167             if(x < s.left){
29168                 x = s.left;
29169                 moved = true;
29170             }
29171             if(y < s.top){
29172                 y = s.top;
29173                 moved = true;
29174             }
29175             if(moved){
29176                 // cache xy
29177                 this.xy = [x, y];
29178                 if(this.isVisible()){
29179                     this.el.setLocation(x, y);
29180                     this.adjustAssets();
29181                 }
29182             }
29183         }
29184     },
29185
29186     // private
29187     onDrag : function(){
29188         if(!this.proxyDrag){
29189             this.xy = this.el.getXY();
29190             this.adjustAssets();
29191         }
29192     },
29193
29194     // private
29195     adjustAssets : function(doShow){
29196         var x = this.xy[0], y = this.xy[1];
29197         var w = this.size.width, h = this.size.height;
29198         if(doShow === true){
29199             if(this.shadow){
29200                 this.shadow.show(this.el);
29201             }
29202             if(this.shim){
29203                 this.shim.show();
29204             }
29205         }
29206         if(this.shadow && this.shadow.isVisible()){
29207             this.shadow.show(this.el);
29208         }
29209         if(this.shim && this.shim.isVisible()){
29210             this.shim.setBounds(x, y, w, h);
29211         }
29212     },
29213
29214     // private
29215     adjustViewport : function(w, h){
29216         if(!w || !h){
29217             w = Roo.lib.Dom.getViewWidth();
29218             h = Roo.lib.Dom.getViewHeight();
29219         }
29220         // cache the size
29221         this.viewSize = [w, h];
29222         if(this.modal && this.mask.isVisible()){
29223             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29224             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29225         }
29226         if(this.isVisible()){
29227             this.constrainXY();
29228         }
29229     },
29230
29231     /**
29232      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29233      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29234      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29235      */
29236     destroy : function(removeEl){
29237         if(this.isVisible()){
29238             this.animateTarget = null;
29239             this.hide();
29240         }
29241         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29242         if(this.tabs){
29243             this.tabs.destroy(removeEl);
29244         }
29245         Roo.destroy(
29246              this.shim,
29247              this.proxy,
29248              this.resizer,
29249              this.close,
29250              this.mask
29251         );
29252         if(this.dd){
29253             this.dd.unreg();
29254         }
29255         if(this.buttons){
29256            for(var i = 0, len = this.buttons.length; i < len; i++){
29257                this.buttons[i].destroy();
29258            }
29259         }
29260         this.el.removeAllListeners();
29261         if(removeEl === true){
29262             this.el.update("");
29263             this.el.remove();
29264         }
29265         Roo.DialogManager.unregister(this);
29266     },
29267
29268     // private
29269     startMove : function(){
29270         if(this.proxyDrag){
29271             this.proxy.show();
29272         }
29273         if(this.constraintoviewport !== false){
29274             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29275         }
29276     },
29277
29278     // private
29279     endMove : function(){
29280         if(!this.proxyDrag){
29281             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29282         }else{
29283             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29284             this.proxy.hide();
29285         }
29286         this.refreshSize();
29287         this.adjustAssets();
29288         this.focus();
29289         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29290     },
29291
29292     /**
29293      * Brings this dialog to the front of any other visible dialogs
29294      * @return {Roo.BasicDialog} this
29295      */
29296     toFront : function(){
29297         Roo.DialogManager.bringToFront(this);
29298         return this;
29299     },
29300
29301     /**
29302      * Sends this dialog to the back (under) of any other visible dialogs
29303      * @return {Roo.BasicDialog} this
29304      */
29305     toBack : function(){
29306         Roo.DialogManager.sendToBack(this);
29307         return this;
29308     },
29309
29310     /**
29311      * Centers this dialog in the viewport
29312      * @return {Roo.BasicDialog} this
29313      */
29314     center : function(){
29315         var xy = this.el.getCenterXY(true);
29316         this.moveTo(xy[0], xy[1]);
29317         return this;
29318     },
29319
29320     /**
29321      * Moves the dialog's top-left corner to the specified point
29322      * @param {Number} x
29323      * @param {Number} y
29324      * @return {Roo.BasicDialog} this
29325      */
29326     moveTo : function(x, y){
29327         this.xy = [x,y];
29328         if(this.isVisible()){
29329             this.el.setXY(this.xy);
29330             this.adjustAssets();
29331         }
29332         return this;
29333     },
29334
29335     /**
29336      * Aligns the dialog to the specified element
29337      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29338      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29339      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29340      * @return {Roo.BasicDialog} this
29341      */
29342     alignTo : function(element, position, offsets){
29343         this.xy = this.el.getAlignToXY(element, position, offsets);
29344         if(this.isVisible()){
29345             this.el.setXY(this.xy);
29346             this.adjustAssets();
29347         }
29348         return this;
29349     },
29350
29351     /**
29352      * Anchors an element to another element and realigns it when the window is resized.
29353      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29354      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29355      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29356      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29357      * is a number, it is used as the buffer delay (defaults to 50ms).
29358      * @return {Roo.BasicDialog} this
29359      */
29360     anchorTo : function(el, alignment, offsets, monitorScroll){
29361         var action = function(){
29362             this.alignTo(el, alignment, offsets);
29363         };
29364         Roo.EventManager.onWindowResize(action, this);
29365         var tm = typeof monitorScroll;
29366         if(tm != 'undefined'){
29367             Roo.EventManager.on(window, 'scroll', action, this,
29368                 {buffer: tm == 'number' ? monitorScroll : 50});
29369         }
29370         action.call(this);
29371         return this;
29372     },
29373
29374     /**
29375      * Returns true if the dialog is visible
29376      * @return {Boolean}
29377      */
29378     isVisible : function(){
29379         return this.el.isVisible();
29380     },
29381
29382     // private
29383     animHide : function(callback){
29384         var b = Roo.get(this.animateTarget).getBox();
29385         this.proxy.show();
29386         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29387         this.el.hide();
29388         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29389                     this.hideEl.createDelegate(this, [callback]));
29390     },
29391
29392     /**
29393      * Hides the dialog.
29394      * @param {Function} callback (optional) Function to call when the dialog is hidden
29395      * @return {Roo.BasicDialog} this
29396      */
29397     hide : function(callback){
29398         if (this.fireEvent("beforehide", this) === false){
29399             return;
29400         }
29401         if(this.shadow){
29402             this.shadow.hide();
29403         }
29404         if(this.shim) {
29405           this.shim.hide();
29406         }
29407         // sometimes animateTarget seems to get set.. causing problems...
29408         // this just double checks..
29409         if(this.animateTarget && Roo.get(this.animateTarget)) {
29410            this.animHide(callback);
29411         }else{
29412             this.el.hide();
29413             this.hideEl(callback);
29414         }
29415         return this;
29416     },
29417
29418     // private
29419     hideEl : function(callback){
29420         this.proxy.hide();
29421         if(this.modal){
29422             this.mask.hide();
29423             Roo.get(document.body).removeClass("x-body-masked");
29424         }
29425         this.fireEvent("hide", this);
29426         if(typeof callback == "function"){
29427             callback();
29428         }
29429     },
29430
29431     // private
29432     hideAction : function(){
29433         this.setLeft("-10000px");
29434         this.setTop("-10000px");
29435         this.setStyle("visibility", "hidden");
29436     },
29437
29438     // private
29439     refreshSize : function(){
29440         this.size = this.el.getSize();
29441         this.xy = this.el.getXY();
29442         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29443     },
29444
29445     // private
29446     // z-index is managed by the DialogManager and may be overwritten at any time
29447     setZIndex : function(index){
29448         if(this.modal){
29449             this.mask.setStyle("z-index", index);
29450         }
29451         if(this.shim){
29452             this.shim.setStyle("z-index", ++index);
29453         }
29454         if(this.shadow){
29455             this.shadow.setZIndex(++index);
29456         }
29457         this.el.setStyle("z-index", ++index);
29458         if(this.proxy){
29459             this.proxy.setStyle("z-index", ++index);
29460         }
29461         if(this.resizer){
29462             this.resizer.proxy.setStyle("z-index", ++index);
29463         }
29464
29465         this.lastZIndex = index;
29466     },
29467
29468     /**
29469      * Returns the element for this dialog
29470      * @return {Roo.Element} The underlying dialog Element
29471      */
29472     getEl : function(){
29473         return this.el;
29474     }
29475 });
29476
29477 /**
29478  * @class Roo.DialogManager
29479  * Provides global access to BasicDialogs that have been created and
29480  * support for z-indexing (layering) multiple open dialogs.
29481  */
29482 Roo.DialogManager = function(){
29483     var list = {};
29484     var accessList = [];
29485     var front = null;
29486
29487     // private
29488     var sortDialogs = function(d1, d2){
29489         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29490     };
29491
29492     // private
29493     var orderDialogs = function(){
29494         accessList.sort(sortDialogs);
29495         var seed = Roo.DialogManager.zseed;
29496         for(var i = 0, len = accessList.length; i < len; i++){
29497             var dlg = accessList[i];
29498             if(dlg){
29499                 dlg.setZIndex(seed + (i*10));
29500             }
29501         }
29502     };
29503
29504     return {
29505         /**
29506          * The starting z-index for BasicDialogs (defaults to 9000)
29507          * @type Number The z-index value
29508          */
29509         zseed : 9000,
29510
29511         // private
29512         register : function(dlg){
29513             list[dlg.id] = dlg;
29514             accessList.push(dlg);
29515         },
29516
29517         // private
29518         unregister : function(dlg){
29519             delete list[dlg.id];
29520             var i=0;
29521             var len=0;
29522             if(!accessList.indexOf){
29523                 for(  i = 0, len = accessList.length; i < len; i++){
29524                     if(accessList[i] == dlg){
29525                         accessList.splice(i, 1);
29526                         return;
29527                     }
29528                 }
29529             }else{
29530                  i = accessList.indexOf(dlg);
29531                 if(i != -1){
29532                     accessList.splice(i, 1);
29533                 }
29534             }
29535         },
29536
29537         /**
29538          * Gets a registered dialog by id
29539          * @param {String/Object} id The id of the dialog or a dialog
29540          * @return {Roo.BasicDialog} this
29541          */
29542         get : function(id){
29543             return typeof id == "object" ? id : list[id];
29544         },
29545
29546         /**
29547          * Brings the specified dialog to the front
29548          * @param {String/Object} dlg The id of the dialog or a dialog
29549          * @return {Roo.BasicDialog} this
29550          */
29551         bringToFront : function(dlg){
29552             dlg = this.get(dlg);
29553             if(dlg != front){
29554                 front = dlg;
29555                 dlg._lastAccess = new Date().getTime();
29556                 orderDialogs();
29557             }
29558             return dlg;
29559         },
29560
29561         /**
29562          * Sends the specified dialog to the back
29563          * @param {String/Object} dlg The id of the dialog or a dialog
29564          * @return {Roo.BasicDialog} this
29565          */
29566         sendToBack : function(dlg){
29567             dlg = this.get(dlg);
29568             dlg._lastAccess = -(new Date().getTime());
29569             orderDialogs();
29570             return dlg;
29571         },
29572
29573         /**
29574          * Hides all dialogs
29575          */
29576         hideAll : function(){
29577             for(var id in list){
29578                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29579                     list[id].hide();
29580                 }
29581             }
29582         }
29583     };
29584 }();
29585
29586 /**
29587  * @class Roo.LayoutDialog
29588  * @extends Roo.BasicDialog
29589  * Dialog which provides adjustments for working with a layout in a Dialog.
29590  * Add your necessary layout config options to the dialog's config.<br>
29591  * Example usage (including a nested layout):
29592  * <pre><code>
29593 if(!dialog){
29594     dialog = new Roo.LayoutDialog("download-dlg", {
29595         modal: true,
29596         width:600,
29597         height:450,
29598         shadow:true,
29599         minWidth:500,
29600         minHeight:350,
29601         autoTabs:true,
29602         proxyDrag:true,
29603         // layout config merges with the dialog config
29604         center:{
29605             tabPosition: "top",
29606             alwaysShowTabs: true
29607         }
29608     });
29609     dialog.addKeyListener(27, dialog.hide, dialog);
29610     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29611     dialog.addButton("Build It!", this.getDownload, this);
29612
29613     // we can even add nested layouts
29614     var innerLayout = new Roo.BorderLayout("dl-inner", {
29615         east: {
29616             initialSize: 200,
29617             autoScroll:true,
29618             split:true
29619         },
29620         center: {
29621             autoScroll:true
29622         }
29623     });
29624     innerLayout.beginUpdate();
29625     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29626     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29627     innerLayout.endUpdate(true);
29628
29629     var layout = dialog.getLayout();
29630     layout.beginUpdate();
29631     layout.add("center", new Roo.ContentPanel("standard-panel",
29632                         {title: "Download the Source", fitToFrame:true}));
29633     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29634                {title: "Build your own roo.js"}));
29635     layout.getRegion("center").showPanel(sp);
29636     layout.endUpdate();
29637 }
29638 </code></pre>
29639     * @constructor
29640     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29641     * @param {Object} config configuration options
29642   */
29643 Roo.LayoutDialog = function(el, cfg){
29644     
29645     var config=  cfg;
29646     if (typeof(cfg) == 'undefined') {
29647         config = Roo.apply({}, el);
29648         // not sure why we use documentElement here.. - it should always be body.
29649         // IE7 borks horribly if we use documentElement.
29650         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
29651         //config.autoCreate = true;
29652     }
29653     
29654     
29655     config.autoTabs = false;
29656     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29657     this.body.setStyle({overflow:"hidden", position:"relative"});
29658     this.layout = new Roo.BorderLayout(this.body.dom, config);
29659     this.layout.monitorWindowResize = false;
29660     this.el.addClass("x-dlg-auto-layout");
29661     // fix case when center region overwrites center function
29662     this.center = Roo.BasicDialog.prototype.center;
29663     this.on("show", this.layout.layout, this.layout, true);
29664     if (config.items) {
29665         var xitems = config.items;
29666         delete config.items;
29667         Roo.each(xitems, this.addxtype, this);
29668     }
29669     
29670     
29671 };
29672 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29673     /**
29674      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29675      * @deprecated
29676      */
29677     endUpdate : function(){
29678         this.layout.endUpdate();
29679     },
29680
29681     /**
29682      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29683      *  @deprecated
29684      */
29685     beginUpdate : function(){
29686         this.layout.beginUpdate();
29687     },
29688
29689     /**
29690      * Get the BorderLayout for this dialog
29691      * @return {Roo.BorderLayout}
29692      */
29693     getLayout : function(){
29694         return this.layout;
29695     },
29696
29697     showEl : function(){
29698         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29699         if(Roo.isIE7){
29700             this.layout.layout();
29701         }
29702     },
29703
29704     // private
29705     // Use the syncHeightBeforeShow config option to control this automatically
29706     syncBodyHeight : function(){
29707         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29708         if(this.layout){this.layout.layout();}
29709     },
29710     
29711       /**
29712      * Add an xtype element (actually adds to the layout.)
29713      * @return {Object} xdata xtype object data.
29714      */
29715     
29716     addxtype : function(c) {
29717         return this.layout.addxtype(c);
29718     }
29719 });/*
29720  * Based on:
29721  * Ext JS Library 1.1.1
29722  * Copyright(c) 2006-2007, Ext JS, LLC.
29723  *
29724  * Originally Released Under LGPL - original licence link has changed is not relivant.
29725  *
29726  * Fork - LGPL
29727  * <script type="text/javascript">
29728  */
29729  
29730 /**
29731  * @class Roo.MessageBox
29732  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29733  * Example usage:
29734  *<pre><code>
29735 // Basic alert:
29736 Roo.Msg.alert('Status', 'Changes saved successfully.');
29737
29738 // Prompt for user data:
29739 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29740     if (btn == 'ok'){
29741         // process text value...
29742     }
29743 });
29744
29745 // Show a dialog using config options:
29746 Roo.Msg.show({
29747    title:'Save Changes?',
29748    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29749    buttons: Roo.Msg.YESNOCANCEL,
29750    fn: processResult,
29751    animEl: 'elId'
29752 });
29753 </code></pre>
29754  * @singleton
29755  */
29756 Roo.MessageBox = function(){
29757     var dlg, opt, mask, waitTimer;
29758     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29759     var buttons, activeTextEl, bwidth;
29760
29761     // private
29762     var handleButton = function(button){
29763         dlg.hide();
29764         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29765     };
29766
29767     // private
29768     var handleHide = function(){
29769         if(opt && opt.cls){
29770             dlg.el.removeClass(opt.cls);
29771         }
29772         if(waitTimer){
29773             Roo.TaskMgr.stop(waitTimer);
29774             waitTimer = null;
29775         }
29776     };
29777
29778     // private
29779     var updateButtons = function(b){
29780         var width = 0;
29781         if(!b){
29782             buttons["ok"].hide();
29783             buttons["cancel"].hide();
29784             buttons["yes"].hide();
29785             buttons["no"].hide();
29786             dlg.footer.dom.style.display = 'none';
29787             return width;
29788         }
29789         dlg.footer.dom.style.display = '';
29790         for(var k in buttons){
29791             if(typeof buttons[k] != "function"){
29792                 if(b[k]){
29793                     buttons[k].show();
29794                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29795                     width += buttons[k].el.getWidth()+15;
29796                 }else{
29797                     buttons[k].hide();
29798                 }
29799             }
29800         }
29801         return width;
29802     };
29803
29804     // private
29805     var handleEsc = function(d, k, e){
29806         if(opt && opt.closable !== false){
29807             dlg.hide();
29808         }
29809         if(e){
29810             e.stopEvent();
29811         }
29812     };
29813
29814     return {
29815         /**
29816          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29817          * @return {Roo.BasicDialog} The BasicDialog element
29818          */
29819         getDialog : function(){
29820            if(!dlg){
29821                 dlg = new Roo.BasicDialog("x-msg-box", {
29822                     autoCreate : true,
29823                     shadow: true,
29824                     draggable: true,
29825                     resizable:false,
29826                     constraintoviewport:false,
29827                     fixedcenter:true,
29828                     collapsible : false,
29829                     shim:true,
29830                     modal: true,
29831                     width:400, height:100,
29832                     buttonAlign:"center",
29833                     closeClick : function(){
29834                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29835                             handleButton("no");
29836                         }else{
29837                             handleButton("cancel");
29838                         }
29839                     }
29840                 });
29841                 dlg.on("hide", handleHide);
29842                 mask = dlg.mask;
29843                 dlg.addKeyListener(27, handleEsc);
29844                 buttons = {};
29845                 var bt = this.buttonText;
29846                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29847                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29848                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29849                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29850                 bodyEl = dlg.body.createChild({
29851
29852                     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>'
29853                 });
29854                 msgEl = bodyEl.dom.firstChild;
29855                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29856                 textboxEl.enableDisplayMode();
29857                 textboxEl.addKeyListener([10,13], function(){
29858                     if(dlg.isVisible() && opt && opt.buttons){
29859                         if(opt.buttons.ok){
29860                             handleButton("ok");
29861                         }else if(opt.buttons.yes){
29862                             handleButton("yes");
29863                         }
29864                     }
29865                 });
29866                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29867                 textareaEl.enableDisplayMode();
29868                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29869                 progressEl.enableDisplayMode();
29870                 var pf = progressEl.dom.firstChild;
29871                 if (pf) {
29872                     pp = Roo.get(pf.firstChild);
29873                     pp.setHeight(pf.offsetHeight);
29874                 }
29875                 
29876             }
29877             return dlg;
29878         },
29879
29880         /**
29881          * Updates the message box body text
29882          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29883          * the XHTML-compliant non-breaking space character '&amp;#160;')
29884          * @return {Roo.MessageBox} This message box
29885          */
29886         updateText : function(text){
29887             if(!dlg.isVisible() && !opt.width){
29888                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29889             }
29890             msgEl.innerHTML = text || '&#160;';
29891             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29892                         Math.max(opt.minWidth || this.minWidth, bwidth));
29893             if(opt.prompt){
29894                 activeTextEl.setWidth(w);
29895             }
29896             if(dlg.isVisible()){
29897                 dlg.fixedcenter = false;
29898             }
29899             dlg.setContentSize(w, bodyEl.getHeight());
29900             if(dlg.isVisible()){
29901                 dlg.fixedcenter = true;
29902             }
29903             return this;
29904         },
29905
29906         /**
29907          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29908          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29909          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29910          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29911          * @return {Roo.MessageBox} This message box
29912          */
29913         updateProgress : function(value, text){
29914             if(text){
29915                 this.updateText(text);
29916             }
29917             if (pp) { // weird bug on my firefox - for some reason this is not defined
29918                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29919             }
29920             return this;
29921         },        
29922
29923         /**
29924          * Returns true if the message box is currently displayed
29925          * @return {Boolean} True if the message box is visible, else false
29926          */
29927         isVisible : function(){
29928             return dlg && dlg.isVisible();  
29929         },
29930
29931         /**
29932          * Hides the message box if it is displayed
29933          */
29934         hide : function(){
29935             if(this.isVisible()){
29936                 dlg.hide();
29937             }  
29938         },
29939
29940         /**
29941          * Displays a new message box, or reinitializes an existing message box, based on the config options
29942          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29943          * The following config object properties are supported:
29944          * <pre>
29945 Property    Type             Description
29946 ----------  ---------------  ------------------------------------------------------------------------------------
29947 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29948                                    closes (defaults to undefined)
29949 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29950                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29951 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29952                                    progress and wait dialogs will ignore this property and always hide the
29953                                    close button as they can only be closed programmatically.
29954 cls               String           A custom CSS class to apply to the message box element
29955 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29956                                    displayed (defaults to 75)
29957 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29958                                    function will be btn (the name of the button that was clicked, if applicable,
29959                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29960                                    Progress and wait dialogs will ignore this option since they do not respond to
29961                                    user actions and can only be closed programmatically, so any required function
29962                                    should be called by the same code after it closes the dialog.
29963 icon              String           A CSS class that provides a background image to be used as an icon for
29964                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29965 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29966 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29967 modal             Boolean          False to allow user interaction with the page while the message box is
29968                                    displayed (defaults to true)
29969 msg               String           A string that will replace the existing message box body text (defaults
29970                                    to the XHTML-compliant non-breaking space character '&#160;')
29971 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29972 progress          Boolean          True to display a progress bar (defaults to false)
29973 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29974 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29975 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29976 title             String           The title text
29977 value             String           The string value to set into the active textbox element if displayed
29978 wait              Boolean          True to display a progress bar (defaults to false)
29979 width             Number           The width of the dialog in pixels
29980 </pre>
29981          *
29982          * Example usage:
29983          * <pre><code>
29984 Roo.Msg.show({
29985    title: 'Address',
29986    msg: 'Please enter your address:',
29987    width: 300,
29988    buttons: Roo.MessageBox.OKCANCEL,
29989    multiline: true,
29990    fn: saveAddress,
29991    animEl: 'addAddressBtn'
29992 });
29993 </code></pre>
29994          * @param {Object} config Configuration options
29995          * @return {Roo.MessageBox} This message box
29996          */
29997         show : function(options){
29998             if(this.isVisible()){
29999                 this.hide();
30000             }
30001             var d = this.getDialog();
30002             opt = options;
30003             d.setTitle(opt.title || "&#160;");
30004             d.close.setDisplayed(opt.closable !== false);
30005             activeTextEl = textboxEl;
30006             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30007             if(opt.prompt){
30008                 if(opt.multiline){
30009                     textboxEl.hide();
30010                     textareaEl.show();
30011                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30012                         opt.multiline : this.defaultTextHeight);
30013                     activeTextEl = textareaEl;
30014                 }else{
30015                     textboxEl.show();
30016                     textareaEl.hide();
30017                 }
30018             }else{
30019                 textboxEl.hide();
30020                 textareaEl.hide();
30021             }
30022             progressEl.setDisplayed(opt.progress === true);
30023             this.updateProgress(0);
30024             activeTextEl.dom.value = opt.value || "";
30025             if(opt.prompt){
30026                 dlg.setDefaultButton(activeTextEl);
30027             }else{
30028                 var bs = opt.buttons;
30029                 var db = null;
30030                 if(bs && bs.ok){
30031                     db = buttons["ok"];
30032                 }else if(bs && bs.yes){
30033                     db = buttons["yes"];
30034                 }
30035                 dlg.setDefaultButton(db);
30036             }
30037             bwidth = updateButtons(opt.buttons);
30038             this.updateText(opt.msg);
30039             if(opt.cls){
30040                 d.el.addClass(opt.cls);
30041             }
30042             d.proxyDrag = opt.proxyDrag === true;
30043             d.modal = opt.modal !== false;
30044             d.mask = opt.modal !== false ? mask : false;
30045             if(!d.isVisible()){
30046                 // force it to the end of the z-index stack so it gets a cursor in FF
30047                 document.body.appendChild(dlg.el.dom);
30048                 d.animateTarget = null;
30049                 d.show(options.animEl);
30050             }
30051             return this;
30052         },
30053
30054         /**
30055          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30056          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30057          * and closing the message box when the process is complete.
30058          * @param {String} title The title bar text
30059          * @param {String} msg The message box body text
30060          * @return {Roo.MessageBox} This message box
30061          */
30062         progress : function(title, msg){
30063             this.show({
30064                 title : title,
30065                 msg : msg,
30066                 buttons: false,
30067                 progress:true,
30068                 closable:false,
30069                 minWidth: this.minProgressWidth,
30070                 modal : true
30071             });
30072             return this;
30073         },
30074
30075         /**
30076          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30077          * If a callback function is passed it will be called after the user clicks the button, and the
30078          * id of the button that was clicked will be passed as the only parameter to the callback
30079          * (could also be the top-right close button).
30080          * @param {String} title The title bar text
30081          * @param {String} msg The message box body text
30082          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30083          * @param {Object} scope (optional) The scope of the callback function
30084          * @return {Roo.MessageBox} This message box
30085          */
30086         alert : function(title, msg, fn, scope){
30087             this.show({
30088                 title : title,
30089                 msg : msg,
30090                 buttons: this.OK,
30091                 fn: fn,
30092                 scope : scope,
30093                 modal : true
30094             });
30095             return this;
30096         },
30097
30098         /**
30099          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30100          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30101          * You are responsible for closing the message box when the process is complete.
30102          * @param {String} msg The message box body text
30103          * @param {String} title (optional) The title bar text
30104          * @return {Roo.MessageBox} This message box
30105          */
30106         wait : function(msg, title){
30107             this.show({
30108                 title : title,
30109                 msg : msg,
30110                 buttons: false,
30111                 closable:false,
30112                 progress:true,
30113                 modal:true,
30114                 width:300,
30115                 wait:true
30116             });
30117             waitTimer = Roo.TaskMgr.start({
30118                 run: function(i){
30119                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30120                 },
30121                 interval: 1000
30122             });
30123             return this;
30124         },
30125
30126         /**
30127          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30128          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30129          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30130          * @param {String} title The title bar text
30131          * @param {String} msg The message box body text
30132          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30133          * @param {Object} scope (optional) The scope of the callback function
30134          * @return {Roo.MessageBox} This message box
30135          */
30136         confirm : function(title, msg, fn, scope){
30137             this.show({
30138                 title : title,
30139                 msg : msg,
30140                 buttons: this.YESNO,
30141                 fn: fn,
30142                 scope : scope,
30143                 modal : true
30144             });
30145             return this;
30146         },
30147
30148         /**
30149          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30150          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30151          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30152          * (could also be the top-right close button) and the text that was entered will be passed as the two
30153          * parameters to the callback.
30154          * @param {String} title The title bar text
30155          * @param {String} msg The message box body text
30156          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30157          * @param {Object} scope (optional) The scope of the callback function
30158          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30159          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30160          * @return {Roo.MessageBox} This message box
30161          */
30162         prompt : function(title, msg, fn, scope, multiline){
30163             this.show({
30164                 title : title,
30165                 msg : msg,
30166                 buttons: this.OKCANCEL,
30167                 fn: fn,
30168                 minWidth:250,
30169                 scope : scope,
30170                 prompt:true,
30171                 multiline: multiline,
30172                 modal : true
30173             });
30174             return this;
30175         },
30176
30177         /**
30178          * Button config that displays a single OK button
30179          * @type Object
30180          */
30181         OK : {ok:true},
30182         /**
30183          * Button config that displays Yes and No buttons
30184          * @type Object
30185          */
30186         YESNO : {yes:true, no:true},
30187         /**
30188          * Button config that displays OK and Cancel buttons
30189          * @type Object
30190          */
30191         OKCANCEL : {ok:true, cancel:true},
30192         /**
30193          * Button config that displays Yes, No and Cancel buttons
30194          * @type Object
30195          */
30196         YESNOCANCEL : {yes:true, no:true, cancel:true},
30197
30198         /**
30199          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30200          * @type Number
30201          */
30202         defaultTextHeight : 75,
30203         /**
30204          * The maximum width in pixels of the message box (defaults to 600)
30205          * @type Number
30206          */
30207         maxWidth : 600,
30208         /**
30209          * The minimum width in pixels of the message box (defaults to 100)
30210          * @type Number
30211          */
30212         minWidth : 100,
30213         /**
30214          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30215          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30216          * @type Number
30217          */
30218         minProgressWidth : 250,
30219         /**
30220          * An object containing the default button text strings that can be overriden for localized language support.
30221          * Supported properties are: ok, cancel, yes and no.
30222          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30223          * @type Object
30224          */
30225         buttonText : {
30226             ok : "OK",
30227             cancel : "Cancel",
30228             yes : "Yes",
30229             no : "No"
30230         }
30231     };
30232 }();
30233
30234 /**
30235  * Shorthand for {@link Roo.MessageBox}
30236  */
30237 Roo.Msg = Roo.MessageBox;/*
30238  * Based on:
30239  * Ext JS Library 1.1.1
30240  * Copyright(c) 2006-2007, Ext JS, LLC.
30241  *
30242  * Originally Released Under LGPL - original licence link has changed is not relivant.
30243  *
30244  * Fork - LGPL
30245  * <script type="text/javascript">
30246  */
30247 /**
30248  * @class Roo.QuickTips
30249  * Provides attractive and customizable tooltips for any element.
30250  * @singleton
30251  */
30252 Roo.QuickTips = function(){
30253     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30254     var ce, bd, xy, dd;
30255     var visible = false, disabled = true, inited = false;
30256     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30257     
30258     var onOver = function(e){
30259         if(disabled){
30260             return;
30261         }
30262         var t = e.getTarget();
30263         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30264             return;
30265         }
30266         if(ce && t == ce.el){
30267             clearTimeout(hideProc);
30268             return;
30269         }
30270         if(t && tagEls[t.id]){
30271             tagEls[t.id].el = t;
30272             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30273             return;
30274         }
30275         var ttp, et = Roo.fly(t);
30276         var ns = cfg.namespace;
30277         if(tm.interceptTitles && t.title){
30278             ttp = t.title;
30279             t.qtip = ttp;
30280             t.removeAttribute("title");
30281             e.preventDefault();
30282         }else{
30283             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30284         }
30285         if(ttp){
30286             showProc = show.defer(tm.showDelay, tm, [{
30287                 el: t, 
30288                 text: ttp, 
30289                 width: et.getAttributeNS(ns, cfg.width),
30290                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30291                 title: et.getAttributeNS(ns, cfg.title),
30292                     cls: et.getAttributeNS(ns, cfg.cls)
30293             }]);
30294         }
30295     };
30296     
30297     var onOut = function(e){
30298         clearTimeout(showProc);
30299         var t = e.getTarget();
30300         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30301             hideProc = setTimeout(hide, tm.hideDelay);
30302         }
30303     };
30304     
30305     var onMove = function(e){
30306         if(disabled){
30307             return;
30308         }
30309         xy = e.getXY();
30310         xy[1] += 18;
30311         if(tm.trackMouse && ce){
30312             el.setXY(xy);
30313         }
30314     };
30315     
30316     var onDown = function(e){
30317         clearTimeout(showProc);
30318         clearTimeout(hideProc);
30319         if(!e.within(el)){
30320             if(tm.hideOnClick){
30321                 hide();
30322                 tm.disable();
30323                 tm.enable.defer(100, tm);
30324             }
30325         }
30326     };
30327     
30328     var getPad = function(){
30329         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30330     };
30331
30332     var show = function(o){
30333         if(disabled){
30334             return;
30335         }
30336         clearTimeout(dismissProc);
30337         ce = o;
30338         if(removeCls){ // in case manually hidden
30339             el.removeClass(removeCls);
30340             removeCls = null;
30341         }
30342         if(ce.cls){
30343             el.addClass(ce.cls);
30344             removeCls = ce.cls;
30345         }
30346         if(ce.title){
30347             tipTitle.update(ce.title);
30348             tipTitle.show();
30349         }else{
30350             tipTitle.update('');
30351             tipTitle.hide();
30352         }
30353         el.dom.style.width  = tm.maxWidth+'px';
30354         //tipBody.dom.style.width = '';
30355         tipBodyText.update(o.text);
30356         var p = getPad(), w = ce.width;
30357         if(!w){
30358             var td = tipBodyText.dom;
30359             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30360             if(aw > tm.maxWidth){
30361                 w = tm.maxWidth;
30362             }else if(aw < tm.minWidth){
30363                 w = tm.minWidth;
30364             }else{
30365                 w = aw;
30366             }
30367         }
30368         //tipBody.setWidth(w);
30369         el.setWidth(parseInt(w, 10) + p);
30370         if(ce.autoHide === false){
30371             close.setDisplayed(true);
30372             if(dd){
30373                 dd.unlock();
30374             }
30375         }else{
30376             close.setDisplayed(false);
30377             if(dd){
30378                 dd.lock();
30379             }
30380         }
30381         if(xy){
30382             el.avoidY = xy[1]-18;
30383             el.setXY(xy);
30384         }
30385         if(tm.animate){
30386             el.setOpacity(.1);
30387             el.setStyle("visibility", "visible");
30388             el.fadeIn({callback: afterShow});
30389         }else{
30390             afterShow();
30391         }
30392     };
30393     
30394     var afterShow = function(){
30395         if(ce){
30396             el.show();
30397             esc.enable();
30398             if(tm.autoDismiss && ce.autoHide !== false){
30399                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30400             }
30401         }
30402     };
30403     
30404     var hide = function(noanim){
30405         clearTimeout(dismissProc);
30406         clearTimeout(hideProc);
30407         ce = null;
30408         if(el.isVisible()){
30409             esc.disable();
30410             if(noanim !== true && tm.animate){
30411                 el.fadeOut({callback: afterHide});
30412             }else{
30413                 afterHide();
30414             } 
30415         }
30416     };
30417     
30418     var afterHide = function(){
30419         el.hide();
30420         if(removeCls){
30421             el.removeClass(removeCls);
30422             removeCls = null;
30423         }
30424     };
30425     
30426     return {
30427         /**
30428         * @cfg {Number} minWidth
30429         * The minimum width of the quick tip (defaults to 40)
30430         */
30431        minWidth : 40,
30432         /**
30433         * @cfg {Number} maxWidth
30434         * The maximum width of the quick tip (defaults to 300)
30435         */
30436        maxWidth : 300,
30437         /**
30438         * @cfg {Boolean} interceptTitles
30439         * True to automatically use the element's DOM title value if available (defaults to false)
30440         */
30441        interceptTitles : false,
30442         /**
30443         * @cfg {Boolean} trackMouse
30444         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30445         */
30446        trackMouse : false,
30447         /**
30448         * @cfg {Boolean} hideOnClick
30449         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30450         */
30451        hideOnClick : true,
30452         /**
30453         * @cfg {Number} showDelay
30454         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30455         */
30456        showDelay : 500,
30457         /**
30458         * @cfg {Number} hideDelay
30459         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30460         */
30461        hideDelay : 200,
30462         /**
30463         * @cfg {Boolean} autoHide
30464         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30465         * Used in conjunction with hideDelay.
30466         */
30467        autoHide : true,
30468         /**
30469         * @cfg {Boolean}
30470         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30471         * (defaults to true).  Used in conjunction with autoDismissDelay.
30472         */
30473        autoDismiss : true,
30474         /**
30475         * @cfg {Number}
30476         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30477         */
30478        autoDismissDelay : 5000,
30479        /**
30480         * @cfg {Boolean} animate
30481         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30482         */
30483        animate : false,
30484
30485        /**
30486         * @cfg {String} title
30487         * Title text to display (defaults to '').  This can be any valid HTML markup.
30488         */
30489         title: '',
30490        /**
30491         * @cfg {String} text
30492         * Body text to display (defaults to '').  This can be any valid HTML markup.
30493         */
30494         text : '',
30495        /**
30496         * @cfg {String} cls
30497         * A CSS class to apply to the base quick tip element (defaults to '').
30498         */
30499         cls : '',
30500        /**
30501         * @cfg {Number} width
30502         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30503         * minWidth or maxWidth.
30504         */
30505         width : null,
30506
30507     /**
30508      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30509      * or display QuickTips in a page.
30510      */
30511        init : function(){
30512           tm = Roo.QuickTips;
30513           cfg = tm.tagConfig;
30514           if(!inited){
30515               if(!Roo.isReady){ // allow calling of init() before onReady
30516                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30517                   return;
30518               }
30519               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30520               el.fxDefaults = {stopFx: true};
30521               // maximum custom styling
30522               //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>');
30523               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>');              
30524               tipTitle = el.child('h3');
30525               tipTitle.enableDisplayMode("block");
30526               tipBody = el.child('div.x-tip-bd');
30527               tipBodyText = el.child('div.x-tip-bd-inner');
30528               //bdLeft = el.child('div.x-tip-bd-left');
30529               //bdRight = el.child('div.x-tip-bd-right');
30530               close = el.child('div.x-tip-close');
30531               close.enableDisplayMode("block");
30532               close.on("click", hide);
30533               var d = Roo.get(document);
30534               d.on("mousedown", onDown);
30535               d.on("mouseover", onOver);
30536               d.on("mouseout", onOut);
30537               d.on("mousemove", onMove);
30538               esc = d.addKeyListener(27, hide);
30539               esc.disable();
30540               if(Roo.dd.DD){
30541                   dd = el.initDD("default", null, {
30542                       onDrag : function(){
30543                           el.sync();  
30544                       }
30545                   });
30546                   dd.setHandleElId(tipTitle.id);
30547                   dd.lock();
30548               }
30549               inited = true;
30550           }
30551           this.enable(); 
30552        },
30553
30554     /**
30555      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30556      * are supported:
30557      * <pre>
30558 Property    Type                   Description
30559 ----------  ---------------------  ------------------------------------------------------------------------
30560 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30561      * </ul>
30562      * @param {Object} config The config object
30563      */
30564        register : function(config){
30565            var cs = config instanceof Array ? config : arguments;
30566            for(var i = 0, len = cs.length; i < len; i++) {
30567                var c = cs[i];
30568                var target = c.target;
30569                if(target){
30570                    if(target instanceof Array){
30571                        for(var j = 0, jlen = target.length; j < jlen; j++){
30572                            tagEls[target[j]] = c;
30573                        }
30574                    }else{
30575                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30576                    }
30577                }
30578            }
30579        },
30580
30581     /**
30582      * Removes this quick tip from its element and destroys it.
30583      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30584      */
30585        unregister : function(el){
30586            delete tagEls[Roo.id(el)];
30587        },
30588
30589     /**
30590      * Enable this quick tip.
30591      */
30592        enable : function(){
30593            if(inited && disabled){
30594                locks.pop();
30595                if(locks.length < 1){
30596                    disabled = false;
30597                }
30598            }
30599        },
30600
30601     /**
30602      * Disable this quick tip.
30603      */
30604        disable : function(){
30605           disabled = true;
30606           clearTimeout(showProc);
30607           clearTimeout(hideProc);
30608           clearTimeout(dismissProc);
30609           if(ce){
30610               hide(true);
30611           }
30612           locks.push(1);
30613        },
30614
30615     /**
30616      * Returns true if the quick tip is enabled, else false.
30617      */
30618        isEnabled : function(){
30619             return !disabled;
30620        },
30621
30622         // private
30623        tagConfig : {
30624            namespace : "ext",
30625            attribute : "qtip",
30626            width : "width",
30627            target : "target",
30628            title : "qtitle",
30629            hide : "hide",
30630            cls : "qclass"
30631        }
30632    };
30633 }();
30634
30635 // backwards compat
30636 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30637  * Based on:
30638  * Ext JS Library 1.1.1
30639  * Copyright(c) 2006-2007, Ext JS, LLC.
30640  *
30641  * Originally Released Under LGPL - original licence link has changed is not relivant.
30642  *
30643  * Fork - LGPL
30644  * <script type="text/javascript">
30645  */
30646  
30647
30648 /**
30649  * @class Roo.tree.TreePanel
30650  * @extends Roo.data.Tree
30651
30652  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30653  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30654  * @cfg {Boolean} enableDD true to enable drag and drop
30655  * @cfg {Boolean} enableDrag true to enable just drag
30656  * @cfg {Boolean} enableDrop true to enable just drop
30657  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30658  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30659  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30660  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30661  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30662  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30663  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30664  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30665  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30666  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30667  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30668  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30669  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30670  * @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>
30671  * @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>
30672  * 
30673  * @constructor
30674  * @param {String/HTMLElement/Element} el The container element
30675  * @param {Object} config
30676  */
30677 Roo.tree.TreePanel = function(el, config){
30678     var root = false;
30679     var loader = false;
30680     if (config.root) {
30681         root = config.root;
30682         delete config.root;
30683     }
30684     if (config.loader) {
30685         loader = config.loader;
30686         delete config.loader;
30687     }
30688     
30689     Roo.apply(this, config);
30690     Roo.tree.TreePanel.superclass.constructor.call(this);
30691     this.el = Roo.get(el);
30692     this.el.addClass('x-tree');
30693     //console.log(root);
30694     if (root) {
30695         this.setRootNode( Roo.factory(root, Roo.tree));
30696     }
30697     if (loader) {
30698         this.loader = Roo.factory(loader, Roo.tree);
30699     }
30700    /**
30701     * Read-only. The id of the container element becomes this TreePanel's id.
30702     */
30703    this.id = this.el.id;
30704    this.addEvents({
30705         /**
30706         * @event beforeload
30707         * Fires before a node is loaded, return false to cancel
30708         * @param {Node} node The node being loaded
30709         */
30710         "beforeload" : true,
30711         /**
30712         * @event load
30713         * Fires when a node is loaded
30714         * @param {Node} node The node that was loaded
30715         */
30716         "load" : true,
30717         /**
30718         * @event textchange
30719         * Fires when the text for a node is changed
30720         * @param {Node} node The node
30721         * @param {String} text The new text
30722         * @param {String} oldText The old text
30723         */
30724         "textchange" : true,
30725         /**
30726         * @event beforeexpand
30727         * Fires before a node is expanded, return false to cancel.
30728         * @param {Node} node The node
30729         * @param {Boolean} deep
30730         * @param {Boolean} anim
30731         */
30732         "beforeexpand" : true,
30733         /**
30734         * @event beforecollapse
30735         * Fires before a node is collapsed, return false to cancel.
30736         * @param {Node} node The node
30737         * @param {Boolean} deep
30738         * @param {Boolean} anim
30739         */
30740         "beforecollapse" : true,
30741         /**
30742         * @event expand
30743         * Fires when a node is expanded
30744         * @param {Node} node The node
30745         */
30746         "expand" : true,
30747         /**
30748         * @event disabledchange
30749         * Fires when the disabled status of a node changes
30750         * @param {Node} node The node
30751         * @param {Boolean} disabled
30752         */
30753         "disabledchange" : true,
30754         /**
30755         * @event collapse
30756         * Fires when a node is collapsed
30757         * @param {Node} node The node
30758         */
30759         "collapse" : true,
30760         /**
30761         * @event beforeclick
30762         * Fires before click processing on a node. Return false to cancel the default action.
30763         * @param {Node} node The node
30764         * @param {Roo.EventObject} e The event object
30765         */
30766         "beforeclick":true,
30767         /**
30768         * @event checkchange
30769         * Fires when a node with a checkbox's checked property changes
30770         * @param {Node} this This node
30771         * @param {Boolean} checked
30772         */
30773         "checkchange":true,
30774         /**
30775         * @event click
30776         * Fires when a node is clicked
30777         * @param {Node} node The node
30778         * @param {Roo.EventObject} e The event object
30779         */
30780         "click":true,
30781         /**
30782         * @event dblclick
30783         * Fires when a node is double clicked
30784         * @param {Node} node The node
30785         * @param {Roo.EventObject} e The event object
30786         */
30787         "dblclick":true,
30788         /**
30789         * @event contextmenu
30790         * Fires when a node is right clicked
30791         * @param {Node} node The node
30792         * @param {Roo.EventObject} e The event object
30793         */
30794         "contextmenu":true,
30795         /**
30796         * @event beforechildrenrendered
30797         * Fires right before the child nodes for a node are rendered
30798         * @param {Node} node The node
30799         */
30800         "beforechildrenrendered":true,
30801        /**
30802              * @event startdrag
30803              * Fires when a node starts being dragged
30804              * @param {Roo.tree.TreePanel} this
30805              * @param {Roo.tree.TreeNode} node
30806              * @param {event} e The raw browser event
30807              */ 
30808             "startdrag" : true,
30809             /**
30810              * @event enddrag
30811              * Fires when a drag operation is complete
30812              * @param {Roo.tree.TreePanel} this
30813              * @param {Roo.tree.TreeNode} node
30814              * @param {event} e The raw browser event
30815              */
30816             "enddrag" : true,
30817             /**
30818              * @event dragdrop
30819              * Fires when a dragged node is dropped on a valid DD target
30820              * @param {Roo.tree.TreePanel} this
30821              * @param {Roo.tree.TreeNode} node
30822              * @param {DD} dd The dd it was dropped on
30823              * @param {event} e The raw browser event
30824              */
30825             "dragdrop" : true,
30826             /**
30827              * @event beforenodedrop
30828              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30829              * passed to handlers has the following properties:<br />
30830              * <ul style="padding:5px;padding-left:16px;">
30831              * <li>tree - The TreePanel</li>
30832              * <li>target - The node being targeted for the drop</li>
30833              * <li>data - The drag data from the drag source</li>
30834              * <li>point - The point of the drop - append, above or below</li>
30835              * <li>source - The drag source</li>
30836              * <li>rawEvent - Raw mouse event</li>
30837              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30838              * to be inserted by setting them on this object.</li>
30839              * <li>cancel - Set this to true to cancel the drop.</li>
30840              * </ul>
30841              * @param {Object} dropEvent
30842              */
30843             "beforenodedrop" : true,
30844             /**
30845              * @event nodedrop
30846              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30847              * passed to handlers has the following properties:<br />
30848              * <ul style="padding:5px;padding-left:16px;">
30849              * <li>tree - The TreePanel</li>
30850              * <li>target - The node being targeted for the drop</li>
30851              * <li>data - The drag data from the drag source</li>
30852              * <li>point - The point of the drop - append, above or below</li>
30853              * <li>source - The drag source</li>
30854              * <li>rawEvent - Raw mouse event</li>
30855              * <li>dropNode - Dropped node(s).</li>
30856              * </ul>
30857              * @param {Object} dropEvent
30858              */
30859             "nodedrop" : true,
30860              /**
30861              * @event nodedragover
30862              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30863              * passed to handlers has the following properties:<br />
30864              * <ul style="padding:5px;padding-left:16px;">
30865              * <li>tree - The TreePanel</li>
30866              * <li>target - The node being targeted for the drop</li>
30867              * <li>data - The drag data from the drag source</li>
30868              * <li>point - The point of the drop - append, above or below</li>
30869              * <li>source - The drag source</li>
30870              * <li>rawEvent - Raw mouse event</li>
30871              * <li>dropNode - Drop node(s) provided by the source.</li>
30872              * <li>cancel - Set this to true to signal drop not allowed.</li>
30873              * </ul>
30874              * @param {Object} dragOverEvent
30875              */
30876             "nodedragover" : true
30877         
30878    });
30879    if(this.singleExpand){
30880        this.on("beforeexpand", this.restrictExpand, this);
30881    }
30882 };
30883 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30884     rootVisible : true,
30885     animate: Roo.enableFx,
30886     lines : true,
30887     enableDD : false,
30888     hlDrop : Roo.enableFx,
30889   
30890     renderer: false,
30891     
30892     rendererTip: false,
30893     // private
30894     restrictExpand : function(node){
30895         var p = node.parentNode;
30896         if(p){
30897             if(p.expandedChild && p.expandedChild.parentNode == p){
30898                 p.expandedChild.collapse();
30899             }
30900             p.expandedChild = node;
30901         }
30902     },
30903
30904     // private override
30905     setRootNode : function(node){
30906         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30907         if(!this.rootVisible){
30908             node.ui = new Roo.tree.RootTreeNodeUI(node);
30909         }
30910         return node;
30911     },
30912
30913     /**
30914      * Returns the container element for this TreePanel
30915      */
30916     getEl : function(){
30917         return this.el;
30918     },
30919
30920     /**
30921      * Returns the default TreeLoader for this TreePanel
30922      */
30923     getLoader : function(){
30924         return this.loader;
30925     },
30926
30927     /**
30928      * Expand all nodes
30929      */
30930     expandAll : function(){
30931         this.root.expand(true);
30932     },
30933
30934     /**
30935      * Collapse all nodes
30936      */
30937     collapseAll : function(){
30938         this.root.collapse(true);
30939     },
30940
30941     /**
30942      * Returns the selection model used by this TreePanel
30943      */
30944     getSelectionModel : function(){
30945         if(!this.selModel){
30946             this.selModel = new Roo.tree.DefaultSelectionModel();
30947         }
30948         return this.selModel;
30949     },
30950
30951     /**
30952      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30953      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30954      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30955      * @return {Array}
30956      */
30957     getChecked : function(a, startNode){
30958         startNode = startNode || this.root;
30959         var r = [];
30960         var f = function(){
30961             if(this.attributes.checked){
30962                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30963             }
30964         }
30965         startNode.cascade(f);
30966         return r;
30967     },
30968
30969     /**
30970      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30971      * @param {String} path
30972      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30973      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30974      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30975      */
30976     expandPath : function(path, attr, callback){
30977         attr = attr || "id";
30978         var keys = path.split(this.pathSeparator);
30979         var curNode = this.root;
30980         if(curNode.attributes[attr] != keys[1]){ // invalid root
30981             if(callback){
30982                 callback(false, null);
30983             }
30984             return;
30985         }
30986         var index = 1;
30987         var f = function(){
30988             if(++index == keys.length){
30989                 if(callback){
30990                     callback(true, curNode);
30991                 }
30992                 return;
30993             }
30994             var c = curNode.findChild(attr, keys[index]);
30995             if(!c){
30996                 if(callback){
30997                     callback(false, curNode);
30998                 }
30999                 return;
31000             }
31001             curNode = c;
31002             c.expand(false, false, f);
31003         };
31004         curNode.expand(false, false, f);
31005     },
31006
31007     /**
31008      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31009      * @param {String} path
31010      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31011      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31012      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31013      */
31014     selectPath : function(path, attr, callback){
31015         attr = attr || "id";
31016         var keys = path.split(this.pathSeparator);
31017         var v = keys.pop();
31018         if(keys.length > 0){
31019             var f = function(success, node){
31020                 if(success && node){
31021                     var n = node.findChild(attr, v);
31022                     if(n){
31023                         n.select();
31024                         if(callback){
31025                             callback(true, n);
31026                         }
31027                     }else if(callback){
31028                         callback(false, n);
31029                     }
31030                 }else{
31031                     if(callback){
31032                         callback(false, n);
31033                     }
31034                 }
31035             };
31036             this.expandPath(keys.join(this.pathSeparator), attr, f);
31037         }else{
31038             this.root.select();
31039             if(callback){
31040                 callback(true, this.root);
31041             }
31042         }
31043     },
31044
31045     getTreeEl : function(){
31046         return this.el;
31047     },
31048
31049     /**
31050      * Trigger rendering of this TreePanel
31051      */
31052     render : function(){
31053         if (this.innerCt) {
31054             return this; // stop it rendering more than once!!
31055         }
31056         
31057         this.innerCt = this.el.createChild({tag:"ul",
31058                cls:"x-tree-root-ct " +
31059                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31060
31061         if(this.containerScroll){
31062             Roo.dd.ScrollManager.register(this.el);
31063         }
31064         if((this.enableDD || this.enableDrop) && !this.dropZone){
31065            /**
31066             * The dropZone used by this tree if drop is enabled
31067             * @type Roo.tree.TreeDropZone
31068             */
31069              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31070                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31071            });
31072         }
31073         if((this.enableDD || this.enableDrag) && !this.dragZone){
31074            /**
31075             * The dragZone used by this tree if drag is enabled
31076             * @type Roo.tree.TreeDragZone
31077             */
31078             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31079                ddGroup: this.ddGroup || "TreeDD",
31080                scroll: this.ddScroll
31081            });
31082         }
31083         this.getSelectionModel().init(this);
31084         if (!this.root) {
31085             console.log("ROOT not set in tree");
31086             return;
31087         }
31088         this.root.render();
31089         if(!this.rootVisible){
31090             this.root.renderChildren();
31091         }
31092         return this;
31093     }
31094 });/*
31095  * Based on:
31096  * Ext JS Library 1.1.1
31097  * Copyright(c) 2006-2007, Ext JS, LLC.
31098  *
31099  * Originally Released Under LGPL - original licence link has changed is not relivant.
31100  *
31101  * Fork - LGPL
31102  * <script type="text/javascript">
31103  */
31104  
31105
31106 /**
31107  * @class Roo.tree.DefaultSelectionModel
31108  * @extends Roo.util.Observable
31109  * The default single selection for a TreePanel.
31110  */
31111 Roo.tree.DefaultSelectionModel = function(){
31112    this.selNode = null;
31113    
31114    this.addEvents({
31115        /**
31116         * @event selectionchange
31117         * Fires when the selected node changes
31118         * @param {DefaultSelectionModel} this
31119         * @param {TreeNode} node the new selection
31120         */
31121        "selectionchange" : true,
31122
31123        /**
31124         * @event beforeselect
31125         * Fires before the selected node changes, return false to cancel the change
31126         * @param {DefaultSelectionModel} this
31127         * @param {TreeNode} node the new selection
31128         * @param {TreeNode} node the old selection
31129         */
31130        "beforeselect" : true
31131    });
31132 };
31133
31134 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31135     init : function(tree){
31136         this.tree = tree;
31137         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31138         tree.on("click", this.onNodeClick, this);
31139     },
31140     
31141     onNodeClick : function(node, e){
31142         if (e.ctrlKey && this.selNode == node)  {
31143             this.unselect(node);
31144             return;
31145         }
31146         this.select(node);
31147     },
31148     
31149     /**
31150      * Select a node.
31151      * @param {TreeNode} node The node to select
31152      * @return {TreeNode} The selected node
31153      */
31154     select : function(node){
31155         var last = this.selNode;
31156         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31157             if(last){
31158                 last.ui.onSelectedChange(false);
31159             }
31160             this.selNode = node;
31161             node.ui.onSelectedChange(true);
31162             this.fireEvent("selectionchange", this, node, last);
31163         }
31164         return node;
31165     },
31166     
31167     /**
31168      * Deselect a node.
31169      * @param {TreeNode} node The node to unselect
31170      */
31171     unselect : function(node){
31172         if(this.selNode == node){
31173             this.clearSelections();
31174         }    
31175     },
31176     
31177     /**
31178      * Clear all selections
31179      */
31180     clearSelections : function(){
31181         var n = this.selNode;
31182         if(n){
31183             n.ui.onSelectedChange(false);
31184             this.selNode = null;
31185             this.fireEvent("selectionchange", this, null);
31186         }
31187         return n;
31188     },
31189     
31190     /**
31191      * Get the selected node
31192      * @return {TreeNode} The selected node
31193      */
31194     getSelectedNode : function(){
31195         return this.selNode;    
31196     },
31197     
31198     /**
31199      * Returns true if the node is selected
31200      * @param {TreeNode} node The node to check
31201      * @return {Boolean}
31202      */
31203     isSelected : function(node){
31204         return this.selNode == node;  
31205     },
31206
31207     /**
31208      * Selects the node above the selected node in the tree, intelligently walking the nodes
31209      * @return TreeNode The new selection
31210      */
31211     selectPrevious : function(){
31212         var s = this.selNode || this.lastSelNode;
31213         if(!s){
31214             return null;
31215         }
31216         var ps = s.previousSibling;
31217         if(ps){
31218             if(!ps.isExpanded() || ps.childNodes.length < 1){
31219                 return this.select(ps);
31220             } else{
31221                 var lc = ps.lastChild;
31222                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31223                     lc = lc.lastChild;
31224                 }
31225                 return this.select(lc);
31226             }
31227         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31228             return this.select(s.parentNode);
31229         }
31230         return null;
31231     },
31232
31233     /**
31234      * Selects the node above the selected node in the tree, intelligently walking the nodes
31235      * @return TreeNode The new selection
31236      */
31237     selectNext : function(){
31238         var s = this.selNode || this.lastSelNode;
31239         if(!s){
31240             return null;
31241         }
31242         if(s.firstChild && s.isExpanded()){
31243              return this.select(s.firstChild);
31244          }else if(s.nextSibling){
31245              return this.select(s.nextSibling);
31246          }else if(s.parentNode){
31247             var newS = null;
31248             s.parentNode.bubble(function(){
31249                 if(this.nextSibling){
31250                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31251                     return false;
31252                 }
31253             });
31254             return newS;
31255          }
31256         return null;
31257     },
31258
31259     onKeyDown : function(e){
31260         var s = this.selNode || this.lastSelNode;
31261         // undesirable, but required
31262         var sm = this;
31263         if(!s){
31264             return;
31265         }
31266         var k = e.getKey();
31267         switch(k){
31268              case e.DOWN:
31269                  e.stopEvent();
31270                  this.selectNext();
31271              break;
31272              case e.UP:
31273                  e.stopEvent();
31274                  this.selectPrevious();
31275              break;
31276              case e.RIGHT:
31277                  e.preventDefault();
31278                  if(s.hasChildNodes()){
31279                      if(!s.isExpanded()){
31280                          s.expand();
31281                      }else if(s.firstChild){
31282                          this.select(s.firstChild, e);
31283                      }
31284                  }
31285              break;
31286              case e.LEFT:
31287                  e.preventDefault();
31288                  if(s.hasChildNodes() && s.isExpanded()){
31289                      s.collapse();
31290                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31291                      this.select(s.parentNode, e);
31292                  }
31293              break;
31294         };
31295     }
31296 });
31297
31298 /**
31299  * @class Roo.tree.MultiSelectionModel
31300  * @extends Roo.util.Observable
31301  * Multi selection for a TreePanel.
31302  */
31303 Roo.tree.MultiSelectionModel = function(){
31304    this.selNodes = [];
31305    this.selMap = {};
31306    this.addEvents({
31307        /**
31308         * @event selectionchange
31309         * Fires when the selected nodes change
31310         * @param {MultiSelectionModel} this
31311         * @param {Array} nodes Array of the selected nodes
31312         */
31313        "selectionchange" : true
31314    });
31315 };
31316
31317 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31318     init : function(tree){
31319         this.tree = tree;
31320         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31321         tree.on("click", this.onNodeClick, this);
31322     },
31323     
31324     onNodeClick : function(node, e){
31325         this.select(node, e, e.ctrlKey);
31326     },
31327     
31328     /**
31329      * Select a node.
31330      * @param {TreeNode} node The node to select
31331      * @param {EventObject} e (optional) An event associated with the selection
31332      * @param {Boolean} keepExisting True to retain existing selections
31333      * @return {TreeNode} The selected node
31334      */
31335     select : function(node, e, keepExisting){
31336         if(keepExisting !== true){
31337             this.clearSelections(true);
31338         }
31339         if(this.isSelected(node)){
31340             this.lastSelNode = node;
31341             return node;
31342         }
31343         this.selNodes.push(node);
31344         this.selMap[node.id] = node;
31345         this.lastSelNode = node;
31346         node.ui.onSelectedChange(true);
31347         this.fireEvent("selectionchange", this, this.selNodes);
31348         return node;
31349     },
31350     
31351     /**
31352      * Deselect a node.
31353      * @param {TreeNode} node The node to unselect
31354      */
31355     unselect : function(node){
31356         if(this.selMap[node.id]){
31357             node.ui.onSelectedChange(false);
31358             var sn = this.selNodes;
31359             var index = -1;
31360             if(sn.indexOf){
31361                 index = sn.indexOf(node);
31362             }else{
31363                 for(var i = 0, len = sn.length; i < len; i++){
31364                     if(sn[i] == node){
31365                         index = i;
31366                         break;
31367                     }
31368                 }
31369             }
31370             if(index != -1){
31371                 this.selNodes.splice(index, 1);
31372             }
31373             delete this.selMap[node.id];
31374             this.fireEvent("selectionchange", this, this.selNodes);
31375         }
31376     },
31377     
31378     /**
31379      * Clear all selections
31380      */
31381     clearSelections : function(suppressEvent){
31382         var sn = this.selNodes;
31383         if(sn.length > 0){
31384             for(var i = 0, len = sn.length; i < len; i++){
31385                 sn[i].ui.onSelectedChange(false);
31386             }
31387             this.selNodes = [];
31388             this.selMap = {};
31389             if(suppressEvent !== true){
31390                 this.fireEvent("selectionchange", this, this.selNodes);
31391             }
31392         }
31393     },
31394     
31395     /**
31396      * Returns true if the node is selected
31397      * @param {TreeNode} node The node to check
31398      * @return {Boolean}
31399      */
31400     isSelected : function(node){
31401         return this.selMap[node.id] ? true : false;  
31402     },
31403     
31404     /**
31405      * Returns an array of the selected nodes
31406      * @return {Array}
31407      */
31408     getSelectedNodes : function(){
31409         return this.selNodes;    
31410     },
31411
31412     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31413
31414     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31415
31416     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31417 });/*
31418  * Based on:
31419  * Ext JS Library 1.1.1
31420  * Copyright(c) 2006-2007, Ext JS, LLC.
31421  *
31422  * Originally Released Under LGPL - original licence link has changed is not relivant.
31423  *
31424  * Fork - LGPL
31425  * <script type="text/javascript">
31426  */
31427  
31428 /**
31429  * @class Roo.tree.TreeNode
31430  * @extends Roo.data.Node
31431  * @cfg {String} text The text for this node
31432  * @cfg {Boolean} expanded true to start the node expanded
31433  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31434  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31435  * @cfg {Boolean} disabled true to start the node disabled
31436  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31437  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31438  * @cfg {String} cls A css class to be added to the node
31439  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31440  * @cfg {String} href URL of the link used for the node (defaults to #)
31441  * @cfg {String} hrefTarget target frame for the link
31442  * @cfg {String} qtip An Ext QuickTip for the node
31443  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31444  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31445  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31446  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31447  * (defaults to undefined with no checkbox rendered)
31448  * @constructor
31449  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31450  */
31451 Roo.tree.TreeNode = function(attributes){
31452     attributes = attributes || {};
31453     if(typeof attributes == "string"){
31454         attributes = {text: attributes};
31455     }
31456     this.childrenRendered = false;
31457     this.rendered = false;
31458     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31459     this.expanded = attributes.expanded === true;
31460     this.isTarget = attributes.isTarget !== false;
31461     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31462     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31463
31464     /**
31465      * Read-only. The text for this node. To change it use setText().
31466      * @type String
31467      */
31468     this.text = attributes.text;
31469     /**
31470      * True if this node is disabled.
31471      * @type Boolean
31472      */
31473     this.disabled = attributes.disabled === true;
31474
31475     this.addEvents({
31476         /**
31477         * @event textchange
31478         * Fires when the text for this node is changed
31479         * @param {Node} this This node
31480         * @param {String} text The new text
31481         * @param {String} oldText The old text
31482         */
31483         "textchange" : true,
31484         /**
31485         * @event beforeexpand
31486         * Fires before this node is expanded, return false to cancel.
31487         * @param {Node} this This node
31488         * @param {Boolean} deep
31489         * @param {Boolean} anim
31490         */
31491         "beforeexpand" : true,
31492         /**
31493         * @event beforecollapse
31494         * Fires before this node is collapsed, return false to cancel.
31495         * @param {Node} this This node
31496         * @param {Boolean} deep
31497         * @param {Boolean} anim
31498         */
31499         "beforecollapse" : true,
31500         /**
31501         * @event expand
31502         * Fires when this node is expanded
31503         * @param {Node} this This node
31504         */
31505         "expand" : true,
31506         /**
31507         * @event disabledchange
31508         * Fires when the disabled status of this node changes
31509         * @param {Node} this This node
31510         * @param {Boolean} disabled
31511         */
31512         "disabledchange" : true,
31513         /**
31514         * @event collapse
31515         * Fires when this node is collapsed
31516         * @param {Node} this This node
31517         */
31518         "collapse" : true,
31519         /**
31520         * @event beforeclick
31521         * Fires before click processing. Return false to cancel the default action.
31522         * @param {Node} this This node
31523         * @param {Roo.EventObject} e The event object
31524         */
31525         "beforeclick":true,
31526         /**
31527         * @event checkchange
31528         * Fires when a node with a checkbox's checked property changes
31529         * @param {Node} this This node
31530         * @param {Boolean} checked
31531         */
31532         "checkchange":true,
31533         /**
31534         * @event click
31535         * Fires when this node is clicked
31536         * @param {Node} this This node
31537         * @param {Roo.EventObject} e The event object
31538         */
31539         "click":true,
31540         /**
31541         * @event dblclick
31542         * Fires when this node is double clicked
31543         * @param {Node} this This node
31544         * @param {Roo.EventObject} e The event object
31545         */
31546         "dblclick":true,
31547         /**
31548         * @event contextmenu
31549         * Fires when this node is right clicked
31550         * @param {Node} this This node
31551         * @param {Roo.EventObject} e The event object
31552         */
31553         "contextmenu":true,
31554         /**
31555         * @event beforechildrenrendered
31556         * Fires right before the child nodes for this node are rendered
31557         * @param {Node} this This node
31558         */
31559         "beforechildrenrendered":true
31560     });
31561
31562     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31563
31564     /**
31565      * Read-only. The UI for this node
31566      * @type TreeNodeUI
31567      */
31568     this.ui = new uiClass(this);
31569 };
31570 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31571     preventHScroll: true,
31572     /**
31573      * Returns true if this node is expanded
31574      * @return {Boolean}
31575      */
31576     isExpanded : function(){
31577         return this.expanded;
31578     },
31579
31580     /**
31581      * Returns the UI object for this node
31582      * @return {TreeNodeUI}
31583      */
31584     getUI : function(){
31585         return this.ui;
31586     },
31587
31588     // private override
31589     setFirstChild : function(node){
31590         var of = this.firstChild;
31591         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31592         if(this.childrenRendered && of && node != of){
31593             of.renderIndent(true, true);
31594         }
31595         if(this.rendered){
31596             this.renderIndent(true, true);
31597         }
31598     },
31599
31600     // private override
31601     setLastChild : function(node){
31602         var ol = this.lastChild;
31603         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31604         if(this.childrenRendered && ol && node != ol){
31605             ol.renderIndent(true, true);
31606         }
31607         if(this.rendered){
31608             this.renderIndent(true, true);
31609         }
31610     },
31611
31612     // these methods are overridden to provide lazy rendering support
31613     // private override
31614     appendChild : function(){
31615         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31616         if(node && this.childrenRendered){
31617             node.render();
31618         }
31619         this.ui.updateExpandIcon();
31620         return node;
31621     },
31622
31623     // private override
31624     removeChild : function(node){
31625         this.ownerTree.getSelectionModel().unselect(node);
31626         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31627         // if it's been rendered remove dom node
31628         if(this.childrenRendered){
31629             node.ui.remove();
31630         }
31631         if(this.childNodes.length < 1){
31632             this.collapse(false, false);
31633         }else{
31634             this.ui.updateExpandIcon();
31635         }
31636         if(!this.firstChild) {
31637             this.childrenRendered = false;
31638         }
31639         return node;
31640     },
31641
31642     // private override
31643     insertBefore : function(node, refNode){
31644         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31645         if(newNode && refNode && this.childrenRendered){
31646             node.render();
31647         }
31648         this.ui.updateExpandIcon();
31649         return newNode;
31650     },
31651
31652     /**
31653      * Sets the text for this node
31654      * @param {String} text
31655      */
31656     setText : function(text){
31657         var oldText = this.text;
31658         this.text = text;
31659         this.attributes.text = text;
31660         if(this.rendered){ // event without subscribing
31661             this.ui.onTextChange(this, text, oldText);
31662         }
31663         this.fireEvent("textchange", this, text, oldText);
31664     },
31665
31666     /**
31667      * Triggers selection of this node
31668      */
31669     select : function(){
31670         this.getOwnerTree().getSelectionModel().select(this);
31671     },
31672
31673     /**
31674      * Triggers deselection of this node
31675      */
31676     unselect : function(){
31677         this.getOwnerTree().getSelectionModel().unselect(this);
31678     },
31679
31680     /**
31681      * Returns true if this node is selected
31682      * @return {Boolean}
31683      */
31684     isSelected : function(){
31685         return this.getOwnerTree().getSelectionModel().isSelected(this);
31686     },
31687
31688     /**
31689      * Expand this node.
31690      * @param {Boolean} deep (optional) True to expand all children as well
31691      * @param {Boolean} anim (optional) false to cancel the default animation
31692      * @param {Function} callback (optional) A callback to be called when
31693      * expanding this node completes (does not wait for deep expand to complete).
31694      * Called with 1 parameter, this node.
31695      */
31696     expand : function(deep, anim, callback){
31697         if(!this.expanded){
31698             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31699                 return;
31700             }
31701             if(!this.childrenRendered){
31702                 this.renderChildren();
31703             }
31704             this.expanded = true;
31705             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31706                 this.ui.animExpand(function(){
31707                     this.fireEvent("expand", this);
31708                     if(typeof callback == "function"){
31709                         callback(this);
31710                     }
31711                     if(deep === true){
31712                         this.expandChildNodes(true);
31713                     }
31714                 }.createDelegate(this));
31715                 return;
31716             }else{
31717                 this.ui.expand();
31718                 this.fireEvent("expand", this);
31719                 if(typeof callback == "function"){
31720                     callback(this);
31721                 }
31722             }
31723         }else{
31724            if(typeof callback == "function"){
31725                callback(this);
31726            }
31727         }
31728         if(deep === true){
31729             this.expandChildNodes(true);
31730         }
31731     },
31732
31733     isHiddenRoot : function(){
31734         return this.isRoot && !this.getOwnerTree().rootVisible;
31735     },
31736
31737     /**
31738      * Collapse this node.
31739      * @param {Boolean} deep (optional) True to collapse all children as well
31740      * @param {Boolean} anim (optional) false to cancel the default animation
31741      */
31742     collapse : function(deep, anim){
31743         if(this.expanded && !this.isHiddenRoot()){
31744             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31745                 return;
31746             }
31747             this.expanded = false;
31748             if((this.getOwnerTree().animate && anim !== false) || anim){
31749                 this.ui.animCollapse(function(){
31750                     this.fireEvent("collapse", this);
31751                     if(deep === true){
31752                         this.collapseChildNodes(true);
31753                     }
31754                 }.createDelegate(this));
31755                 return;
31756             }else{
31757                 this.ui.collapse();
31758                 this.fireEvent("collapse", this);
31759             }
31760         }
31761         if(deep === true){
31762             var cs = this.childNodes;
31763             for(var i = 0, len = cs.length; i < len; i++) {
31764                 cs[i].collapse(true, false);
31765             }
31766         }
31767     },
31768
31769     // private
31770     delayedExpand : function(delay){
31771         if(!this.expandProcId){
31772             this.expandProcId = this.expand.defer(delay, this);
31773         }
31774     },
31775
31776     // private
31777     cancelExpand : function(){
31778         if(this.expandProcId){
31779             clearTimeout(this.expandProcId);
31780         }
31781         this.expandProcId = false;
31782     },
31783
31784     /**
31785      * Toggles expanded/collapsed state of the node
31786      */
31787     toggle : function(){
31788         if(this.expanded){
31789             this.collapse();
31790         }else{
31791             this.expand();
31792         }
31793     },
31794
31795     /**
31796      * Ensures all parent nodes are expanded
31797      */
31798     ensureVisible : function(callback){
31799         var tree = this.getOwnerTree();
31800         tree.expandPath(this.parentNode.getPath(), false, function(){
31801             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31802             Roo.callback(callback);
31803         }.createDelegate(this));
31804     },
31805
31806     /**
31807      * Expand all child nodes
31808      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31809      */
31810     expandChildNodes : function(deep){
31811         var cs = this.childNodes;
31812         for(var i = 0, len = cs.length; i < len; i++) {
31813                 cs[i].expand(deep);
31814         }
31815     },
31816
31817     /**
31818      * Collapse all child nodes
31819      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31820      */
31821     collapseChildNodes : function(deep){
31822         var cs = this.childNodes;
31823         for(var i = 0, len = cs.length; i < len; i++) {
31824                 cs[i].collapse(deep);
31825         }
31826     },
31827
31828     /**
31829      * Disables this node
31830      */
31831     disable : function(){
31832         this.disabled = true;
31833         this.unselect();
31834         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31835             this.ui.onDisableChange(this, true);
31836         }
31837         this.fireEvent("disabledchange", this, true);
31838     },
31839
31840     /**
31841      * Enables this node
31842      */
31843     enable : function(){
31844         this.disabled = false;
31845         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31846             this.ui.onDisableChange(this, false);
31847         }
31848         this.fireEvent("disabledchange", this, false);
31849     },
31850
31851     // private
31852     renderChildren : function(suppressEvent){
31853         if(suppressEvent !== false){
31854             this.fireEvent("beforechildrenrendered", this);
31855         }
31856         var cs = this.childNodes;
31857         for(var i = 0, len = cs.length; i < len; i++){
31858             cs[i].render(true);
31859         }
31860         this.childrenRendered = true;
31861     },
31862
31863     // private
31864     sort : function(fn, scope){
31865         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31866         if(this.childrenRendered){
31867             var cs = this.childNodes;
31868             for(var i = 0, len = cs.length; i < len; i++){
31869                 cs[i].render(true);
31870             }
31871         }
31872     },
31873
31874     // private
31875     render : function(bulkRender){
31876         this.ui.render(bulkRender);
31877         if(!this.rendered){
31878             this.rendered = true;
31879             if(this.expanded){
31880                 this.expanded = false;
31881                 this.expand(false, false);
31882             }
31883         }
31884     },
31885
31886     // private
31887     renderIndent : function(deep, refresh){
31888         if(refresh){
31889             this.ui.childIndent = null;
31890         }
31891         this.ui.renderIndent();
31892         if(deep === true && this.childrenRendered){
31893             var cs = this.childNodes;
31894             for(var i = 0, len = cs.length; i < len; i++){
31895                 cs[i].renderIndent(true, refresh);
31896             }
31897         }
31898     }
31899 });/*
31900  * Based on:
31901  * Ext JS Library 1.1.1
31902  * Copyright(c) 2006-2007, Ext JS, LLC.
31903  *
31904  * Originally Released Under LGPL - original licence link has changed is not relivant.
31905  *
31906  * Fork - LGPL
31907  * <script type="text/javascript">
31908  */
31909  
31910 /**
31911  * @class Roo.tree.AsyncTreeNode
31912  * @extends Roo.tree.TreeNode
31913  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31914  * @constructor
31915  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31916  */
31917  Roo.tree.AsyncTreeNode = function(config){
31918     this.loaded = false;
31919     this.loading = false;
31920     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31921     /**
31922     * @event beforeload
31923     * Fires before this node is loaded, return false to cancel
31924     * @param {Node} this This node
31925     */
31926     this.addEvents({'beforeload':true, 'load': true});
31927     /**
31928     * @event load
31929     * Fires when this node is loaded
31930     * @param {Node} this This node
31931     */
31932     /**
31933      * The loader used by this node (defaults to using the tree's defined loader)
31934      * @type TreeLoader
31935      * @property loader
31936      */
31937 };
31938 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31939     expand : function(deep, anim, callback){
31940         if(this.loading){ // if an async load is already running, waiting til it's done
31941             var timer;
31942             var f = function(){
31943                 if(!this.loading){ // done loading
31944                     clearInterval(timer);
31945                     this.expand(deep, anim, callback);
31946                 }
31947             }.createDelegate(this);
31948             timer = setInterval(f, 200);
31949             return;
31950         }
31951         if(!this.loaded){
31952             if(this.fireEvent("beforeload", this) === false){
31953                 return;
31954             }
31955             this.loading = true;
31956             this.ui.beforeLoad(this);
31957             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31958             if(loader){
31959                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31960                 return;
31961             }
31962         }
31963         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31964     },
31965     
31966     /**
31967      * Returns true if this node is currently loading
31968      * @return {Boolean}
31969      */
31970     isLoading : function(){
31971         return this.loading;  
31972     },
31973     
31974     loadComplete : function(deep, anim, callback){
31975         this.loading = false;
31976         this.loaded = true;
31977         this.ui.afterLoad(this);
31978         this.fireEvent("load", this);
31979         this.expand(deep, anim, callback);
31980     },
31981     
31982     /**
31983      * Returns true if this node has been loaded
31984      * @return {Boolean}
31985      */
31986     isLoaded : function(){
31987         return this.loaded;
31988     },
31989     
31990     hasChildNodes : function(){
31991         if(!this.isLeaf() && !this.loaded){
31992             return true;
31993         }else{
31994             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31995         }
31996     },
31997
31998     /**
31999      * Trigger a reload for this node
32000      * @param {Function} callback
32001      */
32002     reload : function(callback){
32003         this.collapse(false, false);
32004         while(this.firstChild){
32005             this.removeChild(this.firstChild);
32006         }
32007         this.childrenRendered = false;
32008         this.loaded = false;
32009         if(this.isHiddenRoot()){
32010             this.expanded = false;
32011         }
32012         this.expand(false, false, callback);
32013     }
32014 });/*
32015  * Based on:
32016  * Ext JS Library 1.1.1
32017  * Copyright(c) 2006-2007, Ext JS, LLC.
32018  *
32019  * Originally Released Under LGPL - original licence link has changed is not relivant.
32020  *
32021  * Fork - LGPL
32022  * <script type="text/javascript">
32023  */
32024  
32025 /**
32026  * @class Roo.tree.TreeNodeUI
32027  * @constructor
32028  * @param {Object} node The node to render
32029  * The TreeNode UI implementation is separate from the
32030  * tree implementation. Unless you are customizing the tree UI,
32031  * you should never have to use this directly.
32032  */
32033 Roo.tree.TreeNodeUI = function(node){
32034     this.node = node;
32035     this.rendered = false;
32036     this.animating = false;
32037     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32038 };
32039
32040 Roo.tree.TreeNodeUI.prototype = {
32041     removeChild : function(node){
32042         if(this.rendered){
32043             this.ctNode.removeChild(node.ui.getEl());
32044         }
32045     },
32046
32047     beforeLoad : function(){
32048          this.addClass("x-tree-node-loading");
32049     },
32050
32051     afterLoad : function(){
32052          this.removeClass("x-tree-node-loading");
32053     },
32054
32055     onTextChange : function(node, text, oldText){
32056         if(this.rendered){
32057             this.textNode.innerHTML = text;
32058         }
32059     },
32060
32061     onDisableChange : function(node, state){
32062         this.disabled = state;
32063         if(state){
32064             this.addClass("x-tree-node-disabled");
32065         }else{
32066             this.removeClass("x-tree-node-disabled");
32067         }
32068     },
32069
32070     onSelectedChange : function(state){
32071         if(state){
32072             this.focus();
32073             this.addClass("x-tree-selected");
32074         }else{
32075             //this.blur();
32076             this.removeClass("x-tree-selected");
32077         }
32078     },
32079
32080     onMove : function(tree, node, oldParent, newParent, index, refNode){
32081         this.childIndent = null;
32082         if(this.rendered){
32083             var targetNode = newParent.ui.getContainer();
32084             if(!targetNode){//target not rendered
32085                 this.holder = document.createElement("div");
32086                 this.holder.appendChild(this.wrap);
32087                 return;
32088             }
32089             var insertBefore = refNode ? refNode.ui.getEl() : null;
32090             if(insertBefore){
32091                 targetNode.insertBefore(this.wrap, insertBefore);
32092             }else{
32093                 targetNode.appendChild(this.wrap);
32094             }
32095             this.node.renderIndent(true);
32096         }
32097     },
32098
32099     addClass : function(cls){
32100         if(this.elNode){
32101             Roo.fly(this.elNode).addClass(cls);
32102         }
32103     },
32104
32105     removeClass : function(cls){
32106         if(this.elNode){
32107             Roo.fly(this.elNode).removeClass(cls);
32108         }
32109     },
32110
32111     remove : function(){
32112         if(this.rendered){
32113             this.holder = document.createElement("div");
32114             this.holder.appendChild(this.wrap);
32115         }
32116     },
32117
32118     fireEvent : function(){
32119         return this.node.fireEvent.apply(this.node, arguments);
32120     },
32121
32122     initEvents : function(){
32123         this.node.on("move", this.onMove, this);
32124         var E = Roo.EventManager;
32125         var a = this.anchor;
32126
32127         var el = Roo.fly(a, '_treeui');
32128
32129         if(Roo.isOpera){ // opera render bug ignores the CSS
32130             el.setStyle("text-decoration", "none");
32131         }
32132
32133         el.on("click", this.onClick, this);
32134         el.on("dblclick", this.onDblClick, this);
32135
32136         if(this.checkbox){
32137             Roo.EventManager.on(this.checkbox,
32138                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32139         }
32140
32141         el.on("contextmenu", this.onContextMenu, this);
32142
32143         var icon = Roo.fly(this.iconNode);
32144         icon.on("click", this.onClick, this);
32145         icon.on("dblclick", this.onDblClick, this);
32146         icon.on("contextmenu", this.onContextMenu, this);
32147         E.on(this.ecNode, "click", this.ecClick, this, true);
32148
32149         if(this.node.disabled){
32150             this.addClass("x-tree-node-disabled");
32151         }
32152         if(this.node.hidden){
32153             this.addClass("x-tree-node-disabled");
32154         }
32155         var ot = this.node.getOwnerTree();
32156         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32157         if(dd && (!this.node.isRoot || ot.rootVisible)){
32158             Roo.dd.Registry.register(this.elNode, {
32159                 node: this.node,
32160                 handles: this.getDDHandles(),
32161                 isHandle: false
32162             });
32163         }
32164     },
32165
32166     getDDHandles : function(){
32167         return [this.iconNode, this.textNode];
32168     },
32169
32170     hide : function(){
32171         if(this.rendered){
32172             this.wrap.style.display = "none";
32173         }
32174     },
32175
32176     show : function(){
32177         if(this.rendered){
32178             this.wrap.style.display = "";
32179         }
32180     },
32181
32182     onContextMenu : function(e){
32183         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32184             e.preventDefault();
32185             this.focus();
32186             this.fireEvent("contextmenu", this.node, e);
32187         }
32188     },
32189
32190     onClick : function(e){
32191         if(this.dropping){
32192             e.stopEvent();
32193             return;
32194         }
32195         if(this.fireEvent("beforeclick", this.node, e) !== false){
32196             if(!this.disabled && this.node.attributes.href){
32197                 this.fireEvent("click", this.node, e);
32198                 return;
32199             }
32200             e.preventDefault();
32201             if(this.disabled){
32202                 return;
32203             }
32204
32205             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32206                 this.node.toggle();
32207             }
32208
32209             this.fireEvent("click", this.node, e);
32210         }else{
32211             e.stopEvent();
32212         }
32213     },
32214
32215     onDblClick : function(e){
32216         e.preventDefault();
32217         if(this.disabled){
32218             return;
32219         }
32220         if(this.checkbox){
32221             this.toggleCheck();
32222         }
32223         if(!this.animating && this.node.hasChildNodes()){
32224             this.node.toggle();
32225         }
32226         this.fireEvent("dblclick", this.node, e);
32227     },
32228
32229     onCheckChange : function(){
32230         var checked = this.checkbox.checked;
32231         this.node.attributes.checked = checked;
32232         this.fireEvent('checkchange', this.node, checked);
32233     },
32234
32235     ecClick : function(e){
32236         if(!this.animating && this.node.hasChildNodes()){
32237             this.node.toggle();
32238         }
32239     },
32240
32241     startDrop : function(){
32242         this.dropping = true;
32243     },
32244
32245     // delayed drop so the click event doesn't get fired on a drop
32246     endDrop : function(){
32247        setTimeout(function(){
32248            this.dropping = false;
32249        }.createDelegate(this), 50);
32250     },
32251
32252     expand : function(){
32253         this.updateExpandIcon();
32254         this.ctNode.style.display = "";
32255     },
32256
32257     focus : function(){
32258         if(!this.node.preventHScroll){
32259             try{this.anchor.focus();
32260             }catch(e){}
32261         }else if(!Roo.isIE){
32262             try{
32263                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32264                 var l = noscroll.scrollLeft;
32265                 this.anchor.focus();
32266                 noscroll.scrollLeft = l;
32267             }catch(e){}
32268         }
32269     },
32270
32271     toggleCheck : function(value){
32272         var cb = this.checkbox;
32273         if(cb){
32274             cb.checked = (value === undefined ? !cb.checked : value);
32275         }
32276     },
32277
32278     blur : function(){
32279         try{
32280             this.anchor.blur();
32281         }catch(e){}
32282     },
32283
32284     animExpand : function(callback){
32285         var ct = Roo.get(this.ctNode);
32286         ct.stopFx();
32287         if(!this.node.hasChildNodes()){
32288             this.updateExpandIcon();
32289             this.ctNode.style.display = "";
32290             Roo.callback(callback);
32291             return;
32292         }
32293         this.animating = true;
32294         this.updateExpandIcon();
32295
32296         ct.slideIn('t', {
32297            callback : function(){
32298                this.animating = false;
32299                Roo.callback(callback);
32300             },
32301             scope: this,
32302             duration: this.node.ownerTree.duration || .25
32303         });
32304     },
32305
32306     highlight : function(){
32307         var tree = this.node.getOwnerTree();
32308         Roo.fly(this.wrap).highlight(
32309             tree.hlColor || "C3DAF9",
32310             {endColor: tree.hlBaseColor}
32311         );
32312     },
32313
32314     collapse : function(){
32315         this.updateExpandIcon();
32316         this.ctNode.style.display = "none";
32317     },
32318
32319     animCollapse : function(callback){
32320         var ct = Roo.get(this.ctNode);
32321         ct.enableDisplayMode('block');
32322         ct.stopFx();
32323
32324         this.animating = true;
32325         this.updateExpandIcon();
32326
32327         ct.slideOut('t', {
32328             callback : function(){
32329                this.animating = false;
32330                Roo.callback(callback);
32331             },
32332             scope: this,
32333             duration: this.node.ownerTree.duration || .25
32334         });
32335     },
32336
32337     getContainer : function(){
32338         return this.ctNode;
32339     },
32340
32341     getEl : function(){
32342         return this.wrap;
32343     },
32344
32345     appendDDGhost : function(ghostNode){
32346         ghostNode.appendChild(this.elNode.cloneNode(true));
32347     },
32348
32349     getDDRepairXY : function(){
32350         return Roo.lib.Dom.getXY(this.iconNode);
32351     },
32352
32353     onRender : function(){
32354         this.render();
32355     },
32356
32357     render : function(bulkRender){
32358         var n = this.node, a = n.attributes;
32359         var targetNode = n.parentNode ?
32360               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32361
32362         if(!this.rendered){
32363             this.rendered = true;
32364
32365             this.renderElements(n, a, targetNode, bulkRender);
32366
32367             if(a.qtip){
32368                if(this.textNode.setAttributeNS){
32369                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32370                    if(a.qtipTitle){
32371                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32372                    }
32373                }else{
32374                    this.textNode.setAttribute("ext:qtip", a.qtip);
32375                    if(a.qtipTitle){
32376                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32377                    }
32378                }
32379             }else if(a.qtipCfg){
32380                 a.qtipCfg.target = Roo.id(this.textNode);
32381                 Roo.QuickTips.register(a.qtipCfg);
32382             }
32383             this.initEvents();
32384             if(!this.node.expanded){
32385                 this.updateExpandIcon();
32386             }
32387         }else{
32388             if(bulkRender === true) {
32389                 targetNode.appendChild(this.wrap);
32390             }
32391         }
32392     },
32393
32394     renderElements : function(n, a, targetNode, bulkRender){
32395         // add some indent caching, this helps performance when rendering a large tree
32396         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32397         var t = n.getOwnerTree();
32398         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32399         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32400         var cb = typeof a.checked == 'boolean';
32401         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32402         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32403             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32404             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32405             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32406             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32407             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32408              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32409                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32410             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32411             "</li>"];
32412
32413         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32414             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32415                                 n.nextSibling.ui.getEl(), buf.join(""));
32416         }else{
32417             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32418         }
32419
32420         this.elNode = this.wrap.childNodes[0];
32421         this.ctNode = this.wrap.childNodes[1];
32422         var cs = this.elNode.childNodes;
32423         this.indentNode = cs[0];
32424         this.ecNode = cs[1];
32425         this.iconNode = cs[2];
32426         var index = 3;
32427         if(cb){
32428             this.checkbox = cs[3];
32429             index++;
32430         }
32431         this.anchor = cs[index];
32432         this.textNode = cs[index].firstChild;
32433     },
32434
32435     getAnchor : function(){
32436         return this.anchor;
32437     },
32438
32439     getTextEl : function(){
32440         return this.textNode;
32441     },
32442
32443     getIconEl : function(){
32444         return this.iconNode;
32445     },
32446
32447     isChecked : function(){
32448         return this.checkbox ? this.checkbox.checked : false;
32449     },
32450
32451     updateExpandIcon : function(){
32452         if(this.rendered){
32453             var n = this.node, c1, c2;
32454             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32455             var hasChild = n.hasChildNodes();
32456             if(hasChild){
32457                 if(n.expanded){
32458                     cls += "-minus";
32459                     c1 = "x-tree-node-collapsed";
32460                     c2 = "x-tree-node-expanded";
32461                 }else{
32462                     cls += "-plus";
32463                     c1 = "x-tree-node-expanded";
32464                     c2 = "x-tree-node-collapsed";
32465                 }
32466                 if(this.wasLeaf){
32467                     this.removeClass("x-tree-node-leaf");
32468                     this.wasLeaf = false;
32469                 }
32470                 if(this.c1 != c1 || this.c2 != c2){
32471                     Roo.fly(this.elNode).replaceClass(c1, c2);
32472                     this.c1 = c1; this.c2 = c2;
32473                 }
32474             }else{
32475                 if(!this.wasLeaf){
32476                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32477                     delete this.c1;
32478                     delete this.c2;
32479                     this.wasLeaf = true;
32480                 }
32481             }
32482             var ecc = "x-tree-ec-icon "+cls;
32483             if(this.ecc != ecc){
32484                 this.ecNode.className = ecc;
32485                 this.ecc = ecc;
32486             }
32487         }
32488     },
32489
32490     getChildIndent : function(){
32491         if(!this.childIndent){
32492             var buf = [];
32493             var p = this.node;
32494             while(p){
32495                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32496                     if(!p.isLast()) {
32497                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32498                     } else {
32499                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32500                     }
32501                 }
32502                 p = p.parentNode;
32503             }
32504             this.childIndent = buf.join("");
32505         }
32506         return this.childIndent;
32507     },
32508
32509     renderIndent : function(){
32510         if(this.rendered){
32511             var indent = "";
32512             var p = this.node.parentNode;
32513             if(p){
32514                 indent = p.ui.getChildIndent();
32515             }
32516             if(this.indentMarkup != indent){ // don't rerender if not required
32517                 this.indentNode.innerHTML = indent;
32518                 this.indentMarkup = indent;
32519             }
32520             this.updateExpandIcon();
32521         }
32522     }
32523 };
32524
32525 Roo.tree.RootTreeNodeUI = function(){
32526     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32527 };
32528 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32529     render : function(){
32530         if(!this.rendered){
32531             var targetNode = this.node.ownerTree.innerCt.dom;
32532             this.node.expanded = true;
32533             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32534             this.wrap = this.ctNode = targetNode.firstChild;
32535         }
32536     },
32537     collapse : function(){
32538     },
32539     expand : function(){
32540     }
32541 });/*
32542  * Based on:
32543  * Ext JS Library 1.1.1
32544  * Copyright(c) 2006-2007, Ext JS, LLC.
32545  *
32546  * Originally Released Under LGPL - original licence link has changed is not relivant.
32547  *
32548  * Fork - LGPL
32549  * <script type="text/javascript">
32550  */
32551 /**
32552  * @class Roo.tree.TreeLoader
32553  * @extends Roo.util.Observable
32554  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32555  * nodes from a specified URL. The response must be a javascript Array definition
32556  * who's elements are node definition objects. eg:
32557  * <pre><code>
32558    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32559     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32560 </code></pre>
32561  * <br><br>
32562  * A server request is sent, and child nodes are loaded only when a node is expanded.
32563  * The loading node's id is passed to the server under the parameter name "node" to
32564  * enable the server to produce the correct child nodes.
32565  * <br><br>
32566  * To pass extra parameters, an event handler may be attached to the "beforeload"
32567  * event, and the parameters specified in the TreeLoader's baseParams property:
32568  * <pre><code>
32569     myTreeLoader.on("beforeload", function(treeLoader, node) {
32570         this.baseParams.category = node.attributes.category;
32571     }, this);
32572 </code></pre><
32573  * This would pass an HTTP parameter called "category" to the server containing
32574  * the value of the Node's "category" attribute.
32575  * @constructor
32576  * Creates a new Treeloader.
32577  * @param {Object} config A config object containing config properties.
32578  */
32579 Roo.tree.TreeLoader = function(config){
32580     this.baseParams = {};
32581     this.requestMethod = "POST";
32582     Roo.apply(this, config);
32583
32584     this.addEvents({
32585     
32586         /**
32587          * @event beforeload
32588          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32589          * @param {Object} This TreeLoader object.
32590          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32591          * @param {Object} callback The callback function specified in the {@link #load} call.
32592          */
32593         beforeload : true,
32594         /**
32595          * @event load
32596          * Fires when the node has been successfuly loaded.
32597          * @param {Object} This TreeLoader object.
32598          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32599          * @param {Object} response The response object containing the data from the server.
32600          */
32601         load : true,
32602         /**
32603          * @event loadexception
32604          * Fires if the network request failed.
32605          * @param {Object} This TreeLoader object.
32606          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32607          * @param {Object} response The response object containing the data from the server.
32608          */
32609         loadexception : true,
32610         /**
32611          * @event create
32612          * Fires before a node is created, enabling you to return custom Node types 
32613          * @param {Object} This TreeLoader object.
32614          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32615          */
32616         create : true
32617     });
32618
32619     Roo.tree.TreeLoader.superclass.constructor.call(this);
32620 };
32621
32622 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32623     /**
32624     * @cfg {String} dataUrl The URL from which to request a Json string which
32625     * specifies an array of node definition object representing the child nodes
32626     * to be loaded.
32627     */
32628     /**
32629     * @cfg {Object} baseParams (optional) An object containing properties which
32630     * specify HTTP parameters to be passed to each request for child nodes.
32631     */
32632     /**
32633     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32634     * created by this loader. If the attributes sent by the server have an attribute in this object,
32635     * they take priority.
32636     */
32637     /**
32638     * @cfg {Object} uiProviders (optional) An object containing properties which
32639     * 
32640     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32641     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32642     * <i>uiProvider</i> attribute of a returned child node is a string rather
32643     * than a reference to a TreeNodeUI implementation, this that string value
32644     * is used as a property name in the uiProviders object. You can define the provider named
32645     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32646     */
32647     uiProviders : {},
32648
32649     /**
32650     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32651     * child nodes before loading.
32652     */
32653     clearOnLoad : true,
32654
32655     /**
32656     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32657     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32658     * Grid query { data : [ .....] }
32659     */
32660     
32661     root : false,
32662      /**
32663     * @cfg {String} queryParam (optional) 
32664     * Name of the query as it will be passed on the querystring (defaults to 'node')
32665     * eg. the request will be ?node=[id]
32666     */
32667     
32668     
32669     queryParam: false,
32670     
32671     /**
32672      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32673      * This is called automatically when a node is expanded, but may be used to reload
32674      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32675      * @param {Roo.tree.TreeNode} node
32676      * @param {Function} callback
32677      */
32678     load : function(node, callback){
32679         if(this.clearOnLoad){
32680             while(node.firstChild){
32681                 node.removeChild(node.firstChild);
32682             }
32683         }
32684         if(node.attributes.children){ // preloaded json children
32685             var cs = node.attributes.children;
32686             for(var i = 0, len = cs.length; i < len; i++){
32687                 node.appendChild(this.createNode(cs[i]));
32688             }
32689             if(typeof callback == "function"){
32690                 callback();
32691             }
32692         }else if(this.dataUrl){
32693             this.requestData(node, callback);
32694         }
32695     },
32696
32697     getParams: function(node){
32698         var buf = [], bp = this.baseParams;
32699         for(var key in bp){
32700             if(typeof bp[key] != "function"){
32701                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32702             }
32703         }
32704         var n = this.queryParam === false ? 'node' : this.queryParam;
32705         buf.push(n + "=", encodeURIComponent(node.id));
32706         return buf.join("");
32707     },
32708
32709     requestData : function(node, callback){
32710         if(this.fireEvent("beforeload", this, node, callback) !== false){
32711             this.transId = Roo.Ajax.request({
32712                 method:this.requestMethod,
32713                 url: this.dataUrl||this.url,
32714                 success: this.handleResponse,
32715                 failure: this.handleFailure,
32716                 scope: this,
32717                 argument: {callback: callback, node: node},
32718                 params: this.getParams(node)
32719             });
32720         }else{
32721             // if the load is cancelled, make sure we notify
32722             // the node that we are done
32723             if(typeof callback == "function"){
32724                 callback();
32725             }
32726         }
32727     },
32728
32729     isLoading : function(){
32730         return this.transId ? true : false;
32731     },
32732
32733     abort : function(){
32734         if(this.isLoading()){
32735             Roo.Ajax.abort(this.transId);
32736         }
32737     },
32738
32739     // private
32740     createNode : function(attr){
32741         // apply baseAttrs, nice idea Corey!
32742         if(this.baseAttrs){
32743             Roo.applyIf(attr, this.baseAttrs);
32744         }
32745         if(this.applyLoader !== false){
32746             attr.loader = this;
32747         }
32748         // uiProvider = depreciated..
32749         
32750         if(typeof(attr.uiProvider) == 'string'){
32751            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32752                 /**  eval:var:attr */ eval(attr.uiProvider);
32753         }
32754         if(typeof(this.uiProviders['default']) != 'undefined') {
32755             attr.uiProvider = this.uiProviders['default'];
32756         }
32757         
32758         this.fireEvent('create', this, attr);
32759         
32760         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32761         return(attr.leaf ?
32762                         new Roo.tree.TreeNode(attr) :
32763                         new Roo.tree.AsyncTreeNode(attr));
32764     },
32765
32766     processResponse : function(response, node, callback){
32767         var json = response.responseText;
32768         try {
32769             
32770             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32771             if (this.root !== false) {
32772                 o = o[this.root];
32773             }
32774             
32775             for(var i = 0, len = o.length; i < len; i++){
32776                 var n = this.createNode(o[i]);
32777                 if(n){
32778                     node.appendChild(n);
32779                 }
32780             }
32781             if(typeof callback == "function"){
32782                 callback(this, node);
32783             }
32784         }catch(e){
32785             this.handleFailure(response);
32786         }
32787     },
32788
32789     handleResponse : function(response){
32790         this.transId = false;
32791         var a = response.argument;
32792         this.processResponse(response, a.node, a.callback);
32793         this.fireEvent("load", this, a.node, response);
32794     },
32795
32796     handleFailure : function(response){
32797         this.transId = false;
32798         var a = response.argument;
32799         this.fireEvent("loadexception", this, a.node, response);
32800         if(typeof a.callback == "function"){
32801             a.callback(this, a.node);
32802         }
32803     }
32804 });/*
32805  * Based on:
32806  * Ext JS Library 1.1.1
32807  * Copyright(c) 2006-2007, Ext JS, LLC.
32808  *
32809  * Originally Released Under LGPL - original licence link has changed is not relivant.
32810  *
32811  * Fork - LGPL
32812  * <script type="text/javascript">
32813  */
32814
32815 /**
32816 * @class Roo.tree.TreeFilter
32817 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32818 * @param {TreePanel} tree
32819 * @param {Object} config (optional)
32820  */
32821 Roo.tree.TreeFilter = function(tree, config){
32822     this.tree = tree;
32823     this.filtered = {};
32824     Roo.apply(this, config);
32825 };
32826
32827 Roo.tree.TreeFilter.prototype = {
32828     clearBlank:false,
32829     reverse:false,
32830     autoClear:false,
32831     remove:false,
32832
32833      /**
32834      * Filter the data by a specific attribute.
32835      * @param {String/RegExp} value Either string that the attribute value
32836      * should start with or a RegExp to test against the attribute
32837      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32838      * @param {TreeNode} startNode (optional) The node to start the filter at.
32839      */
32840     filter : function(value, attr, startNode){
32841         attr = attr || "text";
32842         var f;
32843         if(typeof value == "string"){
32844             var vlen = value.length;
32845             // auto clear empty filter
32846             if(vlen == 0 && this.clearBlank){
32847                 this.clear();
32848                 return;
32849             }
32850             value = value.toLowerCase();
32851             f = function(n){
32852                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32853             };
32854         }else if(value.exec){ // regex?
32855             f = function(n){
32856                 return value.test(n.attributes[attr]);
32857             };
32858         }else{
32859             throw 'Illegal filter type, must be string or regex';
32860         }
32861         this.filterBy(f, null, startNode);
32862         },
32863
32864     /**
32865      * Filter by a function. The passed function will be called with each
32866      * node in the tree (or from the startNode). If the function returns true, the node is kept
32867      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32868      * @param {Function} fn The filter function
32869      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32870      */
32871     filterBy : function(fn, scope, startNode){
32872         startNode = startNode || this.tree.root;
32873         if(this.autoClear){
32874             this.clear();
32875         }
32876         var af = this.filtered, rv = this.reverse;
32877         var f = function(n){
32878             if(n == startNode){
32879                 return true;
32880             }
32881             if(af[n.id]){
32882                 return false;
32883             }
32884             var m = fn.call(scope || n, n);
32885             if(!m || rv){
32886                 af[n.id] = n;
32887                 n.ui.hide();
32888                 return false;
32889             }
32890             return true;
32891         };
32892         startNode.cascade(f);
32893         if(this.remove){
32894            for(var id in af){
32895                if(typeof id != "function"){
32896                    var n = af[id];
32897                    if(n && n.parentNode){
32898                        n.parentNode.removeChild(n);
32899                    }
32900                }
32901            }
32902         }
32903     },
32904
32905     /**
32906      * Clears the current filter. Note: with the "remove" option
32907      * set a filter cannot be cleared.
32908      */
32909     clear : function(){
32910         var t = this.tree;
32911         var af = this.filtered;
32912         for(var id in af){
32913             if(typeof id != "function"){
32914                 var n = af[id];
32915                 if(n){
32916                     n.ui.show();
32917                 }
32918             }
32919         }
32920         this.filtered = {};
32921     }
32922 };
32923 /*
32924  * Based on:
32925  * Ext JS Library 1.1.1
32926  * Copyright(c) 2006-2007, Ext JS, LLC.
32927  *
32928  * Originally Released Under LGPL - original licence link has changed is not relivant.
32929  *
32930  * Fork - LGPL
32931  * <script type="text/javascript">
32932  */
32933  
32934
32935 /**
32936  * @class Roo.tree.TreeSorter
32937  * Provides sorting of nodes in a TreePanel
32938  * 
32939  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32940  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32941  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32942  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32943  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32944  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32945  * @constructor
32946  * @param {TreePanel} tree
32947  * @param {Object} config
32948  */
32949 Roo.tree.TreeSorter = function(tree, config){
32950     Roo.apply(this, config);
32951     tree.on("beforechildrenrendered", this.doSort, this);
32952     tree.on("append", this.updateSort, this);
32953     tree.on("insert", this.updateSort, this);
32954     
32955     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32956     var p = this.property || "text";
32957     var sortType = this.sortType;
32958     var fs = this.folderSort;
32959     var cs = this.caseSensitive === true;
32960     var leafAttr = this.leafAttr || 'leaf';
32961
32962     this.sortFn = function(n1, n2){
32963         if(fs){
32964             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32965                 return 1;
32966             }
32967             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32968                 return -1;
32969             }
32970         }
32971         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32972         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32973         if(v1 < v2){
32974                         return dsc ? +1 : -1;
32975                 }else if(v1 > v2){
32976                         return dsc ? -1 : +1;
32977         }else{
32978                 return 0;
32979         }
32980     };
32981 };
32982
32983 Roo.tree.TreeSorter.prototype = {
32984     doSort : function(node){
32985         node.sort(this.sortFn);
32986     },
32987     
32988     compareNodes : function(n1, n2){
32989         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32990     },
32991     
32992     updateSort : function(tree, node){
32993         if(node.childrenRendered){
32994             this.doSort.defer(1, this, [node]);
32995         }
32996     }
32997 };/*
32998  * Based on:
32999  * Ext JS Library 1.1.1
33000  * Copyright(c) 2006-2007, Ext JS, LLC.
33001  *
33002  * Originally Released Under LGPL - original licence link has changed is not relivant.
33003  *
33004  * Fork - LGPL
33005  * <script type="text/javascript">
33006  */
33007
33008 if(Roo.dd.DropZone){
33009     
33010 Roo.tree.TreeDropZone = function(tree, config){
33011     this.allowParentInsert = false;
33012     this.allowContainerDrop = false;
33013     this.appendOnly = false;
33014     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33015     this.tree = tree;
33016     this.lastInsertClass = "x-tree-no-status";
33017     this.dragOverData = {};
33018 };
33019
33020 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33021     ddGroup : "TreeDD",
33022     
33023     expandDelay : 1000,
33024     
33025     expandNode : function(node){
33026         if(node.hasChildNodes() && !node.isExpanded()){
33027             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33028         }
33029     },
33030     
33031     queueExpand : function(node){
33032         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33033     },
33034     
33035     cancelExpand : function(){
33036         if(this.expandProcId){
33037             clearTimeout(this.expandProcId);
33038             this.expandProcId = false;
33039         }
33040     },
33041     
33042     isValidDropPoint : function(n, pt, dd, e, data){
33043         if(!n || !data){ return false; }
33044         var targetNode = n.node;
33045         var dropNode = data.node;
33046         // default drop rules
33047         if(!(targetNode && targetNode.isTarget && pt)){
33048             return false;
33049         }
33050         if(pt == "append" && targetNode.allowChildren === false){
33051             return false;
33052         }
33053         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33054             return false;
33055         }
33056         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33057             return false;
33058         }
33059         // reuse the object
33060         var overEvent = this.dragOverData;
33061         overEvent.tree = this.tree;
33062         overEvent.target = targetNode;
33063         overEvent.data = data;
33064         overEvent.point = pt;
33065         overEvent.source = dd;
33066         overEvent.rawEvent = e;
33067         overEvent.dropNode = dropNode;
33068         overEvent.cancel = false;  
33069         var result = this.tree.fireEvent("nodedragover", overEvent);
33070         return overEvent.cancel === false && result !== false;
33071     },
33072     
33073     getDropPoint : function(e, n, dd){
33074         var tn = n.node;
33075         if(tn.isRoot){
33076             return tn.allowChildren !== false ? "append" : false; // always append for root
33077         }
33078         var dragEl = n.ddel;
33079         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33080         var y = Roo.lib.Event.getPageY(e);
33081         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33082         
33083         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33084         var noAppend = tn.allowChildren === false;
33085         if(this.appendOnly || tn.parentNode.allowChildren === false){
33086             return noAppend ? false : "append";
33087         }
33088         var noBelow = false;
33089         if(!this.allowParentInsert){
33090             noBelow = tn.hasChildNodes() && tn.isExpanded();
33091         }
33092         var q = (b - t) / (noAppend ? 2 : 3);
33093         if(y >= t && y < (t + q)){
33094             return "above";
33095         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33096             return "below";
33097         }else{
33098             return "append";
33099         }
33100     },
33101     
33102     onNodeEnter : function(n, dd, e, data){
33103         this.cancelExpand();
33104     },
33105     
33106     onNodeOver : function(n, dd, e, data){
33107         var pt = this.getDropPoint(e, n, dd);
33108         var node = n.node;
33109         
33110         // auto node expand check
33111         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33112             this.queueExpand(node);
33113         }else if(pt != "append"){
33114             this.cancelExpand();
33115         }
33116         
33117         // set the insert point style on the target node
33118         var returnCls = this.dropNotAllowed;
33119         if(this.isValidDropPoint(n, pt, dd, e, data)){
33120            if(pt){
33121                var el = n.ddel;
33122                var cls;
33123                if(pt == "above"){
33124                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33125                    cls = "x-tree-drag-insert-above";
33126                }else if(pt == "below"){
33127                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33128                    cls = "x-tree-drag-insert-below";
33129                }else{
33130                    returnCls = "x-tree-drop-ok-append";
33131                    cls = "x-tree-drag-append";
33132                }
33133                if(this.lastInsertClass != cls){
33134                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33135                    this.lastInsertClass = cls;
33136                }
33137            }
33138        }
33139        return returnCls;
33140     },
33141     
33142     onNodeOut : function(n, dd, e, data){
33143         this.cancelExpand();
33144         this.removeDropIndicators(n);
33145     },
33146     
33147     onNodeDrop : function(n, dd, e, data){
33148         var point = this.getDropPoint(e, n, dd);
33149         var targetNode = n.node;
33150         targetNode.ui.startDrop();
33151         if(!this.isValidDropPoint(n, point, dd, e, data)){
33152             targetNode.ui.endDrop();
33153             return false;
33154         }
33155         // first try to find the drop node
33156         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33157         var dropEvent = {
33158             tree : this.tree,
33159             target: targetNode,
33160             data: data,
33161             point: point,
33162             source: dd,
33163             rawEvent: e,
33164             dropNode: dropNode,
33165             cancel: !dropNode   
33166         };
33167         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33168         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33169             targetNode.ui.endDrop();
33170             return false;
33171         }
33172         // allow target changing
33173         targetNode = dropEvent.target;
33174         if(point == "append" && !targetNode.isExpanded()){
33175             targetNode.expand(false, null, function(){
33176                 this.completeDrop(dropEvent);
33177             }.createDelegate(this));
33178         }else{
33179             this.completeDrop(dropEvent);
33180         }
33181         return true;
33182     },
33183     
33184     completeDrop : function(de){
33185         var ns = de.dropNode, p = de.point, t = de.target;
33186         if(!(ns instanceof Array)){
33187             ns = [ns];
33188         }
33189         var n;
33190         for(var i = 0, len = ns.length; i < len; i++){
33191             n = ns[i];
33192             if(p == "above"){
33193                 t.parentNode.insertBefore(n, t);
33194             }else if(p == "below"){
33195                 t.parentNode.insertBefore(n, t.nextSibling);
33196             }else{
33197                 t.appendChild(n);
33198             }
33199         }
33200         n.ui.focus();
33201         if(this.tree.hlDrop){
33202             n.ui.highlight();
33203         }
33204         t.ui.endDrop();
33205         this.tree.fireEvent("nodedrop", de);
33206     },
33207     
33208     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33209         if(this.tree.hlDrop){
33210             dropNode.ui.focus();
33211             dropNode.ui.highlight();
33212         }
33213         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33214     },
33215     
33216     getTree : function(){
33217         return this.tree;
33218     },
33219     
33220     removeDropIndicators : function(n){
33221         if(n && n.ddel){
33222             var el = n.ddel;
33223             Roo.fly(el).removeClass([
33224                     "x-tree-drag-insert-above",
33225                     "x-tree-drag-insert-below",
33226                     "x-tree-drag-append"]);
33227             this.lastInsertClass = "_noclass";
33228         }
33229     },
33230     
33231     beforeDragDrop : function(target, e, id){
33232         this.cancelExpand();
33233         return true;
33234     },
33235     
33236     afterRepair : function(data){
33237         if(data && Roo.enableFx){
33238             data.node.ui.highlight();
33239         }
33240         this.hideProxy();
33241     }    
33242 });
33243
33244 }
33245 /*
33246  * Based on:
33247  * Ext JS Library 1.1.1
33248  * Copyright(c) 2006-2007, Ext JS, LLC.
33249  *
33250  * Originally Released Under LGPL - original licence link has changed is not relivant.
33251  *
33252  * Fork - LGPL
33253  * <script type="text/javascript">
33254  */
33255  
33256
33257 if(Roo.dd.DragZone){
33258 Roo.tree.TreeDragZone = function(tree, config){
33259     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33260     this.tree = tree;
33261 };
33262
33263 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33264     ddGroup : "TreeDD",
33265     
33266     onBeforeDrag : function(data, e){
33267         var n = data.node;
33268         return n && n.draggable && !n.disabled;
33269     },
33270     
33271     onInitDrag : function(e){
33272         var data = this.dragData;
33273         this.tree.getSelectionModel().select(data.node);
33274         this.proxy.update("");
33275         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33276         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33277     },
33278     
33279     getRepairXY : function(e, data){
33280         return data.node.ui.getDDRepairXY();
33281     },
33282     
33283     onEndDrag : function(data, e){
33284         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33285     },
33286     
33287     onValidDrop : function(dd, e, id){
33288         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33289         this.hideProxy();
33290     },
33291     
33292     beforeInvalidDrop : function(e, id){
33293         // this scrolls the original position back into view
33294         var sm = this.tree.getSelectionModel();
33295         sm.clearSelections();
33296         sm.select(this.dragData.node);
33297     }
33298 });
33299 }/*
33300  * Based on:
33301  * Ext JS Library 1.1.1
33302  * Copyright(c) 2006-2007, Ext JS, LLC.
33303  *
33304  * Originally Released Under LGPL - original licence link has changed is not relivant.
33305  *
33306  * Fork - LGPL
33307  * <script type="text/javascript">
33308  */
33309 /**
33310  * @class Roo.tree.TreeEditor
33311  * @extends Roo.Editor
33312  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33313  * as the editor field.
33314  * @constructor
33315  * @param {TreePanel} tree
33316  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33317  */
33318 Roo.tree.TreeEditor = function(tree, config){
33319     config = config || {};
33320     var field = config.events ? config : new Roo.form.TextField(config);
33321     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33322
33323     this.tree = tree;
33324
33325     tree.on('beforeclick', this.beforeNodeClick, this);
33326     tree.getTreeEl().on('mousedown', this.hide, this);
33327     this.on('complete', this.updateNode, this);
33328     this.on('beforestartedit', this.fitToTree, this);
33329     this.on('startedit', this.bindScroll, this, {delay:10});
33330     this.on('specialkey', this.onSpecialKey, this);
33331 };
33332
33333 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33334     /**
33335      * @cfg {String} alignment
33336      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33337      */
33338     alignment: "l-l",
33339     // inherit
33340     autoSize: false,
33341     /**
33342      * @cfg {Boolean} hideEl
33343      * True to hide the bound element while the editor is displayed (defaults to false)
33344      */
33345     hideEl : false,
33346     /**
33347      * @cfg {String} cls
33348      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33349      */
33350     cls: "x-small-editor x-tree-editor",
33351     /**
33352      * @cfg {Boolean} shim
33353      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33354      */
33355     shim:false,
33356     // inherit
33357     shadow:"frame",
33358     /**
33359      * @cfg {Number} maxWidth
33360      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33361      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33362      * scroll and client offsets into account prior to each edit.
33363      */
33364     maxWidth: 250,
33365
33366     editDelay : 350,
33367
33368     // private
33369     fitToTree : function(ed, el){
33370         var td = this.tree.getTreeEl().dom, nd = el.dom;
33371         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33372             td.scrollLeft = nd.offsetLeft;
33373         }
33374         var w = Math.min(
33375                 this.maxWidth,
33376                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33377         this.setSize(w, '');
33378     },
33379
33380     // private
33381     triggerEdit : function(node){
33382         this.completeEdit();
33383         this.editNode = node;
33384         this.startEdit(node.ui.textNode, node.text);
33385     },
33386
33387     // private
33388     bindScroll : function(){
33389         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33390     },
33391
33392     // private
33393     beforeNodeClick : function(node, e){
33394         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33395         this.lastClick = new Date();
33396         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33397             e.stopEvent();
33398             this.triggerEdit(node);
33399             return false;
33400         }
33401     },
33402
33403     // private
33404     updateNode : function(ed, value){
33405         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33406         this.editNode.setText(value);
33407     },
33408
33409     // private
33410     onHide : function(){
33411         Roo.tree.TreeEditor.superclass.onHide.call(this);
33412         if(this.editNode){
33413             this.editNode.ui.focus();
33414         }
33415     },
33416
33417     // private
33418     onSpecialKey : function(field, e){
33419         var k = e.getKey();
33420         if(k == e.ESC){
33421             e.stopEvent();
33422             this.cancelEdit();
33423         }else if(k == e.ENTER && !e.hasModifier()){
33424             e.stopEvent();
33425             this.completeEdit();
33426         }
33427     }
33428 });//<Script type="text/javascript">
33429 /*
33430  * Based on:
33431  * Ext JS Library 1.1.1
33432  * Copyright(c) 2006-2007, Ext JS, LLC.
33433  *
33434  * Originally Released Under LGPL - original licence link has changed is not relivant.
33435  *
33436  * Fork - LGPL
33437  * <script type="text/javascript">
33438  */
33439  
33440 /**
33441  * Not documented??? - probably should be...
33442  */
33443
33444 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33445     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33446     
33447     renderElements : function(n, a, targetNode, bulkRender){
33448         //consel.log("renderElements?");
33449         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33450
33451         var t = n.getOwnerTree();
33452         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33453         
33454         var cols = t.columns;
33455         var bw = t.borderWidth;
33456         var c = cols[0];
33457         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33458          var cb = typeof a.checked == "boolean";
33459         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33460         var colcls = 'x-t-' + tid + '-c0';
33461         var buf = [
33462             '<li class="x-tree-node">',
33463             
33464                 
33465                 '<div class="x-tree-node-el ', a.cls,'">',
33466                     // extran...
33467                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33468                 
33469                 
33470                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33471                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33472                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33473                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33474                            (a.iconCls ? ' '+a.iconCls : ''),
33475                            '" unselectable="on" />',
33476                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33477                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33478                              
33479                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33480                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33481                             '<span unselectable="on" qtip="' + tx + '">',
33482                              tx,
33483                              '</span></a>' ,
33484                     '</div>',
33485                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33486                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33487                  ];
33488         for(var i = 1, len = cols.length; i < len; i++){
33489             c = cols[i];
33490             colcls = 'x-t-' + tid + '-c' +i;
33491             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33492             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33493                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33494                       "</div>");
33495          }
33496          
33497          buf.push(
33498             '</a>',
33499             '<div class="x-clear"></div></div>',
33500             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33501             "</li>");
33502         
33503         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33504             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33505                                 n.nextSibling.ui.getEl(), buf.join(""));
33506         }else{
33507             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33508         }
33509         var el = this.wrap.firstChild;
33510         this.elRow = el;
33511         this.elNode = el.firstChild;
33512         this.ranchor = el.childNodes[1];
33513         this.ctNode = this.wrap.childNodes[1];
33514         var cs = el.firstChild.childNodes;
33515         this.indentNode = cs[0];
33516         this.ecNode = cs[1];
33517         this.iconNode = cs[2];
33518         var index = 3;
33519         if(cb){
33520             this.checkbox = cs[3];
33521             index++;
33522         }
33523         this.anchor = cs[index];
33524         
33525         this.textNode = cs[index].firstChild;
33526         
33527         //el.on("click", this.onClick, this);
33528         //el.on("dblclick", this.onDblClick, this);
33529         
33530         
33531        // console.log(this);
33532     },
33533     initEvents : function(){
33534         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33535         
33536             
33537         var a = this.ranchor;
33538
33539         var el = Roo.get(a);
33540
33541         if(Roo.isOpera){ // opera render bug ignores the CSS
33542             el.setStyle("text-decoration", "none");
33543         }
33544
33545         el.on("click", this.onClick, this);
33546         el.on("dblclick", this.onDblClick, this);
33547         el.on("contextmenu", this.onContextMenu, this);
33548         
33549     },
33550     
33551     /*onSelectedChange : function(state){
33552         if(state){
33553             this.focus();
33554             this.addClass("x-tree-selected");
33555         }else{
33556             //this.blur();
33557             this.removeClass("x-tree-selected");
33558         }
33559     },*/
33560     addClass : function(cls){
33561         if(this.elRow){
33562             Roo.fly(this.elRow).addClass(cls);
33563         }
33564         
33565     },
33566     
33567     
33568     removeClass : function(cls){
33569         if(this.elRow){
33570             Roo.fly(this.elRow).removeClass(cls);
33571         }
33572     }
33573
33574     
33575     
33576 });//<Script type="text/javascript">
33577
33578 /*
33579  * Based on:
33580  * Ext JS Library 1.1.1
33581  * Copyright(c) 2006-2007, Ext JS, LLC.
33582  *
33583  * Originally Released Under LGPL - original licence link has changed is not relivant.
33584  *
33585  * Fork - LGPL
33586  * <script type="text/javascript">
33587  */
33588  
33589
33590 /**
33591  * @class Roo.tree.ColumnTree
33592  * @extends Roo.data.TreePanel
33593  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33594  * @cfg {int} borderWidth  compined right/left border allowance
33595  * @constructor
33596  * @param {String/HTMLElement/Element} el The container element
33597  * @param {Object} config
33598  */
33599 Roo.tree.ColumnTree =  function(el, config)
33600 {
33601    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33602    this.addEvents({
33603         /**
33604         * @event resize
33605         * Fire this event on a container when it resizes
33606         * @param {int} w Width
33607         * @param {int} h Height
33608         */
33609        "resize" : true
33610     });
33611     this.on('resize', this.onResize, this);
33612 };
33613
33614 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33615     //lines:false,
33616     
33617     
33618     borderWidth: Roo.isBorderBox ? 0 : 2, 
33619     headEls : false,
33620     
33621     render : function(){
33622         // add the header.....
33623        
33624         Roo.tree.ColumnTree.superclass.render.apply(this);
33625         
33626         this.el.addClass('x-column-tree');
33627         
33628         this.headers = this.el.createChild(
33629             {cls:'x-tree-headers'},this.innerCt.dom);
33630    
33631         var cols = this.columns, c;
33632         var totalWidth = 0;
33633         this.headEls = [];
33634         var  len = cols.length;
33635         for(var i = 0; i < len; i++){
33636              c = cols[i];
33637              totalWidth += c.width;
33638             this.headEls.push(this.headers.createChild({
33639                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33640                  cn: {
33641                      cls:'x-tree-hd-text',
33642                      html: c.header
33643                  },
33644                  style:'width:'+(c.width-this.borderWidth)+'px;'
33645              }));
33646         }
33647         this.headers.createChild({cls:'x-clear'});
33648         // prevent floats from wrapping when clipped
33649         this.headers.setWidth(totalWidth);
33650         //this.innerCt.setWidth(totalWidth);
33651         this.innerCt.setStyle({ overflow: 'auto' });
33652         this.onResize(this.width, this.height);
33653              
33654         
33655     },
33656     onResize : function(w,h)
33657     {
33658         this.height = h;
33659         this.width = w;
33660         // resize cols..
33661         this.innerCt.setWidth(this.width);
33662         this.innerCt.setHeight(this.height-20);
33663         
33664         // headers...
33665         var cols = this.columns, c;
33666         var totalWidth = 0;
33667         var expEl = false;
33668         var len = cols.length;
33669         for(var i = 0; i < len; i++){
33670             c = cols[i];
33671             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33672                 // it's the expander..
33673                 expEl  = this.headEls[i];
33674                 continue;
33675             }
33676             totalWidth += c.width;
33677             
33678         }
33679         if (expEl) {
33680             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33681         }
33682         this.headers.setWidth(w-20);
33683
33684         
33685         
33686         
33687     }
33688 });
33689 /*
33690  * Based on:
33691  * Ext JS Library 1.1.1
33692  * Copyright(c) 2006-2007, Ext JS, LLC.
33693  *
33694  * Originally Released Under LGPL - original licence link has changed is not relivant.
33695  *
33696  * Fork - LGPL
33697  * <script type="text/javascript">
33698  */
33699  
33700 /**
33701  * @class Roo.menu.Menu
33702  * @extends Roo.util.Observable
33703  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33704  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33705  * @constructor
33706  * Creates a new Menu
33707  * @param {Object} config Configuration options
33708  */
33709 Roo.menu.Menu = function(config){
33710     Roo.apply(this, config);
33711     this.id = this.id || Roo.id();
33712     this.addEvents({
33713         /**
33714          * @event beforeshow
33715          * Fires before this menu is displayed
33716          * @param {Roo.menu.Menu} this
33717          */
33718         beforeshow : true,
33719         /**
33720          * @event beforehide
33721          * Fires before this menu is hidden
33722          * @param {Roo.menu.Menu} this
33723          */
33724         beforehide : true,
33725         /**
33726          * @event show
33727          * Fires after this menu is displayed
33728          * @param {Roo.menu.Menu} this
33729          */
33730         show : true,
33731         /**
33732          * @event hide
33733          * Fires after this menu is hidden
33734          * @param {Roo.menu.Menu} this
33735          */
33736         hide : true,
33737         /**
33738          * @event click
33739          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33740          * @param {Roo.menu.Menu} this
33741          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33742          * @param {Roo.EventObject} e
33743          */
33744         click : true,
33745         /**
33746          * @event mouseover
33747          * Fires when the mouse is hovering over this menu
33748          * @param {Roo.menu.Menu} this
33749          * @param {Roo.EventObject} e
33750          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33751          */
33752         mouseover : true,
33753         /**
33754          * @event mouseout
33755          * Fires when the mouse exits this menu
33756          * @param {Roo.menu.Menu} this
33757          * @param {Roo.EventObject} e
33758          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33759          */
33760         mouseout : true,
33761         /**
33762          * @event itemclick
33763          * Fires when a menu item contained in this menu is clicked
33764          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33765          * @param {Roo.EventObject} e
33766          */
33767         itemclick: true
33768     });
33769     if (this.registerMenu) {
33770         Roo.menu.MenuMgr.register(this);
33771     }
33772     
33773     var mis = this.items;
33774     this.items = new Roo.util.MixedCollection();
33775     if(mis){
33776         this.add.apply(this, mis);
33777     }
33778 };
33779
33780 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33781     /**
33782      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33783      */
33784     minWidth : 120,
33785     /**
33786      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33787      * for bottom-right shadow (defaults to "sides")
33788      */
33789     shadow : "sides",
33790     /**
33791      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33792      * this menu (defaults to "tl-tr?")
33793      */
33794     subMenuAlign : "tl-tr?",
33795     /**
33796      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33797      * relative to its element of origin (defaults to "tl-bl?")
33798      */
33799     defaultAlign : "tl-bl?",
33800     /**
33801      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33802      */
33803     allowOtherMenus : false,
33804     /**
33805      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33806      */
33807     registerMenu : true,
33808
33809     hidden:true,
33810
33811     // private
33812     render : function(){
33813         if(this.el){
33814             return;
33815         }
33816         var el = this.el = new Roo.Layer({
33817             cls: "x-menu",
33818             shadow:this.shadow,
33819             constrain: false,
33820             parentEl: this.parentEl || document.body,
33821             zindex:15000
33822         });
33823
33824         this.keyNav = new Roo.menu.MenuNav(this);
33825
33826         if(this.plain){
33827             el.addClass("x-menu-plain");
33828         }
33829         if(this.cls){
33830             el.addClass(this.cls);
33831         }
33832         // generic focus element
33833         this.focusEl = el.createChild({
33834             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33835         });
33836         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33837         ul.on("click", this.onClick, this);
33838         ul.on("mouseover", this.onMouseOver, this);
33839         ul.on("mouseout", this.onMouseOut, this);
33840         this.items.each(function(item){
33841             var li = document.createElement("li");
33842             li.className = "x-menu-list-item";
33843             ul.dom.appendChild(li);
33844             item.render(li, this);
33845         }, this);
33846         this.ul = ul;
33847         this.autoWidth();
33848     },
33849
33850     // private
33851     autoWidth : function(){
33852         var el = this.el, ul = this.ul;
33853         if(!el){
33854             return;
33855         }
33856         var w = this.width;
33857         if(w){
33858             el.setWidth(w);
33859         }else if(Roo.isIE){
33860             el.setWidth(this.minWidth);
33861             var t = el.dom.offsetWidth; // force recalc
33862             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33863         }
33864     },
33865
33866     // private
33867     delayAutoWidth : function(){
33868         if(this.rendered){
33869             if(!this.awTask){
33870                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33871             }
33872             this.awTask.delay(20);
33873         }
33874     },
33875
33876     // private
33877     findTargetItem : function(e){
33878         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33879         if(t && t.menuItemId){
33880             return this.items.get(t.menuItemId);
33881         }
33882     },
33883
33884     // private
33885     onClick : function(e){
33886         var t;
33887         if(t = this.findTargetItem(e)){
33888             t.onClick(e);
33889             this.fireEvent("click", this, t, e);
33890         }
33891     },
33892
33893     // private
33894     setActiveItem : function(item, autoExpand){
33895         if(item != this.activeItem){
33896             if(this.activeItem){
33897                 this.activeItem.deactivate();
33898             }
33899             this.activeItem = item;
33900             item.activate(autoExpand);
33901         }else if(autoExpand){
33902             item.expandMenu();
33903         }
33904     },
33905
33906     // private
33907     tryActivate : function(start, step){
33908         var items = this.items;
33909         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33910             var item = items.get(i);
33911             if(!item.disabled && item.canActivate){
33912                 this.setActiveItem(item, false);
33913                 return item;
33914             }
33915         }
33916         return false;
33917     },
33918
33919     // private
33920     onMouseOver : function(e){
33921         var t;
33922         if(t = this.findTargetItem(e)){
33923             if(t.canActivate && !t.disabled){
33924                 this.setActiveItem(t, true);
33925             }
33926         }
33927         this.fireEvent("mouseover", this, e, t);
33928     },
33929
33930     // private
33931     onMouseOut : function(e){
33932         var t;
33933         if(t = this.findTargetItem(e)){
33934             if(t == this.activeItem && t.shouldDeactivate(e)){
33935                 this.activeItem.deactivate();
33936                 delete this.activeItem;
33937             }
33938         }
33939         this.fireEvent("mouseout", this, e, t);
33940     },
33941
33942     /**
33943      * Read-only.  Returns true if the menu is currently displayed, else false.
33944      * @type Boolean
33945      */
33946     isVisible : function(){
33947         return this.el && !this.hidden;
33948     },
33949
33950     /**
33951      * Displays this menu relative to another element
33952      * @param {String/HTMLElement/Roo.Element} element The element to align to
33953      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33954      * the element (defaults to this.defaultAlign)
33955      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33956      */
33957     show : function(el, pos, parentMenu){
33958         this.parentMenu = parentMenu;
33959         if(!this.el){
33960             this.render();
33961         }
33962         this.fireEvent("beforeshow", this);
33963         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33964     },
33965
33966     /**
33967      * Displays this menu at a specific xy position
33968      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33969      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33970      */
33971     showAt : function(xy, parentMenu, /* private: */_e){
33972         this.parentMenu = parentMenu;
33973         if(!this.el){
33974             this.render();
33975         }
33976         if(_e !== false){
33977             this.fireEvent("beforeshow", this);
33978             xy = this.el.adjustForConstraints(xy);
33979         }
33980         this.el.setXY(xy);
33981         this.el.show();
33982         this.hidden = false;
33983         this.focus();
33984         this.fireEvent("show", this);
33985     },
33986
33987     focus : function(){
33988         if(!this.hidden){
33989             this.doFocus.defer(50, this);
33990         }
33991     },
33992
33993     doFocus : function(){
33994         if(!this.hidden){
33995             this.focusEl.focus();
33996         }
33997     },
33998
33999     /**
34000      * Hides this menu and optionally all parent menus
34001      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34002      */
34003     hide : function(deep){
34004         if(this.el && this.isVisible()){
34005             this.fireEvent("beforehide", this);
34006             if(this.activeItem){
34007                 this.activeItem.deactivate();
34008                 this.activeItem = null;
34009             }
34010             this.el.hide();
34011             this.hidden = true;
34012             this.fireEvent("hide", this);
34013         }
34014         if(deep === true && this.parentMenu){
34015             this.parentMenu.hide(true);
34016         }
34017     },
34018
34019     /**
34020      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34021      * Any of the following are valid:
34022      * <ul>
34023      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34024      * <li>An HTMLElement object which will be converted to a menu item</li>
34025      * <li>A menu item config object that will be created as a new menu item</li>
34026      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34027      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34028      * </ul>
34029      * Usage:
34030      * <pre><code>
34031 // Create the menu
34032 var menu = new Roo.menu.Menu();
34033
34034 // Create a menu item to add by reference
34035 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34036
34037 // Add a bunch of items at once using different methods.
34038 // Only the last item added will be returned.
34039 var item = menu.add(
34040     menuItem,                // add existing item by ref
34041     'Dynamic Item',          // new TextItem
34042     '-',                     // new separator
34043     { text: 'Config Item' }  // new item by config
34044 );
34045 </code></pre>
34046      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34047      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34048      */
34049     add : function(){
34050         var a = arguments, l = a.length, item;
34051         for(var i = 0; i < l; i++){
34052             var el = a[i];
34053             if ((typeof(el) == "object") && el.xtype && el.xns) {
34054                 el = Roo.factory(el, Roo.menu);
34055             }
34056             
34057             if(el.render){ // some kind of Item
34058                 item = this.addItem(el);
34059             }else if(typeof el == "string"){ // string
34060                 if(el == "separator" || el == "-"){
34061                     item = this.addSeparator();
34062                 }else{
34063                     item = this.addText(el);
34064                 }
34065             }else if(el.tagName || el.el){ // element
34066                 item = this.addElement(el);
34067             }else if(typeof el == "object"){ // must be menu item config?
34068                 item = this.addMenuItem(el);
34069             }
34070         }
34071         return item;
34072     },
34073
34074     /**
34075      * Returns this menu's underlying {@link Roo.Element} object
34076      * @return {Roo.Element} The element
34077      */
34078     getEl : function(){
34079         if(!this.el){
34080             this.render();
34081         }
34082         return this.el;
34083     },
34084
34085     /**
34086      * Adds a separator bar to the menu
34087      * @return {Roo.menu.Item} The menu item that was added
34088      */
34089     addSeparator : function(){
34090         return this.addItem(new Roo.menu.Separator());
34091     },
34092
34093     /**
34094      * Adds an {@link Roo.Element} object to the menu
34095      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34096      * @return {Roo.menu.Item} The menu item that was added
34097      */
34098     addElement : function(el){
34099         return this.addItem(new Roo.menu.BaseItem(el));
34100     },
34101
34102     /**
34103      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34104      * @param {Roo.menu.Item} item The menu item to add
34105      * @return {Roo.menu.Item} The menu item that was added
34106      */
34107     addItem : function(item){
34108         this.items.add(item);
34109         if(this.ul){
34110             var li = document.createElement("li");
34111             li.className = "x-menu-list-item";
34112             this.ul.dom.appendChild(li);
34113             item.render(li, this);
34114             this.delayAutoWidth();
34115         }
34116         return item;
34117     },
34118
34119     /**
34120      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34121      * @param {Object} config A MenuItem config object
34122      * @return {Roo.menu.Item} The menu item that was added
34123      */
34124     addMenuItem : function(config){
34125         if(!(config instanceof Roo.menu.Item)){
34126             if(typeof config.checked == "boolean"){ // must be check menu item config?
34127                 config = new Roo.menu.CheckItem(config);
34128             }else{
34129                 config = new Roo.menu.Item(config);
34130             }
34131         }
34132         return this.addItem(config);
34133     },
34134
34135     /**
34136      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34137      * @param {String} text The text to display in the menu item
34138      * @return {Roo.menu.Item} The menu item that was added
34139      */
34140     addText : function(text){
34141         return this.addItem(new Roo.menu.TextItem({ text : text }));
34142     },
34143
34144     /**
34145      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34146      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34147      * @param {Roo.menu.Item} item The menu item to add
34148      * @return {Roo.menu.Item} The menu item that was added
34149      */
34150     insert : function(index, item){
34151         this.items.insert(index, item);
34152         if(this.ul){
34153             var li = document.createElement("li");
34154             li.className = "x-menu-list-item";
34155             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34156             item.render(li, this);
34157             this.delayAutoWidth();
34158         }
34159         return item;
34160     },
34161
34162     /**
34163      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34164      * @param {Roo.menu.Item} item The menu item to remove
34165      */
34166     remove : function(item){
34167         this.items.removeKey(item.id);
34168         item.destroy();
34169     },
34170
34171     /**
34172      * Removes and destroys all items in the menu
34173      */
34174     removeAll : function(){
34175         var f;
34176         while(f = this.items.first()){
34177             this.remove(f);
34178         }
34179     }
34180 });
34181
34182 // MenuNav is a private utility class used internally by the Menu
34183 Roo.menu.MenuNav = function(menu){
34184     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34185     this.scope = this.menu = menu;
34186 };
34187
34188 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34189     doRelay : function(e, h){
34190         var k = e.getKey();
34191         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34192             this.menu.tryActivate(0, 1);
34193             return false;
34194         }
34195         return h.call(this.scope || this, e, this.menu);
34196     },
34197
34198     up : function(e, m){
34199         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34200             m.tryActivate(m.items.length-1, -1);
34201         }
34202     },
34203
34204     down : function(e, m){
34205         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34206             m.tryActivate(0, 1);
34207         }
34208     },
34209
34210     right : function(e, m){
34211         if(m.activeItem){
34212             m.activeItem.expandMenu(true);
34213         }
34214     },
34215
34216     left : function(e, m){
34217         m.hide();
34218         if(m.parentMenu && m.parentMenu.activeItem){
34219             m.parentMenu.activeItem.activate();
34220         }
34221     },
34222
34223     enter : function(e, m){
34224         if(m.activeItem){
34225             e.stopPropagation();
34226             m.activeItem.onClick(e);
34227             m.fireEvent("click", this, m.activeItem);
34228             return true;
34229         }
34230     }
34231 });/*
34232  * Based on:
34233  * Ext JS Library 1.1.1
34234  * Copyright(c) 2006-2007, Ext JS, LLC.
34235  *
34236  * Originally Released Under LGPL - original licence link has changed is not relivant.
34237  *
34238  * Fork - LGPL
34239  * <script type="text/javascript">
34240  */
34241  
34242 /**
34243  * @class Roo.menu.MenuMgr
34244  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34245  * @singleton
34246  */
34247 Roo.menu.MenuMgr = function(){
34248    var menus, active, groups = {}, attached = false, lastShow = new Date();
34249
34250    // private - called when first menu is created
34251    function init(){
34252        menus = {};
34253        active = new Roo.util.MixedCollection();
34254        Roo.get(document).addKeyListener(27, function(){
34255            if(active.length > 0){
34256                hideAll();
34257            }
34258        });
34259    }
34260
34261    // private
34262    function hideAll(){
34263        if(active && active.length > 0){
34264            var c = active.clone();
34265            c.each(function(m){
34266                m.hide();
34267            });
34268        }
34269    }
34270
34271    // private
34272    function onHide(m){
34273        active.remove(m);
34274        if(active.length < 1){
34275            Roo.get(document).un("mousedown", onMouseDown);
34276            attached = false;
34277        }
34278    }
34279
34280    // private
34281    function onShow(m){
34282        var last = active.last();
34283        lastShow = new Date();
34284        active.add(m);
34285        if(!attached){
34286            Roo.get(document).on("mousedown", onMouseDown);
34287            attached = true;
34288        }
34289        if(m.parentMenu){
34290           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34291           m.parentMenu.activeChild = m;
34292        }else if(last && last.isVisible()){
34293           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34294        }
34295    }
34296
34297    // private
34298    function onBeforeHide(m){
34299        if(m.activeChild){
34300            m.activeChild.hide();
34301        }
34302        if(m.autoHideTimer){
34303            clearTimeout(m.autoHideTimer);
34304            delete m.autoHideTimer;
34305        }
34306    }
34307
34308    // private
34309    function onBeforeShow(m){
34310        var pm = m.parentMenu;
34311        if(!pm && !m.allowOtherMenus){
34312            hideAll();
34313        }else if(pm && pm.activeChild && active != m){
34314            pm.activeChild.hide();
34315        }
34316    }
34317
34318    // private
34319    function onMouseDown(e){
34320        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34321            hideAll();
34322        }
34323    }
34324
34325    // private
34326    function onBeforeCheck(mi, state){
34327        if(state){
34328            var g = groups[mi.group];
34329            for(var i = 0, l = g.length; i < l; i++){
34330                if(g[i] != mi){
34331                    g[i].setChecked(false);
34332                }
34333            }
34334        }
34335    }
34336
34337    return {
34338
34339        /**
34340         * Hides all menus that are currently visible
34341         */
34342        hideAll : function(){
34343             hideAll();  
34344        },
34345
34346        // private
34347        register : function(menu){
34348            if(!menus){
34349                init();
34350            }
34351            menus[menu.id] = menu;
34352            menu.on("beforehide", onBeforeHide);
34353            menu.on("hide", onHide);
34354            menu.on("beforeshow", onBeforeShow);
34355            menu.on("show", onShow);
34356            var g = menu.group;
34357            if(g && menu.events["checkchange"]){
34358                if(!groups[g]){
34359                    groups[g] = [];
34360                }
34361                groups[g].push(menu);
34362                menu.on("checkchange", onCheck);
34363            }
34364        },
34365
34366         /**
34367          * Returns a {@link Roo.menu.Menu} object
34368          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34369          * be used to generate and return a new Menu instance.
34370          */
34371        get : function(menu){
34372            if(typeof menu == "string"){ // menu id
34373                return menus[menu];
34374            }else if(menu.events){  // menu instance
34375                return menu;
34376            }else if(typeof menu.length == 'number'){ // array of menu items?
34377                return new Roo.menu.Menu({items:menu});
34378            }else{ // otherwise, must be a config
34379                return new Roo.menu.Menu(menu);
34380            }
34381        },
34382
34383        // private
34384        unregister : function(menu){
34385            delete menus[menu.id];
34386            menu.un("beforehide", onBeforeHide);
34387            menu.un("hide", onHide);
34388            menu.un("beforeshow", onBeforeShow);
34389            menu.un("show", onShow);
34390            var g = menu.group;
34391            if(g && menu.events["checkchange"]){
34392                groups[g].remove(menu);
34393                menu.un("checkchange", onCheck);
34394            }
34395        },
34396
34397        // private
34398        registerCheckable : function(menuItem){
34399            var g = menuItem.group;
34400            if(g){
34401                if(!groups[g]){
34402                    groups[g] = [];
34403                }
34404                groups[g].push(menuItem);
34405                menuItem.on("beforecheckchange", onBeforeCheck);
34406            }
34407        },
34408
34409        // private
34410        unregisterCheckable : function(menuItem){
34411            var g = menuItem.group;
34412            if(g){
34413                groups[g].remove(menuItem);
34414                menuItem.un("beforecheckchange", onBeforeCheck);
34415            }
34416        }
34417    };
34418 }();/*
34419  * Based on:
34420  * Ext JS Library 1.1.1
34421  * Copyright(c) 2006-2007, Ext JS, LLC.
34422  *
34423  * Originally Released Under LGPL - original licence link has changed is not relivant.
34424  *
34425  * Fork - LGPL
34426  * <script type="text/javascript">
34427  */
34428  
34429
34430 /**
34431  * @class Roo.menu.BaseItem
34432  * @extends Roo.Component
34433  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34434  * management and base configuration options shared by all menu components.
34435  * @constructor
34436  * Creates a new BaseItem
34437  * @param {Object} config Configuration options
34438  */
34439 Roo.menu.BaseItem = function(config){
34440     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34441
34442     this.addEvents({
34443         /**
34444          * @event click
34445          * Fires when this item is clicked
34446          * @param {Roo.menu.BaseItem} this
34447          * @param {Roo.EventObject} e
34448          */
34449         click: true,
34450         /**
34451          * @event activate
34452          * Fires when this item is activated
34453          * @param {Roo.menu.BaseItem} this
34454          */
34455         activate : true,
34456         /**
34457          * @event deactivate
34458          * Fires when this item is deactivated
34459          * @param {Roo.menu.BaseItem} this
34460          */
34461         deactivate : true
34462     });
34463
34464     if(this.handler){
34465         this.on("click", this.handler, this.scope, true);
34466     }
34467 };
34468
34469 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34470     /**
34471      * @cfg {Function} handler
34472      * A function that will handle the click event of this menu item (defaults to undefined)
34473      */
34474     /**
34475      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34476      */
34477     canActivate : false,
34478     /**
34479      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34480      */
34481     activeClass : "x-menu-item-active",
34482     /**
34483      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34484      */
34485     hideOnClick : true,
34486     /**
34487      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34488      */
34489     hideDelay : 100,
34490
34491     // private
34492     ctype: "Roo.menu.BaseItem",
34493
34494     // private
34495     actionMode : "container",
34496
34497     // private
34498     render : function(container, parentMenu){
34499         this.parentMenu = parentMenu;
34500         Roo.menu.BaseItem.superclass.render.call(this, container);
34501         this.container.menuItemId = this.id;
34502     },
34503
34504     // private
34505     onRender : function(container, position){
34506         this.el = Roo.get(this.el);
34507         container.dom.appendChild(this.el.dom);
34508     },
34509
34510     // private
34511     onClick : function(e){
34512         if(!this.disabled && this.fireEvent("click", this, e) !== false
34513                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34514             this.handleClick(e);
34515         }else{
34516             e.stopEvent();
34517         }
34518     },
34519
34520     // private
34521     activate : function(){
34522         if(this.disabled){
34523             return false;
34524         }
34525         var li = this.container;
34526         li.addClass(this.activeClass);
34527         this.region = li.getRegion().adjust(2, 2, -2, -2);
34528         this.fireEvent("activate", this);
34529         return true;
34530     },
34531
34532     // private
34533     deactivate : function(){
34534         this.container.removeClass(this.activeClass);
34535         this.fireEvent("deactivate", this);
34536     },
34537
34538     // private
34539     shouldDeactivate : function(e){
34540         return !this.region || !this.region.contains(e.getPoint());
34541     },
34542
34543     // private
34544     handleClick : function(e){
34545         if(this.hideOnClick){
34546             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34547         }
34548     },
34549
34550     // private
34551     expandMenu : function(autoActivate){
34552         // do nothing
34553     },
34554
34555     // private
34556     hideMenu : function(){
34557         // do nothing
34558     }
34559 });/*
34560  * Based on:
34561  * Ext JS Library 1.1.1
34562  * Copyright(c) 2006-2007, Ext JS, LLC.
34563  *
34564  * Originally Released Under LGPL - original licence link has changed is not relivant.
34565  *
34566  * Fork - LGPL
34567  * <script type="text/javascript">
34568  */
34569  
34570 /**
34571  * @class Roo.menu.Adapter
34572  * @extends Roo.menu.BaseItem
34573  * 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.
34574  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34575  * @constructor
34576  * Creates a new Adapter
34577  * @param {Object} config Configuration options
34578  */
34579 Roo.menu.Adapter = function(component, config){
34580     Roo.menu.Adapter.superclass.constructor.call(this, config);
34581     this.component = component;
34582 };
34583 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34584     // private
34585     canActivate : true,
34586
34587     // private
34588     onRender : function(container, position){
34589         this.component.render(container);
34590         this.el = this.component.getEl();
34591     },
34592
34593     // private
34594     activate : function(){
34595         if(this.disabled){
34596             return false;
34597         }
34598         this.component.focus();
34599         this.fireEvent("activate", this);
34600         return true;
34601     },
34602
34603     // private
34604     deactivate : function(){
34605         this.fireEvent("deactivate", this);
34606     },
34607
34608     // private
34609     disable : function(){
34610         this.component.disable();
34611         Roo.menu.Adapter.superclass.disable.call(this);
34612     },
34613
34614     // private
34615     enable : function(){
34616         this.component.enable();
34617         Roo.menu.Adapter.superclass.enable.call(this);
34618     }
34619 });/*
34620  * Based on:
34621  * Ext JS Library 1.1.1
34622  * Copyright(c) 2006-2007, Ext JS, LLC.
34623  *
34624  * Originally Released Under LGPL - original licence link has changed is not relivant.
34625  *
34626  * Fork - LGPL
34627  * <script type="text/javascript">
34628  */
34629
34630 /**
34631  * @class Roo.menu.TextItem
34632  * @extends Roo.menu.BaseItem
34633  * Adds a static text string to a menu, usually used as either a heading or group separator.
34634  * Note: old style constructor with text is still supported.
34635  * 
34636  * @constructor
34637  * Creates a new TextItem
34638  * @param {Object} cfg Configuration
34639  */
34640 Roo.menu.TextItem = function(cfg){
34641     if (typeof(cfg) == 'string') {
34642         this.text = cfg;
34643     } else {
34644         Roo.apply(this,cfg);
34645     }
34646     
34647     Roo.menu.TextItem.superclass.constructor.call(this);
34648 };
34649
34650 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34651     /**
34652      * @cfg {Boolean} text Text to show on item.
34653      */
34654     text : '',
34655     
34656     /**
34657      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34658      */
34659     hideOnClick : false,
34660     /**
34661      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34662      */
34663     itemCls : "x-menu-text",
34664
34665     // private
34666     onRender : function(){
34667         var s = document.createElement("span");
34668         s.className = this.itemCls;
34669         s.innerHTML = this.text;
34670         this.el = s;
34671         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34672     }
34673 });/*
34674  * Based on:
34675  * Ext JS Library 1.1.1
34676  * Copyright(c) 2006-2007, Ext JS, LLC.
34677  *
34678  * Originally Released Under LGPL - original licence link has changed is not relivant.
34679  *
34680  * Fork - LGPL
34681  * <script type="text/javascript">
34682  */
34683
34684 /**
34685  * @class Roo.menu.Separator
34686  * @extends Roo.menu.BaseItem
34687  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34688  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34689  * @constructor
34690  * @param {Object} config Configuration options
34691  */
34692 Roo.menu.Separator = function(config){
34693     Roo.menu.Separator.superclass.constructor.call(this, config);
34694 };
34695
34696 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34697     /**
34698      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34699      */
34700     itemCls : "x-menu-sep",
34701     /**
34702      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34703      */
34704     hideOnClick : false,
34705
34706     // private
34707     onRender : function(li){
34708         var s = document.createElement("span");
34709         s.className = this.itemCls;
34710         s.innerHTML = "&#160;";
34711         this.el = s;
34712         li.addClass("x-menu-sep-li");
34713         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34714     }
34715 });/*
34716  * Based on:
34717  * Ext JS Library 1.1.1
34718  * Copyright(c) 2006-2007, Ext JS, LLC.
34719  *
34720  * Originally Released Under LGPL - original licence link has changed is not relivant.
34721  *
34722  * Fork - LGPL
34723  * <script type="text/javascript">
34724  */
34725 /**
34726  * @class Roo.menu.Item
34727  * @extends Roo.menu.BaseItem
34728  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34729  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34730  * activation and click handling.
34731  * @constructor
34732  * Creates a new Item
34733  * @param {Object} config Configuration options
34734  */
34735 Roo.menu.Item = function(config){
34736     Roo.menu.Item.superclass.constructor.call(this, config);
34737     if(this.menu){
34738         this.menu = Roo.menu.MenuMgr.get(this.menu);
34739     }
34740 };
34741 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34742     
34743     /**
34744      * @cfg {String} text
34745      * The text to show on the menu item.
34746      */
34747     text: '',
34748      /**
34749      * @cfg {String} HTML to render in menu
34750      * The text to show on the menu item (HTML version).
34751      */
34752     html: '',
34753     /**
34754      * @cfg {String} icon
34755      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34756      */
34757     icon: undefined,
34758     /**
34759      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34760      */
34761     itemCls : "x-menu-item",
34762     /**
34763      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34764      */
34765     canActivate : true,
34766     /**
34767      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34768      */
34769     showDelay: 200,
34770     // doc'd in BaseItem
34771     hideDelay: 200,
34772
34773     // private
34774     ctype: "Roo.menu.Item",
34775     
34776     // private
34777     onRender : function(container, position){
34778         var el = document.createElement("a");
34779         el.hideFocus = true;
34780         el.unselectable = "on";
34781         el.href = this.href || "#";
34782         if(this.hrefTarget){
34783             el.target = this.hrefTarget;
34784         }
34785         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34786         
34787         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34788         
34789         el.innerHTML = String.format(
34790                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34791                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34792         this.el = el;
34793         Roo.menu.Item.superclass.onRender.call(this, container, position);
34794     },
34795
34796     /**
34797      * Sets the text to display in this menu item
34798      * @param {String} text The text to display
34799      * @param {Boolean} isHTML true to indicate text is pure html.
34800      */
34801     setText : function(text, isHTML){
34802         if (isHTML) {
34803             this.html = text;
34804         } else {
34805             this.text = text;
34806             this.html = '';
34807         }
34808         if(this.rendered){
34809             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34810      
34811             this.el.update(String.format(
34812                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34813                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34814             this.parentMenu.autoWidth();
34815         }
34816     },
34817
34818     // private
34819     handleClick : function(e){
34820         if(!this.href){ // if no link defined, stop the event automatically
34821             e.stopEvent();
34822         }
34823         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34824     },
34825
34826     // private
34827     activate : function(autoExpand){
34828         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34829             this.focus();
34830             if(autoExpand){
34831                 this.expandMenu();
34832             }
34833         }
34834         return true;
34835     },
34836
34837     // private
34838     shouldDeactivate : function(e){
34839         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34840             if(this.menu && this.menu.isVisible()){
34841                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34842             }
34843             return true;
34844         }
34845         return false;
34846     },
34847
34848     // private
34849     deactivate : function(){
34850         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34851         this.hideMenu();
34852     },
34853
34854     // private
34855     expandMenu : function(autoActivate){
34856         if(!this.disabled && this.menu){
34857             clearTimeout(this.hideTimer);
34858             delete this.hideTimer;
34859             if(!this.menu.isVisible() && !this.showTimer){
34860                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34861             }else if (this.menu.isVisible() && autoActivate){
34862                 this.menu.tryActivate(0, 1);
34863             }
34864         }
34865     },
34866
34867     // private
34868     deferExpand : function(autoActivate){
34869         delete this.showTimer;
34870         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34871         if(autoActivate){
34872             this.menu.tryActivate(0, 1);
34873         }
34874     },
34875
34876     // private
34877     hideMenu : function(){
34878         clearTimeout(this.showTimer);
34879         delete this.showTimer;
34880         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34881             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34882         }
34883     },
34884
34885     // private
34886     deferHide : function(){
34887         delete this.hideTimer;
34888         this.menu.hide();
34889     }
34890 });/*
34891  * Based on:
34892  * Ext JS Library 1.1.1
34893  * Copyright(c) 2006-2007, Ext JS, LLC.
34894  *
34895  * Originally Released Under LGPL - original licence link has changed is not relivant.
34896  *
34897  * Fork - LGPL
34898  * <script type="text/javascript">
34899  */
34900  
34901 /**
34902  * @class Roo.menu.CheckItem
34903  * @extends Roo.menu.Item
34904  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34905  * @constructor
34906  * Creates a new CheckItem
34907  * @param {Object} config Configuration options
34908  */
34909 Roo.menu.CheckItem = function(config){
34910     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34911     this.addEvents({
34912         /**
34913          * @event beforecheckchange
34914          * Fires before the checked value is set, providing an opportunity to cancel if needed
34915          * @param {Roo.menu.CheckItem} this
34916          * @param {Boolean} checked The new checked value that will be set
34917          */
34918         "beforecheckchange" : true,
34919         /**
34920          * @event checkchange
34921          * Fires after the checked value has been set
34922          * @param {Roo.menu.CheckItem} this
34923          * @param {Boolean} checked The checked value that was set
34924          */
34925         "checkchange" : true
34926     });
34927     if(this.checkHandler){
34928         this.on('checkchange', this.checkHandler, this.scope);
34929     }
34930 };
34931 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34932     /**
34933      * @cfg {String} group
34934      * All check items with the same group name will automatically be grouped into a single-select
34935      * radio button group (defaults to '')
34936      */
34937     /**
34938      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34939      */
34940     itemCls : "x-menu-item x-menu-check-item",
34941     /**
34942      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34943      */
34944     groupClass : "x-menu-group-item",
34945
34946     /**
34947      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34948      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34949      * initialized with checked = true will be rendered as checked.
34950      */
34951     checked: false,
34952
34953     // private
34954     ctype: "Roo.menu.CheckItem",
34955
34956     // private
34957     onRender : function(c){
34958         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34959         if(this.group){
34960             this.el.addClass(this.groupClass);
34961         }
34962         Roo.menu.MenuMgr.registerCheckable(this);
34963         if(this.checked){
34964             this.checked = false;
34965             this.setChecked(true, true);
34966         }
34967     },
34968
34969     // private
34970     destroy : function(){
34971         if(this.rendered){
34972             Roo.menu.MenuMgr.unregisterCheckable(this);
34973         }
34974         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34975     },
34976
34977     /**
34978      * Set the checked state of this item
34979      * @param {Boolean} checked The new checked value
34980      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34981      */
34982     setChecked : function(state, suppressEvent){
34983         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34984             if(this.container){
34985                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34986             }
34987             this.checked = state;
34988             if(suppressEvent !== true){
34989                 this.fireEvent("checkchange", this, state);
34990             }
34991         }
34992     },
34993
34994     // private
34995     handleClick : function(e){
34996        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34997            this.setChecked(!this.checked);
34998        }
34999        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35000     }
35001 });/*
35002  * Based on:
35003  * Ext JS Library 1.1.1
35004  * Copyright(c) 2006-2007, Ext JS, LLC.
35005  *
35006  * Originally Released Under LGPL - original licence link has changed is not relivant.
35007  *
35008  * Fork - LGPL
35009  * <script type="text/javascript">
35010  */
35011  
35012 /**
35013  * @class Roo.menu.DateItem
35014  * @extends Roo.menu.Adapter
35015  * A menu item that wraps the {@link Roo.DatPicker} component.
35016  * @constructor
35017  * Creates a new DateItem
35018  * @param {Object} config Configuration options
35019  */
35020 Roo.menu.DateItem = function(config){
35021     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35022     /** The Roo.DatePicker object @type Roo.DatePicker */
35023     this.picker = this.component;
35024     this.addEvents({select: true});
35025     
35026     this.picker.on("render", function(picker){
35027         picker.getEl().swallowEvent("click");
35028         picker.container.addClass("x-menu-date-item");
35029     });
35030
35031     this.picker.on("select", this.onSelect, this);
35032 };
35033
35034 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35035     // private
35036     onSelect : function(picker, date){
35037         this.fireEvent("select", this, date, picker);
35038         Roo.menu.DateItem.superclass.handleClick.call(this);
35039     }
35040 });/*
35041  * Based on:
35042  * Ext JS Library 1.1.1
35043  * Copyright(c) 2006-2007, Ext JS, LLC.
35044  *
35045  * Originally Released Under LGPL - original licence link has changed is not relivant.
35046  *
35047  * Fork - LGPL
35048  * <script type="text/javascript">
35049  */
35050  
35051 /**
35052  * @class Roo.menu.ColorItem
35053  * @extends Roo.menu.Adapter
35054  * A menu item that wraps the {@link Roo.ColorPalette} component.
35055  * @constructor
35056  * Creates a new ColorItem
35057  * @param {Object} config Configuration options
35058  */
35059 Roo.menu.ColorItem = function(config){
35060     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35061     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35062     this.palette = this.component;
35063     this.relayEvents(this.palette, ["select"]);
35064     if(this.selectHandler){
35065         this.on('select', this.selectHandler, this.scope);
35066     }
35067 };
35068 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35069  * Based on:
35070  * Ext JS Library 1.1.1
35071  * Copyright(c) 2006-2007, Ext JS, LLC.
35072  *
35073  * Originally Released Under LGPL - original licence link has changed is not relivant.
35074  *
35075  * Fork - LGPL
35076  * <script type="text/javascript">
35077  */
35078  
35079
35080 /**
35081  * @class Roo.menu.DateMenu
35082  * @extends Roo.menu.Menu
35083  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35084  * @constructor
35085  * Creates a new DateMenu
35086  * @param {Object} config Configuration options
35087  */
35088 Roo.menu.DateMenu = function(config){
35089     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35090     this.plain = true;
35091     var di = new Roo.menu.DateItem(config);
35092     this.add(di);
35093     /**
35094      * The {@link Roo.DatePicker} instance for this DateMenu
35095      * @type DatePicker
35096      */
35097     this.picker = di.picker;
35098     /**
35099      * @event select
35100      * @param {DatePicker} picker
35101      * @param {Date} date
35102      */
35103     this.relayEvents(di, ["select"]);
35104
35105     this.on('beforeshow', function(){
35106         if(this.picker){
35107             this.picker.hideMonthPicker(true);
35108         }
35109     }, this);
35110 };
35111 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35112     cls:'x-date-menu'
35113 });/*
35114  * Based on:
35115  * Ext JS Library 1.1.1
35116  * Copyright(c) 2006-2007, Ext JS, LLC.
35117  *
35118  * Originally Released Under LGPL - original licence link has changed is not relivant.
35119  *
35120  * Fork - LGPL
35121  * <script type="text/javascript">
35122  */
35123  
35124
35125 /**
35126  * @class Roo.menu.ColorMenu
35127  * @extends Roo.menu.Menu
35128  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35129  * @constructor
35130  * Creates a new ColorMenu
35131  * @param {Object} config Configuration options
35132  */
35133 Roo.menu.ColorMenu = function(config){
35134     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35135     this.plain = true;
35136     var ci = new Roo.menu.ColorItem(config);
35137     this.add(ci);
35138     /**
35139      * The {@link Roo.ColorPalette} instance for this ColorMenu
35140      * @type ColorPalette
35141      */
35142     this.palette = ci.palette;
35143     /**
35144      * @event select
35145      * @param {ColorPalette} palette
35146      * @param {String} color
35147      */
35148     this.relayEvents(ci, ["select"]);
35149 };
35150 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35151  * Based on:
35152  * Ext JS Library 1.1.1
35153  * Copyright(c) 2006-2007, Ext JS, LLC.
35154  *
35155  * Originally Released Under LGPL - original licence link has changed is not relivant.
35156  *
35157  * Fork - LGPL
35158  * <script type="text/javascript">
35159  */
35160  
35161 /**
35162  * @class Roo.form.Field
35163  * @extends Roo.BoxComponent
35164  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35165  * @constructor
35166  * Creates a new Field
35167  * @param {Object} config Configuration options
35168  */
35169 Roo.form.Field = function(config){
35170     Roo.form.Field.superclass.constructor.call(this, config);
35171 };
35172
35173 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35174     /**
35175      * @cfg {String} fieldLabel Label to use when rendering a form.
35176      */
35177        /**
35178      * @cfg {String} qtip Mouse over tip
35179      */
35180      
35181     /**
35182      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35183      */
35184     invalidClass : "x-form-invalid",
35185     /**
35186      * @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")
35187      */
35188     invalidText : "The value in this field is invalid",
35189     /**
35190      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35191      */
35192     focusClass : "x-form-focus",
35193     /**
35194      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35195       automatic validation (defaults to "keyup").
35196      */
35197     validationEvent : "keyup",
35198     /**
35199      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35200      */
35201     validateOnBlur : true,
35202     /**
35203      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35204      */
35205     validationDelay : 250,
35206     /**
35207      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35208      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35209      */
35210     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35211     /**
35212      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35213      */
35214     fieldClass : "x-form-field",
35215     /**
35216      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35217      *<pre>
35218 Value         Description
35219 -----------   ----------------------------------------------------------------------
35220 qtip          Display a quick tip when the user hovers over the field
35221 title         Display a default browser title attribute popup
35222 under         Add a block div beneath the field containing the error text
35223 side          Add an error icon to the right of the field with a popup on hover
35224 [element id]  Add the error text directly to the innerHTML of the specified element
35225 </pre>
35226      */
35227     msgTarget : 'qtip',
35228     /**
35229      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35230      */
35231     msgFx : 'normal',
35232
35233     /**
35234      * @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.
35235      */
35236     readOnly : false,
35237
35238     /**
35239      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35240      */
35241     disabled : false,
35242
35243     /**
35244      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35245      */
35246     inputType : undefined,
35247     
35248     /**
35249      * @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).
35250          */
35251         tabIndex : undefined,
35252         
35253     // private
35254     isFormField : true,
35255
35256     // private
35257     hasFocus : false,
35258     /**
35259      * @property {Roo.Element} fieldEl
35260      * Element Containing the rendered Field (with label etc.)
35261      */
35262     /**
35263      * @cfg {Mixed} value A value to initialize this field with.
35264      */
35265     value : undefined,
35266
35267     /**
35268      * @cfg {String} name The field's HTML name attribute.
35269      */
35270     /**
35271      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35272      */
35273
35274         // private ??
35275         initComponent : function(){
35276         Roo.form.Field.superclass.initComponent.call(this);
35277         this.addEvents({
35278             /**
35279              * @event focus
35280              * Fires when this field receives input focus.
35281              * @param {Roo.form.Field} this
35282              */
35283             focus : true,
35284             /**
35285              * @event blur
35286              * Fires when this field loses input focus.
35287              * @param {Roo.form.Field} this
35288              */
35289             blur : true,
35290             /**
35291              * @event specialkey
35292              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35293              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35294              * @param {Roo.form.Field} this
35295              * @param {Roo.EventObject} e The event object
35296              */
35297             specialkey : true,
35298             /**
35299              * @event change
35300              * Fires just before the field blurs if the field value has changed.
35301              * @param {Roo.form.Field} this
35302              * @param {Mixed} newValue The new value
35303              * @param {Mixed} oldValue The original value
35304              */
35305             change : true,
35306             /**
35307              * @event invalid
35308              * Fires after the field has been marked as invalid.
35309              * @param {Roo.form.Field} this
35310              * @param {String} msg The validation message
35311              */
35312             invalid : true,
35313             /**
35314              * @event valid
35315              * Fires after the field has been validated with no errors.
35316              * @param {Roo.form.Field} this
35317              */
35318             valid : true,
35319              /**
35320              * @event keyup
35321              * Fires after the key up
35322              * @param {Roo.form.Field} this
35323              * @param {Roo.EventObject}  e The event Object
35324              */
35325             keyup : true
35326         });
35327     },
35328
35329     /**
35330      * Returns the name attribute of the field if available
35331      * @return {String} name The field name
35332      */
35333     getName: function(){
35334          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35335     },
35336
35337     // private
35338     onRender : function(ct, position){
35339         Roo.form.Field.superclass.onRender.call(this, ct, position);
35340         if(!this.el){
35341             var cfg = this.getAutoCreate();
35342             if(!cfg.name){
35343                 cfg.name = this.name || this.id;
35344             }
35345             if(this.inputType){
35346                 cfg.type = this.inputType;
35347             }
35348             this.el = ct.createChild(cfg, position);
35349         }
35350         var type = this.el.dom.type;
35351         if(type){
35352             if(type == 'password'){
35353                 type = 'text';
35354             }
35355             this.el.addClass('x-form-'+type);
35356         }
35357         if(this.readOnly){
35358             this.el.dom.readOnly = true;
35359         }
35360         if(this.tabIndex !== undefined){
35361             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35362         }
35363
35364         this.el.addClass([this.fieldClass, this.cls]);
35365         this.initValue();
35366     },
35367
35368     /**
35369      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35370      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35371      * @return {Roo.form.Field} this
35372      */
35373     applyTo : function(target){
35374         this.allowDomMove = false;
35375         this.el = Roo.get(target);
35376         this.render(this.el.dom.parentNode);
35377         return this;
35378     },
35379
35380     // private
35381     initValue : function(){
35382         if(this.value !== undefined){
35383             this.setValue(this.value);
35384         }else if(this.el.dom.value.length > 0){
35385             this.setValue(this.el.dom.value);
35386         }
35387     },
35388
35389     /**
35390      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35391      */
35392     isDirty : function() {
35393         if(this.disabled) {
35394             return false;
35395         }
35396         return String(this.getValue()) !== String(this.originalValue);
35397     },
35398
35399     // private
35400     afterRender : function(){
35401         Roo.form.Field.superclass.afterRender.call(this);
35402         this.initEvents();
35403     },
35404
35405     // private
35406     fireKey : function(e){
35407         //Roo.log('field ' + e.getKey());
35408         if(e.isNavKeyPress()){
35409             this.fireEvent("specialkey", this, e);
35410         }
35411     },
35412
35413     /**
35414      * Resets the current field value to the originally loaded value and clears any validation messages
35415      */
35416     reset : function(){
35417         this.setValue(this.originalValue);
35418         this.clearInvalid();
35419     },
35420
35421     // private
35422     initEvents : function(){
35423         // safari killled keypress - so keydown is now used..
35424         this.el.on("keydown" , this.fireKey,  this);
35425         this.el.on("focus", this.onFocus,  this);
35426         this.el.on("blur", this.onBlur,  this);
35427         this.el.relayEvent('keyup', this);
35428
35429         // reference to original value for reset
35430         this.originalValue = this.getValue();
35431     },
35432
35433     // private
35434     onFocus : function(){
35435         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35436             this.el.addClass(this.focusClass);
35437         }
35438         if(!this.hasFocus){
35439             this.hasFocus = true;
35440             this.startValue = this.getValue();
35441             this.fireEvent("focus", this);
35442         }
35443     },
35444
35445     beforeBlur : Roo.emptyFn,
35446
35447     // private
35448     onBlur : function(){
35449         this.beforeBlur();
35450         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35451             this.el.removeClass(this.focusClass);
35452         }
35453         this.hasFocus = false;
35454         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35455             this.validate();
35456         }
35457         var v = this.getValue();
35458         if(String(v) !== String(this.startValue)){
35459             this.fireEvent('change', this, v, this.startValue);
35460         }
35461         this.fireEvent("blur", this);
35462     },
35463
35464     /**
35465      * Returns whether or not the field value is currently valid
35466      * @param {Boolean} preventMark True to disable marking the field invalid
35467      * @return {Boolean} True if the value is valid, else false
35468      */
35469     isValid : function(preventMark){
35470         if(this.disabled){
35471             return true;
35472         }
35473         var restore = this.preventMark;
35474         this.preventMark = preventMark === true;
35475         var v = this.validateValue(this.processValue(this.getRawValue()));
35476         this.preventMark = restore;
35477         return v;
35478     },
35479
35480     /**
35481      * Validates the field value
35482      * @return {Boolean} True if the value is valid, else false
35483      */
35484     validate : function(){
35485         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35486             this.clearInvalid();
35487             return true;
35488         }
35489         return false;
35490     },
35491
35492     processValue : function(value){
35493         return value;
35494     },
35495
35496     // private
35497     // Subclasses should provide the validation implementation by overriding this
35498     validateValue : function(value){
35499         return true;
35500     },
35501
35502     /**
35503      * Mark this field as invalid
35504      * @param {String} msg The validation message
35505      */
35506     markInvalid : function(msg){
35507         if(!this.rendered || this.preventMark){ // not rendered
35508             return;
35509         }
35510         this.el.addClass(this.invalidClass);
35511         msg = msg || this.invalidText;
35512         switch(this.msgTarget){
35513             case 'qtip':
35514                 this.el.dom.qtip = msg;
35515                 this.el.dom.qclass = 'x-form-invalid-tip';
35516                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35517                     Roo.QuickTips.enable();
35518                 }
35519                 break;
35520             case 'title':
35521                 this.el.dom.title = msg;
35522                 break;
35523             case 'under':
35524                 if(!this.errorEl){
35525                     var elp = this.el.findParent('.x-form-element', 5, true);
35526                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35527                     this.errorEl.setWidth(elp.getWidth(true)-20);
35528                 }
35529                 this.errorEl.update(msg);
35530                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35531                 break;
35532             case 'side':
35533                 if(!this.errorIcon){
35534                     var elp = this.el.findParent('.x-form-element', 5, true);
35535                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35536                 }
35537                 this.alignErrorIcon();
35538                 this.errorIcon.dom.qtip = msg;
35539                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35540                 this.errorIcon.show();
35541                 this.on('resize', this.alignErrorIcon, this);
35542                 break;
35543             default:
35544                 var t = Roo.getDom(this.msgTarget);
35545                 t.innerHTML = msg;
35546                 t.style.display = this.msgDisplay;
35547                 break;
35548         }
35549         this.fireEvent('invalid', this, msg);
35550     },
35551
35552     // private
35553     alignErrorIcon : function(){
35554         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35555     },
35556
35557     /**
35558      * Clear any invalid styles/messages for this field
35559      */
35560     clearInvalid : function(){
35561         if(!this.rendered || this.preventMark){ // not rendered
35562             return;
35563         }
35564         this.el.removeClass(this.invalidClass);
35565         switch(this.msgTarget){
35566             case 'qtip':
35567                 this.el.dom.qtip = '';
35568                 break;
35569             case 'title':
35570                 this.el.dom.title = '';
35571                 break;
35572             case 'under':
35573                 if(this.errorEl){
35574                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35575                 }
35576                 break;
35577             case 'side':
35578                 if(this.errorIcon){
35579                     this.errorIcon.dom.qtip = '';
35580                     this.errorIcon.hide();
35581                     this.un('resize', this.alignErrorIcon, this);
35582                 }
35583                 break;
35584             default:
35585                 var t = Roo.getDom(this.msgTarget);
35586                 t.innerHTML = '';
35587                 t.style.display = 'none';
35588                 break;
35589         }
35590         this.fireEvent('valid', this);
35591     },
35592
35593     /**
35594      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35595      * @return {Mixed} value The field value
35596      */
35597     getRawValue : function(){
35598         var v = this.el.getValue();
35599         if(v === this.emptyText){
35600             v = '';
35601         }
35602         return v;
35603     },
35604
35605     /**
35606      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35607      * @return {Mixed} value The field value
35608      */
35609     getValue : function(){
35610         var v = this.el.getValue();
35611         if(v === this.emptyText || v === undefined){
35612             v = '';
35613         }
35614         return v;
35615     },
35616
35617     /**
35618      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35619      * @param {Mixed} value The value to set
35620      */
35621     setRawValue : function(v){
35622         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35623     },
35624
35625     /**
35626      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35627      * @param {Mixed} value The value to set
35628      */
35629     setValue : function(v){
35630         this.value = v;
35631         if(this.rendered){
35632             this.el.dom.value = (v === null || v === undefined ? '' : v);
35633             this.validate();
35634         }
35635     },
35636
35637     adjustSize : function(w, h){
35638         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35639         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35640         return s;
35641     },
35642
35643     adjustWidth : function(tag, w){
35644         tag = tag.toLowerCase();
35645         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35646             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35647                 if(tag == 'input'){
35648                     return w + 2;
35649                 }
35650                 if(tag = 'textarea'){
35651                     return w-2;
35652                 }
35653             }else if(Roo.isOpera){
35654                 if(tag == 'input'){
35655                     return w + 2;
35656                 }
35657                 if(tag = 'textarea'){
35658                     return w-2;
35659                 }
35660             }
35661         }
35662         return w;
35663     }
35664 });
35665
35666
35667 // anything other than normal should be considered experimental
35668 Roo.form.Field.msgFx = {
35669     normal : {
35670         show: function(msgEl, f){
35671             msgEl.setDisplayed('block');
35672         },
35673
35674         hide : function(msgEl, f){
35675             msgEl.setDisplayed(false).update('');
35676         }
35677     },
35678
35679     slide : {
35680         show: function(msgEl, f){
35681             msgEl.slideIn('t', {stopFx:true});
35682         },
35683
35684         hide : function(msgEl, f){
35685             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35686         }
35687     },
35688
35689     slideRight : {
35690         show: function(msgEl, f){
35691             msgEl.fixDisplay();
35692             msgEl.alignTo(f.el, 'tl-tr');
35693             msgEl.slideIn('l', {stopFx:true});
35694         },
35695
35696         hide : function(msgEl, f){
35697             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35698         }
35699     }
35700 };/*
35701  * Based on:
35702  * Ext JS Library 1.1.1
35703  * Copyright(c) 2006-2007, Ext JS, LLC.
35704  *
35705  * Originally Released Under LGPL - original licence link has changed is not relivant.
35706  *
35707  * Fork - LGPL
35708  * <script type="text/javascript">
35709  */
35710  
35711
35712 /**
35713  * @class Roo.form.TextField
35714  * @extends Roo.form.Field
35715  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35716  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35717  * @constructor
35718  * Creates a new TextField
35719  * @param {Object} config Configuration options
35720  */
35721 Roo.form.TextField = function(config){
35722     Roo.form.TextField.superclass.constructor.call(this, config);
35723     this.addEvents({
35724         /**
35725          * @event autosize
35726          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35727          * according to the default logic, but this event provides a hook for the developer to apply additional
35728          * logic at runtime to resize the field if needed.
35729              * @param {Roo.form.Field} this This text field
35730              * @param {Number} width The new field width
35731              */
35732         autosize : true
35733     });
35734 };
35735
35736 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35737     /**
35738      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35739      */
35740     grow : false,
35741     /**
35742      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35743      */
35744     growMin : 30,
35745     /**
35746      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35747      */
35748     growMax : 800,
35749     /**
35750      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35751      */
35752     vtype : null,
35753     /**
35754      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35755      */
35756     maskRe : null,
35757     /**
35758      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35759      */
35760     disableKeyFilter : false,
35761     /**
35762      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35763      */
35764     allowBlank : true,
35765     /**
35766      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35767      */
35768     minLength : 0,
35769     /**
35770      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35771      */
35772     maxLength : Number.MAX_VALUE,
35773     /**
35774      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35775      */
35776     minLengthText : "The minimum length for this field is {0}",
35777     /**
35778      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35779      */
35780     maxLengthText : "The maximum length for this field is {0}",
35781     /**
35782      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35783      */
35784     selectOnFocus : false,
35785     /**
35786      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35787      */
35788     blankText : "This field is required",
35789     /**
35790      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35791      * If available, this function will be called only after the basic validators all return true, and will be passed the
35792      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35793      */
35794     validator : null,
35795     /**
35796      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35797      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35798      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35799      */
35800     regex : null,
35801     /**
35802      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35803      */
35804     regexText : "",
35805     /**
35806      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35807      */
35808     emptyText : null,
35809     /**
35810      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35811      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35812      */
35813     emptyClass : 'x-form-empty-field',
35814
35815     // private
35816     initEvents : function(){
35817         Roo.form.TextField.superclass.initEvents.call(this);
35818         if(this.validationEvent == 'keyup'){
35819             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35820             this.el.on('keyup', this.filterValidation, this);
35821         }
35822         else if(this.validationEvent !== false){
35823             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35824         }
35825         if(this.selectOnFocus || this.emptyText){
35826             this.on("focus", this.preFocus, this);
35827             if(this.emptyText){
35828                 this.on('blur', this.postBlur, this);
35829                 this.applyEmptyText();
35830             }
35831         }
35832         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35833             this.el.on("keypress", this.filterKeys, this);
35834         }
35835         if(this.grow){
35836             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35837             this.el.on("click", this.autoSize,  this);
35838         }
35839     },
35840
35841     processValue : function(value){
35842         if(this.stripCharsRe){
35843             var newValue = value.replace(this.stripCharsRe, '');
35844             if(newValue !== value){
35845                 this.setRawValue(newValue);
35846                 return newValue;
35847             }
35848         }
35849         return value;
35850     },
35851
35852     filterValidation : function(e){
35853         if(!e.isNavKeyPress()){
35854             this.validationTask.delay(this.validationDelay);
35855         }
35856     },
35857
35858     // private
35859     onKeyUp : function(e){
35860         if(!e.isNavKeyPress()){
35861             this.autoSize();
35862         }
35863     },
35864
35865     /**
35866      * Resets the current field value to the originally-loaded value and clears any validation messages.
35867      * Also adds emptyText and emptyClass if the original value was blank.
35868      */
35869     reset : function(){
35870         Roo.form.TextField.superclass.reset.call(this);
35871         this.applyEmptyText();
35872     },
35873
35874     applyEmptyText : function(){
35875         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35876             this.setRawValue(this.emptyText);
35877             this.el.addClass(this.emptyClass);
35878         }
35879     },
35880
35881     // private
35882     preFocus : function(){
35883         if(this.emptyText){
35884             if(this.el.dom.value == this.emptyText){
35885                 this.setRawValue('');
35886             }
35887             this.el.removeClass(this.emptyClass);
35888         }
35889         if(this.selectOnFocus){
35890             this.el.dom.select();
35891         }
35892     },
35893
35894     // private
35895     postBlur : function(){
35896         this.applyEmptyText();
35897     },
35898
35899     // private
35900     filterKeys : function(e){
35901         var k = e.getKey();
35902         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35903             return;
35904         }
35905         var c = e.getCharCode(), cc = String.fromCharCode(c);
35906         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35907             return;
35908         }
35909         if(!this.maskRe.test(cc)){
35910             e.stopEvent();
35911         }
35912     },
35913
35914     setValue : function(v){
35915         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35916             this.el.removeClass(this.emptyClass);
35917         }
35918         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35919         this.applyEmptyText();
35920         this.autoSize();
35921     },
35922
35923     /**
35924      * Validates a value according to the field's validation rules and marks the field as invalid
35925      * if the validation fails
35926      * @param {Mixed} value The value to validate
35927      * @return {Boolean} True if the value is valid, else false
35928      */
35929     validateValue : function(value){
35930         if(value.length < 1 || value === this.emptyText){ // if it's blank
35931              if(this.allowBlank){
35932                 this.clearInvalid();
35933                 return true;
35934              }else{
35935                 this.markInvalid(this.blankText);
35936                 return false;
35937              }
35938         }
35939         if(value.length < this.minLength){
35940             this.markInvalid(String.format(this.minLengthText, this.minLength));
35941             return false;
35942         }
35943         if(value.length > this.maxLength){
35944             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35945             return false;
35946         }
35947         if(this.vtype){
35948             var vt = Roo.form.VTypes;
35949             if(!vt[this.vtype](value, this)){
35950                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35951                 return false;
35952             }
35953         }
35954         if(typeof this.validator == "function"){
35955             var msg = this.validator(value);
35956             if(msg !== true){
35957                 this.markInvalid(msg);
35958                 return false;
35959             }
35960         }
35961         if(this.regex && !this.regex.test(value)){
35962             this.markInvalid(this.regexText);
35963             return false;
35964         }
35965         return true;
35966     },
35967
35968     /**
35969      * Selects text in this field
35970      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35971      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35972      */
35973     selectText : function(start, end){
35974         var v = this.getRawValue();
35975         if(v.length > 0){
35976             start = start === undefined ? 0 : start;
35977             end = end === undefined ? v.length : end;
35978             var d = this.el.dom;
35979             if(d.setSelectionRange){
35980                 d.setSelectionRange(start, end);
35981             }else if(d.createTextRange){
35982                 var range = d.createTextRange();
35983                 range.moveStart("character", start);
35984                 range.moveEnd("character", v.length-end);
35985                 range.select();
35986             }
35987         }
35988     },
35989
35990     /**
35991      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35992      * This only takes effect if grow = true, and fires the autosize event.
35993      */
35994     autoSize : function(){
35995         if(!this.grow || !this.rendered){
35996             return;
35997         }
35998         if(!this.metrics){
35999             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36000         }
36001         var el = this.el;
36002         var v = el.dom.value;
36003         var d = document.createElement('div');
36004         d.appendChild(document.createTextNode(v));
36005         v = d.innerHTML;
36006         d = null;
36007         v += "&#160;";
36008         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36009         this.el.setWidth(w);
36010         this.fireEvent("autosize", this, w);
36011     }
36012 });/*
36013  * Based on:
36014  * Ext JS Library 1.1.1
36015  * Copyright(c) 2006-2007, Ext JS, LLC.
36016  *
36017  * Originally Released Under LGPL - original licence link has changed is not relivant.
36018  *
36019  * Fork - LGPL
36020  * <script type="text/javascript">
36021  */
36022  
36023 /**
36024  * @class Roo.form.Hidden
36025  * @extends Roo.form.TextField
36026  * Simple Hidden element used on forms 
36027  * 
36028  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36029  * 
36030  * @constructor
36031  * Creates a new Hidden form element.
36032  * @param {Object} config Configuration options
36033  */
36034
36035
36036
36037 // easy hidden field...
36038 Roo.form.Hidden = function(config){
36039     Roo.form.Hidden.superclass.constructor.call(this, config);
36040 };
36041   
36042 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36043     fieldLabel:      '',
36044     inputType:      'hidden',
36045     width:          50,
36046     allowBlank:     true,
36047     labelSeparator: '',
36048     hidden:         true,
36049     itemCls :       'x-form-item-display-none'
36050
36051
36052 });
36053
36054
36055 /*
36056  * Based on:
36057  * Ext JS Library 1.1.1
36058  * Copyright(c) 2006-2007, Ext JS, LLC.
36059  *
36060  * Originally Released Under LGPL - original licence link has changed is not relivant.
36061  *
36062  * Fork - LGPL
36063  * <script type="text/javascript">
36064  */
36065  
36066 /**
36067  * @class Roo.form.TriggerField
36068  * @extends Roo.form.TextField
36069  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36070  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36071  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36072  * for which you can provide a custom implementation.  For example:
36073  * <pre><code>
36074 var trigger = new Roo.form.TriggerField();
36075 trigger.onTriggerClick = myTriggerFn;
36076 trigger.applyTo('my-field');
36077 </code></pre>
36078  *
36079  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36080  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36081  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36082  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36083  * @constructor
36084  * Create a new TriggerField.
36085  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36086  * to the base TextField)
36087  */
36088 Roo.form.TriggerField = function(config){
36089     this.mimicing = false;
36090     Roo.form.TriggerField.superclass.constructor.call(this, config);
36091 };
36092
36093 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36094     /**
36095      * @cfg {String} triggerClass A CSS class to apply to the trigger
36096      */
36097     /**
36098      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36099      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36100      */
36101     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36102     /**
36103      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36104      */
36105     hideTrigger:false,
36106
36107     /** @cfg {Boolean} grow @hide */
36108     /** @cfg {Number} growMin @hide */
36109     /** @cfg {Number} growMax @hide */
36110
36111     /**
36112      * @hide 
36113      * @method
36114      */
36115     autoSize: Roo.emptyFn,
36116     // private
36117     monitorTab : true,
36118     // private
36119     deferHeight : true,
36120
36121     
36122     actionMode : 'wrap',
36123     // private
36124     onResize : function(w, h){
36125         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36126         if(typeof w == 'number'){
36127             var x = w - this.trigger.getWidth();
36128             this.el.setWidth(this.adjustWidth('input', x));
36129             this.trigger.setStyle('left', x+'px');
36130         }
36131     },
36132
36133     // private
36134     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36135
36136     // private
36137     getResizeEl : function(){
36138         return this.wrap;
36139     },
36140
36141     // private
36142     getPositionEl : function(){
36143         return this.wrap;
36144     },
36145
36146     // private
36147     alignErrorIcon : function(){
36148         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36149     },
36150
36151     // private
36152     onRender : function(ct, position){
36153         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36154         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36155         this.trigger = this.wrap.createChild(this.triggerConfig ||
36156                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36157         if(this.hideTrigger){
36158             this.trigger.setDisplayed(false);
36159         }
36160         this.initTrigger();
36161         if(!this.width){
36162             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36163         }
36164     },
36165
36166     // private
36167     initTrigger : function(){
36168         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36169         this.trigger.addClassOnOver('x-form-trigger-over');
36170         this.trigger.addClassOnClick('x-form-trigger-click');
36171     },
36172
36173     // private
36174     onDestroy : function(){
36175         if(this.trigger){
36176             this.trigger.removeAllListeners();
36177             this.trigger.remove();
36178         }
36179         if(this.wrap){
36180             this.wrap.remove();
36181         }
36182         Roo.form.TriggerField.superclass.onDestroy.call(this);
36183     },
36184
36185     // private
36186     onFocus : function(){
36187         Roo.form.TriggerField.superclass.onFocus.call(this);
36188         if(!this.mimicing){
36189             this.wrap.addClass('x-trigger-wrap-focus');
36190             this.mimicing = true;
36191             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36192             if(this.monitorTab){
36193                 this.el.on("keydown", this.checkTab, this);
36194             }
36195         }
36196     },
36197
36198     // private
36199     checkTab : function(e){
36200         if(e.getKey() == e.TAB){
36201             this.triggerBlur();
36202         }
36203     },
36204
36205     // private
36206     onBlur : function(){
36207         // do nothing
36208     },
36209
36210     // private
36211     mimicBlur : function(e, t){
36212         if(!this.wrap.contains(t) && this.validateBlur()){
36213             this.triggerBlur();
36214         }
36215     },
36216
36217     // private
36218     triggerBlur : function(){
36219         this.mimicing = false;
36220         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36221         if(this.monitorTab){
36222             this.el.un("keydown", this.checkTab, this);
36223         }
36224         this.wrap.removeClass('x-trigger-wrap-focus');
36225         Roo.form.TriggerField.superclass.onBlur.call(this);
36226     },
36227
36228     // private
36229     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36230     validateBlur : function(e, t){
36231         return true;
36232     },
36233
36234     // private
36235     onDisable : function(){
36236         Roo.form.TriggerField.superclass.onDisable.call(this);
36237         if(this.wrap){
36238             this.wrap.addClass('x-item-disabled');
36239         }
36240     },
36241
36242     // private
36243     onEnable : function(){
36244         Roo.form.TriggerField.superclass.onEnable.call(this);
36245         if(this.wrap){
36246             this.wrap.removeClass('x-item-disabled');
36247         }
36248     },
36249
36250     // private
36251     onShow : function(){
36252         var ae = this.getActionEl();
36253         
36254         if(ae){
36255             ae.dom.style.display = '';
36256             ae.dom.style.visibility = 'visible';
36257         }
36258     },
36259
36260     // private
36261     
36262     onHide : function(){
36263         var ae = this.getActionEl();
36264         ae.dom.style.display = 'none';
36265     },
36266
36267     /**
36268      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36269      * by an implementing function.
36270      * @method
36271      * @param {EventObject} e
36272      */
36273     onTriggerClick : Roo.emptyFn
36274 });
36275
36276 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36277 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36278 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36279 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36280     initComponent : function(){
36281         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36282
36283         this.triggerConfig = {
36284             tag:'span', cls:'x-form-twin-triggers', cn:[
36285             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36286             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36287         ]};
36288     },
36289
36290     getTrigger : function(index){
36291         return this.triggers[index];
36292     },
36293
36294     initTrigger : function(){
36295         var ts = this.trigger.select('.x-form-trigger', true);
36296         this.wrap.setStyle('overflow', 'hidden');
36297         var triggerField = this;
36298         ts.each(function(t, all, index){
36299             t.hide = function(){
36300                 var w = triggerField.wrap.getWidth();
36301                 this.dom.style.display = 'none';
36302                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36303             };
36304             t.show = function(){
36305                 var w = triggerField.wrap.getWidth();
36306                 this.dom.style.display = '';
36307                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36308             };
36309             var triggerIndex = 'Trigger'+(index+1);
36310
36311             if(this['hide'+triggerIndex]){
36312                 t.dom.style.display = 'none';
36313             }
36314             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36315             t.addClassOnOver('x-form-trigger-over');
36316             t.addClassOnClick('x-form-trigger-click');
36317         }, this);
36318         this.triggers = ts.elements;
36319     },
36320
36321     onTrigger1Click : Roo.emptyFn,
36322     onTrigger2Click : Roo.emptyFn
36323 });/*
36324  * Based on:
36325  * Ext JS Library 1.1.1
36326  * Copyright(c) 2006-2007, Ext JS, LLC.
36327  *
36328  * Originally Released Under LGPL - original licence link has changed is not relivant.
36329  *
36330  * Fork - LGPL
36331  * <script type="text/javascript">
36332  */
36333  
36334 /**
36335  * @class Roo.form.TextArea
36336  * @extends Roo.form.TextField
36337  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36338  * support for auto-sizing.
36339  * @constructor
36340  * Creates a new TextArea
36341  * @param {Object} config Configuration options
36342  */
36343 Roo.form.TextArea = function(config){
36344     Roo.form.TextArea.superclass.constructor.call(this, config);
36345     // these are provided exchanges for backwards compat
36346     // minHeight/maxHeight were replaced by growMin/growMax to be
36347     // compatible with TextField growing config values
36348     if(this.minHeight !== undefined){
36349         this.growMin = this.minHeight;
36350     }
36351     if(this.maxHeight !== undefined){
36352         this.growMax = this.maxHeight;
36353     }
36354 };
36355
36356 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36357     /**
36358      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36359      */
36360     growMin : 60,
36361     /**
36362      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36363      */
36364     growMax: 1000,
36365     /**
36366      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36367      * in the field (equivalent to setting overflow: hidden, defaults to false)
36368      */
36369     preventScrollbars: false,
36370     /**
36371      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36372      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36373      */
36374
36375     // private
36376     onRender : function(ct, position){
36377         if(!this.el){
36378             this.defaultAutoCreate = {
36379                 tag: "textarea",
36380                 style:"width:300px;height:60px;",
36381                 autocomplete: "off"
36382             };
36383         }
36384         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36385         if(this.grow){
36386             this.textSizeEl = Roo.DomHelper.append(document.body, {
36387                 tag: "pre", cls: "x-form-grow-sizer"
36388             });
36389             if(this.preventScrollbars){
36390                 this.el.setStyle("overflow", "hidden");
36391             }
36392             this.el.setHeight(this.growMin);
36393         }
36394     },
36395
36396     onDestroy : function(){
36397         if(this.textSizeEl){
36398             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36399         }
36400         Roo.form.TextArea.superclass.onDestroy.call(this);
36401     },
36402
36403     // private
36404     onKeyUp : function(e){
36405         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36406             this.autoSize();
36407         }
36408     },
36409
36410     /**
36411      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36412      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36413      */
36414     autoSize : function(){
36415         if(!this.grow || !this.textSizeEl){
36416             return;
36417         }
36418         var el = this.el;
36419         var v = el.dom.value;
36420         var ts = this.textSizeEl;
36421
36422         ts.innerHTML = '';
36423         ts.appendChild(document.createTextNode(v));
36424         v = ts.innerHTML;
36425
36426         Roo.fly(ts).setWidth(this.el.getWidth());
36427         if(v.length < 1){
36428             v = "&#160;&#160;";
36429         }else{
36430             if(Roo.isIE){
36431                 v = v.replace(/\n/g, '<p>&#160;</p>');
36432             }
36433             v += "&#160;\n&#160;";
36434         }
36435         ts.innerHTML = v;
36436         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36437         if(h != this.lastHeight){
36438             this.lastHeight = h;
36439             this.el.setHeight(h);
36440             this.fireEvent("autosize", this, h);
36441         }
36442     }
36443 });/*
36444  * Based on:
36445  * Ext JS Library 1.1.1
36446  * Copyright(c) 2006-2007, Ext JS, LLC.
36447  *
36448  * Originally Released Under LGPL - original licence link has changed is not relivant.
36449  *
36450  * Fork - LGPL
36451  * <script type="text/javascript">
36452  */
36453  
36454
36455 /**
36456  * @class Roo.form.NumberField
36457  * @extends Roo.form.TextField
36458  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36459  * @constructor
36460  * Creates a new NumberField
36461  * @param {Object} config Configuration options
36462  */
36463 Roo.form.NumberField = function(config){
36464     Roo.form.NumberField.superclass.constructor.call(this, config);
36465 };
36466
36467 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36468     /**
36469      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36470      */
36471     fieldClass: "x-form-field x-form-num-field",
36472     /**
36473      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36474      */
36475     allowDecimals : true,
36476     /**
36477      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36478      */
36479     decimalSeparator : ".",
36480     /**
36481      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36482      */
36483     decimalPrecision : 2,
36484     /**
36485      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36486      */
36487     allowNegative : true,
36488     /**
36489      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36490      */
36491     minValue : Number.NEGATIVE_INFINITY,
36492     /**
36493      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36494      */
36495     maxValue : Number.MAX_VALUE,
36496     /**
36497      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36498      */
36499     minText : "The minimum value for this field is {0}",
36500     /**
36501      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36502      */
36503     maxText : "The maximum value for this field is {0}",
36504     /**
36505      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36506      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36507      */
36508     nanText : "{0} is not a valid number",
36509
36510     // private
36511     initEvents : function(){
36512         Roo.form.NumberField.superclass.initEvents.call(this);
36513         var allowed = "0123456789";
36514         if(this.allowDecimals){
36515             allowed += this.decimalSeparator;
36516         }
36517         if(this.allowNegative){
36518             allowed += "-";
36519         }
36520         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36521         var keyPress = function(e){
36522             var k = e.getKey();
36523             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36524                 return;
36525             }
36526             var c = e.getCharCode();
36527             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36528                 e.stopEvent();
36529             }
36530         };
36531         this.el.on("keypress", keyPress, this);
36532     },
36533
36534     // private
36535     validateValue : function(value){
36536         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36537             return false;
36538         }
36539         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36540              return true;
36541         }
36542         var num = this.parseValue(value);
36543         if(isNaN(num)){
36544             this.markInvalid(String.format(this.nanText, value));
36545             return false;
36546         }
36547         if(num < this.minValue){
36548             this.markInvalid(String.format(this.minText, this.minValue));
36549             return false;
36550         }
36551         if(num > this.maxValue){
36552             this.markInvalid(String.format(this.maxText, this.maxValue));
36553             return false;
36554         }
36555         return true;
36556     },
36557
36558     getValue : function(){
36559         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36560     },
36561
36562     // private
36563     parseValue : function(value){
36564         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36565         return isNaN(value) ? '' : value;
36566     },
36567
36568     // private
36569     fixPrecision : function(value){
36570         var nan = isNaN(value);
36571         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36572             return nan ? '' : value;
36573         }
36574         return parseFloat(value).toFixed(this.decimalPrecision);
36575     },
36576
36577     setValue : function(v){
36578         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36579     },
36580
36581     // private
36582     decimalPrecisionFcn : function(v){
36583         return Math.floor(v);
36584     },
36585
36586     beforeBlur : function(){
36587         var v = this.parseValue(this.getRawValue());
36588         if(v){
36589             this.setValue(this.fixPrecision(v));
36590         }
36591     }
36592 });/*
36593  * Based on:
36594  * Ext JS Library 1.1.1
36595  * Copyright(c) 2006-2007, Ext JS, LLC.
36596  *
36597  * Originally Released Under LGPL - original licence link has changed is not relivant.
36598  *
36599  * Fork - LGPL
36600  * <script type="text/javascript">
36601  */
36602  
36603 /**
36604  * @class Roo.form.DateField
36605  * @extends Roo.form.TriggerField
36606  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36607 * @constructor
36608 * Create a new DateField
36609 * @param {Object} config
36610  */
36611 Roo.form.DateField = function(config){
36612     Roo.form.DateField.superclass.constructor.call(this, config);
36613     
36614       this.addEvents({
36615          
36616         /**
36617          * @event select
36618          * Fires when a date is selected
36619              * @param {Roo.form.DateField} combo This combo box
36620              * @param {Date} date The date selected
36621              */
36622         'select' : true
36623          
36624     });
36625     
36626     
36627     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36628     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36629     this.ddMatch = null;
36630     if(this.disabledDates){
36631         var dd = this.disabledDates;
36632         var re = "(?:";
36633         for(var i = 0; i < dd.length; i++){
36634             re += dd[i];
36635             if(i != dd.length-1) re += "|";
36636         }
36637         this.ddMatch = new RegExp(re + ")");
36638     }
36639 };
36640
36641 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36642     /**
36643      * @cfg {String} format
36644      * The default date format string which can be overriden for localization support.  The format must be
36645      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36646      */
36647     format : "m/d/y",
36648     /**
36649      * @cfg {String} altFormats
36650      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36651      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36652      */
36653     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36654     /**
36655      * @cfg {Array} disabledDays
36656      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36657      */
36658     disabledDays : null,
36659     /**
36660      * @cfg {String} disabledDaysText
36661      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36662      */
36663     disabledDaysText : "Disabled",
36664     /**
36665      * @cfg {Array} disabledDates
36666      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36667      * expression so they are very powerful. Some examples:
36668      * <ul>
36669      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36670      * <li>["03/08", "09/16"] would disable those days for every year</li>
36671      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36672      * <li>["03/../2006"] would disable every day in March 2006</li>
36673      * <li>["^03"] would disable every day in every March</li>
36674      * </ul>
36675      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36676      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36677      */
36678     disabledDates : null,
36679     /**
36680      * @cfg {String} disabledDatesText
36681      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36682      */
36683     disabledDatesText : "Disabled",
36684     /**
36685      * @cfg {Date/String} minValue
36686      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36687      * valid format (defaults to null).
36688      */
36689     minValue : null,
36690     /**
36691      * @cfg {Date/String} maxValue
36692      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36693      * valid format (defaults to null).
36694      */
36695     maxValue : null,
36696     /**
36697      * @cfg {String} minText
36698      * The error text to display when the date in the cell is before minValue (defaults to
36699      * 'The date in this field must be after {minValue}').
36700      */
36701     minText : "The date in this field must be equal to or after {0}",
36702     /**
36703      * @cfg {String} maxText
36704      * The error text to display when the date in the cell is after maxValue (defaults to
36705      * 'The date in this field must be before {maxValue}').
36706      */
36707     maxText : "The date in this field must be equal to or before {0}",
36708     /**
36709      * @cfg {String} invalidText
36710      * The error text to display when the date in the field is invalid (defaults to
36711      * '{value} is not a valid date - it must be in the format {format}').
36712      */
36713     invalidText : "{0} is not a valid date - it must be in the format {1}",
36714     /**
36715      * @cfg {String} triggerClass
36716      * An additional CSS class used to style the trigger button.  The trigger will always get the
36717      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36718      * which displays a calendar icon).
36719      */
36720     triggerClass : 'x-form-date-trigger',
36721     
36722
36723     /**
36724      * @cfg {bool} useIso
36725      * if enabled, then the date field will use a hidden field to store the 
36726      * real value as iso formated date. default (false)
36727      */ 
36728     useIso : false,
36729     /**
36730      * @cfg {String/Object} autoCreate
36731      * A DomHelper element spec, or true for a default element spec (defaults to
36732      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36733      */ 
36734     // private
36735     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36736     
36737     // private
36738     hiddenField: false,
36739     
36740     onRender : function(ct, position)
36741     {
36742         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36743         if (this.useIso) {
36744             this.el.dom.removeAttribute('name'); 
36745             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36746                     'before', true);
36747             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36748             // prevent input submission
36749             this.hiddenName = this.name;
36750         }
36751             
36752             
36753     },
36754     
36755     // private
36756     validateValue : function(value)
36757     {
36758         value = this.formatDate(value);
36759         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36760             return false;
36761         }
36762         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36763              return true;
36764         }
36765         var svalue = value;
36766         value = this.parseDate(value);
36767         if(!value){
36768             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36769             return false;
36770         }
36771         var time = value.getTime();
36772         if(this.minValue && time < this.minValue.getTime()){
36773             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36774             return false;
36775         }
36776         if(this.maxValue && time > this.maxValue.getTime()){
36777             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36778             return false;
36779         }
36780         if(this.disabledDays){
36781             var day = value.getDay();
36782             for(var i = 0; i < this.disabledDays.length; i++) {
36783                 if(day === this.disabledDays[i]){
36784                     this.markInvalid(this.disabledDaysText);
36785                     return false;
36786                 }
36787             }
36788         }
36789         var fvalue = this.formatDate(value);
36790         if(this.ddMatch && this.ddMatch.test(fvalue)){
36791             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36792             return false;
36793         }
36794         return true;
36795     },
36796
36797     // private
36798     // Provides logic to override the default TriggerField.validateBlur which just returns true
36799     validateBlur : function(){
36800         return !this.menu || !this.menu.isVisible();
36801     },
36802
36803     /**
36804      * Returns the current date value of the date field.
36805      * @return {Date} The date value
36806      */
36807     getValue : function(){
36808         
36809         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36810     },
36811
36812     /**
36813      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36814      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36815      * (the default format used is "m/d/y").
36816      * <br />Usage:
36817      * <pre><code>
36818 //All of these calls set the same date value (May 4, 2006)
36819
36820 //Pass a date object:
36821 var dt = new Date('5/4/06');
36822 dateField.setValue(dt);
36823
36824 //Pass a date string (default format):
36825 dateField.setValue('5/4/06');
36826
36827 //Pass a date string (custom format):
36828 dateField.format = 'Y-m-d';
36829 dateField.setValue('2006-5-4');
36830 </code></pre>
36831      * @param {String/Date} date The date or valid date string
36832      */
36833     setValue : function(date){
36834         if (this.hiddenField) {
36835             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36836         }
36837         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36838     },
36839
36840     // private
36841     parseDate : function(value){
36842         if(!value || value instanceof Date){
36843             return value;
36844         }
36845         var v = Date.parseDate(value, this.format);
36846         if(!v && this.altFormats){
36847             if(!this.altFormatsArray){
36848                 this.altFormatsArray = this.altFormats.split("|");
36849             }
36850             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36851                 v = Date.parseDate(value, this.altFormatsArray[i]);
36852             }
36853         }
36854         return v;
36855     },
36856
36857     // private
36858     formatDate : function(date, fmt){
36859         return (!date || !(date instanceof Date)) ?
36860                date : date.dateFormat(fmt || this.format);
36861     },
36862
36863     // private
36864     menuListeners : {
36865         select: function(m, d){
36866             this.setValue(d);
36867             this.fireEvent('select', this, d);
36868         },
36869         show : function(){ // retain focus styling
36870             this.onFocus();
36871         },
36872         hide : function(){
36873             this.focus.defer(10, this);
36874             var ml = this.menuListeners;
36875             this.menu.un("select", ml.select,  this);
36876             this.menu.un("show", ml.show,  this);
36877             this.menu.un("hide", ml.hide,  this);
36878         }
36879     },
36880
36881     // private
36882     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36883     onTriggerClick : function(){
36884         if(this.disabled){
36885             return;
36886         }
36887         if(this.menu == null){
36888             this.menu = new Roo.menu.DateMenu();
36889         }
36890         Roo.apply(this.menu.picker,  {
36891             showClear: this.allowBlank,
36892             minDate : this.minValue,
36893             maxDate : this.maxValue,
36894             disabledDatesRE : this.ddMatch,
36895             disabledDatesText : this.disabledDatesText,
36896             disabledDays : this.disabledDays,
36897             disabledDaysText : this.disabledDaysText,
36898             format : this.format,
36899             minText : String.format(this.minText, this.formatDate(this.minValue)),
36900             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36901         });
36902         this.menu.on(Roo.apply({}, this.menuListeners, {
36903             scope:this
36904         }));
36905         this.menu.picker.setValue(this.getValue() || new Date());
36906         this.menu.show(this.el, "tl-bl?");
36907     },
36908
36909     beforeBlur : function(){
36910         var v = this.parseDate(this.getRawValue());
36911         if(v){
36912             this.setValue(v);
36913         }
36914     }
36915
36916     /** @cfg {Boolean} grow @hide */
36917     /** @cfg {Number} growMin @hide */
36918     /** @cfg {Number} growMax @hide */
36919     /**
36920      * @hide
36921      * @method autoSize
36922      */
36923 });/*
36924  * Based on:
36925  * Ext JS Library 1.1.1
36926  * Copyright(c) 2006-2007, Ext JS, LLC.
36927  *
36928  * Originally Released Under LGPL - original licence link has changed is not relivant.
36929  *
36930  * Fork - LGPL
36931  * <script type="text/javascript">
36932  */
36933  
36934
36935 /**
36936  * @class Roo.form.ComboBox
36937  * @extends Roo.form.TriggerField
36938  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36939  * @constructor
36940  * Create a new ComboBox.
36941  * @param {Object} config Configuration options
36942  */
36943 Roo.form.ComboBox = function(config){
36944     Roo.form.ComboBox.superclass.constructor.call(this, config);
36945     this.addEvents({
36946         /**
36947          * @event expand
36948          * Fires when the dropdown list is expanded
36949              * @param {Roo.form.ComboBox} combo This combo box
36950              */
36951         'expand' : true,
36952         /**
36953          * @event collapse
36954          * Fires when the dropdown list is collapsed
36955              * @param {Roo.form.ComboBox} combo This combo box
36956              */
36957         'collapse' : true,
36958         /**
36959          * @event beforeselect
36960          * Fires before a list item is selected. Return false to cancel the selection.
36961              * @param {Roo.form.ComboBox} combo This combo box
36962              * @param {Roo.data.Record} record The data record returned from the underlying store
36963              * @param {Number} index The index of the selected item in the dropdown list
36964              */
36965         'beforeselect' : true,
36966         /**
36967          * @event select
36968          * Fires when a list item is selected
36969              * @param {Roo.form.ComboBox} combo This combo box
36970              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36971              * @param {Number} index The index of the selected item in the dropdown list
36972              */
36973         'select' : true,
36974         /**
36975          * @event beforequery
36976          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36977          * The event object passed has these properties:
36978              * @param {Roo.form.ComboBox} combo This combo box
36979              * @param {String} query The query
36980              * @param {Boolean} forceAll true to force "all" query
36981              * @param {Boolean} cancel true to cancel the query
36982              * @param {Object} e The query event object
36983              */
36984         'beforequery': true,
36985          /**
36986          * @event add
36987          * Fires when the 'add' icon is pressed (add a listener to enable add button)
36988              * @param {Roo.form.ComboBox} combo This combo box
36989              */
36990         'add' : true,
36991         /**
36992          * @event edit
36993          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
36994              * @param {Roo.form.ComboBox} combo This combo box
36995              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
36996              */
36997         'edit' : true
36998         
36999         
37000     });
37001     if(this.transform){
37002         this.allowDomMove = false;
37003         var s = Roo.getDom(this.transform);
37004         if(!this.hiddenName){
37005             this.hiddenName = s.name;
37006         }
37007         if(!this.store){
37008             this.mode = 'local';
37009             var d = [], opts = s.options;
37010             for(var i = 0, len = opts.length;i < len; i++){
37011                 var o = opts[i];
37012                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37013                 if(o.selected) {
37014                     this.value = value;
37015                 }
37016                 d.push([value, o.text]);
37017             }
37018             this.store = new Roo.data.SimpleStore({
37019                 'id': 0,
37020                 fields: ['value', 'text'],
37021                 data : d
37022             });
37023             this.valueField = 'value';
37024             this.displayField = 'text';
37025         }
37026         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37027         if(!this.lazyRender){
37028             this.target = true;
37029             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37030             s.parentNode.removeChild(s); // remove it
37031             this.render(this.el.parentNode);
37032         }else{
37033             s.parentNode.removeChild(s); // remove it
37034         }
37035
37036     }
37037     if (this.store) {
37038         this.store = Roo.factory(this.store, Roo.data);
37039     }
37040     
37041     this.selectedIndex = -1;
37042     if(this.mode == 'local'){
37043         if(config.queryDelay === undefined){
37044             this.queryDelay = 10;
37045         }
37046         if(config.minChars === undefined){
37047             this.minChars = 0;
37048         }
37049     }
37050 };
37051
37052 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37053     /**
37054      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37055      */
37056     /**
37057      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37058      * rendering into an Roo.Editor, defaults to false)
37059      */
37060     /**
37061      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37062      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37063      */
37064     /**
37065      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37066      */
37067     /**
37068      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37069      * the dropdown list (defaults to undefined, with no header element)
37070      */
37071
37072      /**
37073      * @cfg {String/Roo.Template} tpl The template to use to render the output
37074      */
37075      
37076     // private
37077     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37078     /**
37079      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37080      */
37081     listWidth: undefined,
37082     /**
37083      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37084      * mode = 'remote' or 'text' if mode = 'local')
37085      */
37086     displayField: undefined,
37087     /**
37088      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37089      * mode = 'remote' or 'value' if mode = 'local'). 
37090      * Note: use of a valueField requires the user make a selection
37091      * in order for a value to be mapped.
37092      */
37093     valueField: undefined,
37094     /**
37095      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37096      * field's data value (defaults to the underlying DOM element's name)
37097      */
37098     hiddenName: undefined,
37099     /**
37100      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37101      */
37102     listClass: '',
37103     /**
37104      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37105      */
37106     selectedClass: 'x-combo-selected',
37107     /**
37108      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37109      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37110      * which displays a downward arrow icon).
37111      */
37112     triggerClass : 'x-form-arrow-trigger',
37113     /**
37114      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37115      */
37116     shadow:'sides',
37117     /**
37118      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37119      * anchor positions (defaults to 'tl-bl')
37120      */
37121     listAlign: 'tl-bl?',
37122     /**
37123      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37124      */
37125     maxHeight: 300,
37126     /**
37127      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37128      * query specified by the allQuery config option (defaults to 'query')
37129      */
37130     triggerAction: 'query',
37131     /**
37132      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37133      * (defaults to 4, does not apply if editable = false)
37134      */
37135     minChars : 4,
37136     /**
37137      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37138      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37139      */
37140     typeAhead: false,
37141     /**
37142      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37143      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37144      */
37145     queryDelay: 500,
37146     /**
37147      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37148      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37149      */
37150     pageSize: 0,
37151     /**
37152      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37153      * when editable = true (defaults to false)
37154      */
37155     selectOnFocus:false,
37156     /**
37157      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37158      */
37159     queryParam: 'query',
37160     /**
37161      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37162      * when mode = 'remote' (defaults to 'Loading...')
37163      */
37164     loadingText: 'Loading...',
37165     /**
37166      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37167      */
37168     resizable: false,
37169     /**
37170      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37171      */
37172     handleHeight : 8,
37173     /**
37174      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37175      * traditional select (defaults to true)
37176      */
37177     editable: true,
37178     /**
37179      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37180      */
37181     allQuery: '',
37182     /**
37183      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37184      */
37185     mode: 'remote',
37186     /**
37187      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37188      * listWidth has a higher value)
37189      */
37190     minListWidth : 70,
37191     /**
37192      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37193      * allow the user to set arbitrary text into the field (defaults to false)
37194      */
37195     forceSelection:false,
37196     /**
37197      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37198      * if typeAhead = true (defaults to 250)
37199      */
37200     typeAheadDelay : 250,
37201     /**
37202      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37203      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37204      */
37205     valueNotFoundText : undefined,
37206     /**
37207      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37208      */
37209     blockFocus : false,
37210     
37211     /**
37212      * @cfg {Boolean} disableClear Disable showing of clear button.
37213      */
37214     disableClear : false,
37215     /**
37216      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37217      */
37218     alwaysQuery : false,
37219     
37220     //private
37221     addicon : false,
37222     editicon: false,
37223     
37224     
37225     // private
37226     onRender : function(ct, position){
37227         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37228         if(this.hiddenName){
37229             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37230                     'before', true);
37231             this.hiddenField.value =
37232                 this.hiddenValue !== undefined ? this.hiddenValue :
37233                 this.value !== undefined ? this.value : '';
37234
37235             // prevent input submission
37236             this.el.dom.removeAttribute('name');
37237         }
37238         if(Roo.isGecko){
37239             this.el.dom.setAttribute('autocomplete', 'off');
37240         }
37241
37242         var cls = 'x-combo-list';
37243
37244         this.list = new Roo.Layer({
37245             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37246         });
37247
37248         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37249         this.list.setWidth(lw);
37250         this.list.swallowEvent('mousewheel');
37251         this.assetHeight = 0;
37252
37253         if(this.title){
37254             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37255             this.assetHeight += this.header.getHeight();
37256         }
37257
37258         this.innerList = this.list.createChild({cls:cls+'-inner'});
37259         this.innerList.on('mouseover', this.onViewOver, this);
37260         this.innerList.on('mousemove', this.onViewMove, this);
37261         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37262         
37263         if(this.allowBlank && !this.pageSize && !this.disableClear){
37264             this.footer = this.list.createChild({cls:cls+'-ft'});
37265             this.pageTb = new Roo.Toolbar(this.footer);
37266            
37267         }
37268         if(this.pageSize){
37269             this.footer = this.list.createChild({cls:cls+'-ft'});
37270             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37271                     {pageSize: this.pageSize});
37272             
37273         }
37274         
37275         if (this.pageTb && this.allowBlank && !this.disableClear) {
37276             var _this = this;
37277             this.pageTb.add(new Roo.Toolbar.Fill(), {
37278                 cls: 'x-btn-icon x-btn-clear',
37279                 text: '&#160;',
37280                 handler: function()
37281                 {
37282                     _this.collapse();
37283                     _this.clearValue();
37284                     _this.onSelect(false, -1);
37285                 }
37286             });
37287         }
37288         if (this.footer) {
37289             this.assetHeight += this.footer.getHeight();
37290         }
37291         
37292
37293         if(!this.tpl){
37294             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37295         }
37296
37297         this.view = new Roo.View(this.innerList, this.tpl, {
37298             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37299         });
37300
37301         this.view.on('click', this.onViewClick, this);
37302
37303         this.store.on('beforeload', this.onBeforeLoad, this);
37304         this.store.on('load', this.onLoad, this);
37305         this.store.on('loadexception', this.collapse, this);
37306
37307         if(this.resizable){
37308             this.resizer = new Roo.Resizable(this.list,  {
37309                pinned:true, handles:'se'
37310             });
37311             this.resizer.on('resize', function(r, w, h){
37312                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37313                 this.listWidth = w;
37314                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37315                 this.restrictHeight();
37316             }, this);
37317             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37318         }
37319         if(!this.editable){
37320             this.editable = true;
37321             this.setEditable(false);
37322         }  
37323         
37324         
37325         if (typeof(this.events.add.listeners) != 'undefined') {
37326             
37327             this.addicon = this.wrap.createChild(
37328                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37329        
37330             this.addicon.on('click', function(e) {
37331                 this.fireEvent('add', this);
37332             }, this);
37333         }
37334         if (typeof(this.events.edit.listeners) != 'undefined') {
37335             
37336             this.editicon = this.wrap.createChild(
37337                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37338             if (this.addicon) {
37339                 this.editicon.setStyle('margin-left', '40px');
37340             }
37341             this.editicon.on('click', function(e) {
37342                 
37343                 // we fire even  if inothing is selected..
37344                 this.fireEvent('edit', this, this.lastData );
37345                 
37346             }, this);
37347         }
37348         
37349         
37350         
37351     },
37352
37353     // private
37354     initEvents : function(){
37355         Roo.form.ComboBox.superclass.initEvents.call(this);
37356
37357         this.keyNav = new Roo.KeyNav(this.el, {
37358             "up" : function(e){
37359                 this.inKeyMode = true;
37360                 this.selectPrev();
37361             },
37362
37363             "down" : function(e){
37364                 if(!this.isExpanded()){
37365                     this.onTriggerClick();
37366                 }else{
37367                     this.inKeyMode = true;
37368                     this.selectNext();
37369                 }
37370             },
37371
37372             "enter" : function(e){
37373                 this.onViewClick();
37374                 //return true;
37375             },
37376
37377             "esc" : function(e){
37378                 this.collapse();
37379             },
37380
37381             "tab" : function(e){
37382                 this.onViewClick(false);
37383                 return true;
37384             },
37385
37386             scope : this,
37387
37388             doRelay : function(foo, bar, hname){
37389                 if(hname == 'down' || this.scope.isExpanded()){
37390                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37391                 }
37392                 return true;
37393             },
37394
37395             forceKeyDown: true
37396         });
37397         this.queryDelay = Math.max(this.queryDelay || 10,
37398                 this.mode == 'local' ? 10 : 250);
37399         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37400         if(this.typeAhead){
37401             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37402         }
37403         if(this.editable !== false){
37404             this.el.on("keyup", this.onKeyUp, this);
37405         }
37406         if(this.forceSelection){
37407             this.on('blur', this.doForce, this);
37408         }
37409     },
37410
37411     onDestroy : function(){
37412         if(this.view){
37413             this.view.setStore(null);
37414             this.view.el.removeAllListeners();
37415             this.view.el.remove();
37416             this.view.purgeListeners();
37417         }
37418         if(this.list){
37419             this.list.destroy();
37420         }
37421         if(this.store){
37422             this.store.un('beforeload', this.onBeforeLoad, this);
37423             this.store.un('load', this.onLoad, this);
37424             this.store.un('loadexception', this.collapse, this);
37425         }
37426         Roo.form.ComboBox.superclass.onDestroy.call(this);
37427     },
37428
37429     // private
37430     fireKey : function(e){
37431         if(e.isNavKeyPress() && !this.list.isVisible()){
37432             this.fireEvent("specialkey", this, e);
37433         }
37434     },
37435
37436     // private
37437     onResize: function(w, h){
37438         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37439         
37440         if(typeof w != 'number'){
37441             // we do not handle it!?!?
37442             return;
37443         }
37444         var tw = this.trigger.getWidth();
37445         tw += this.addicon ? this.addicon.getWidth() : 0;
37446         tw += this.editicon ? this.editicon.getWidth() : 0;
37447         var x = w - tw;
37448         this.el.setWidth( this.adjustWidth('input', x));
37449             
37450         this.trigger.setStyle('left', x+'px');
37451         
37452         if(this.list && this.listWidth === undefined){
37453             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37454             this.list.setWidth(lw);
37455             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37456         }
37457         
37458     
37459         
37460     },
37461
37462     /**
37463      * Allow or prevent the user from directly editing the field text.  If false is passed,
37464      * the user will only be able to select from the items defined in the dropdown list.  This method
37465      * is the runtime equivalent of setting the 'editable' config option at config time.
37466      * @param {Boolean} value True to allow the user to directly edit the field text
37467      */
37468     setEditable : function(value){
37469         if(value == this.editable){
37470             return;
37471         }
37472         this.editable = value;
37473         if(!value){
37474             this.el.dom.setAttribute('readOnly', true);
37475             this.el.on('mousedown', this.onTriggerClick,  this);
37476             this.el.addClass('x-combo-noedit');
37477         }else{
37478             this.el.dom.setAttribute('readOnly', false);
37479             this.el.un('mousedown', this.onTriggerClick,  this);
37480             this.el.removeClass('x-combo-noedit');
37481         }
37482     },
37483
37484     // private
37485     onBeforeLoad : function(){
37486         if(!this.hasFocus){
37487             return;
37488         }
37489         this.innerList.update(this.loadingText ?
37490                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37491         this.restrictHeight();
37492         this.selectedIndex = -1;
37493     },
37494
37495     // private
37496     onLoad : function(){
37497         if(!this.hasFocus){
37498             return;
37499         }
37500         if(this.store.getCount() > 0){
37501             this.expand();
37502             this.restrictHeight();
37503             if(this.lastQuery == this.allQuery){
37504                 if(this.editable){
37505                     this.el.dom.select();
37506                 }
37507                 if(!this.selectByValue(this.value, true)){
37508                     this.select(0, true);
37509                 }
37510             }else{
37511                 this.selectNext();
37512                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37513                     this.taTask.delay(this.typeAheadDelay);
37514                 }
37515             }
37516         }else{
37517             this.onEmptyResults();
37518         }
37519         //this.el.focus();
37520     },
37521
37522     // private
37523     onTypeAhead : function(){
37524         if(this.store.getCount() > 0){
37525             var r = this.store.getAt(0);
37526             var newValue = r.data[this.displayField];
37527             var len = newValue.length;
37528             var selStart = this.getRawValue().length;
37529             if(selStart != len){
37530                 this.setRawValue(newValue);
37531                 this.selectText(selStart, newValue.length);
37532             }
37533         }
37534     },
37535
37536     // private
37537     onSelect : function(record, index){
37538         if(this.fireEvent('beforeselect', this, record, index) !== false){
37539             this.setFromData(index > -1 ? record.data : false);
37540             this.collapse();
37541             this.fireEvent('select', this, record, index);
37542         }
37543     },
37544
37545     /**
37546      * Returns the currently selected field value or empty string if no value is set.
37547      * @return {String} value The selected value
37548      */
37549     getValue : function(){
37550         if(this.valueField){
37551             return typeof this.value != 'undefined' ? this.value : '';
37552         }else{
37553             return Roo.form.ComboBox.superclass.getValue.call(this);
37554         }
37555     },
37556
37557     /**
37558      * Clears any text/value currently set in the field
37559      */
37560     clearValue : function(){
37561         if(this.hiddenField){
37562             this.hiddenField.value = '';
37563         }
37564         this.value = '';
37565         this.setRawValue('');
37566         this.lastSelectionText = '';
37567         this.applyEmptyText();
37568     },
37569
37570     /**
37571      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37572      * will be displayed in the field.  If the value does not match the data value of an existing item,
37573      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37574      * Otherwise the field will be blank (although the value will still be set).
37575      * @param {String} value The value to match
37576      */
37577     setValue : function(v){
37578         var text = v;
37579         if(this.valueField){
37580             var r = this.findRecord(this.valueField, v);
37581             if(r){
37582                 text = r.data[this.displayField];
37583             }else if(this.valueNotFoundText !== undefined){
37584                 text = this.valueNotFoundText;
37585             }
37586         }
37587         this.lastSelectionText = text;
37588         if(this.hiddenField){
37589             this.hiddenField.value = v;
37590         }
37591         Roo.form.ComboBox.superclass.setValue.call(this, text);
37592         this.value = v;
37593     },
37594     /**
37595      * @property {Object} the last set data for the element
37596      */
37597     
37598     lastData : false,
37599     /**
37600      * Sets the value of the field based on a object which is related to the record format for the store.
37601      * @param {Object} value the value to set as. or false on reset?
37602      */
37603     setFromData : function(o){
37604         var dv = ''; // display value
37605         var vv = ''; // value value..
37606         this.lastData = o;
37607         if (this.displayField) {
37608             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37609         } else {
37610             // this is an error condition!!!
37611             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37612         }
37613         
37614         if(this.valueField){
37615             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37616         }
37617         if(this.hiddenField){
37618             this.hiddenField.value = vv;
37619             
37620             this.lastSelectionText = dv;
37621             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37622             this.value = vv;
37623             return;
37624         }
37625         // no hidden field.. - we store the value in 'value', but still display
37626         // display field!!!!
37627         this.lastSelectionText = dv;
37628         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37629         this.value = vv;
37630         
37631         
37632     },
37633     // private
37634     reset : function(){
37635         // overridden so that last data is reset..
37636         this.setValue(this.originalValue);
37637         this.clearInvalid();
37638         this.lastData = false;
37639     },
37640     // private
37641     findRecord : function(prop, value){
37642         var record;
37643         if(this.store.getCount() > 0){
37644             this.store.each(function(r){
37645                 if(r.data[prop] == value){
37646                     record = r;
37647                     return false;
37648                 }
37649             });
37650         }
37651         return record;
37652     },
37653
37654     // private
37655     onViewMove : function(e, t){
37656         this.inKeyMode = false;
37657     },
37658
37659     // private
37660     onViewOver : function(e, t){
37661         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37662             return;
37663         }
37664         var item = this.view.findItemFromChild(t);
37665         if(item){
37666             var index = this.view.indexOf(item);
37667             this.select(index, false);
37668         }
37669     },
37670
37671     // private
37672     onViewClick : function(doFocus){
37673         var index = this.view.getSelectedIndexes()[0];
37674         var r = this.store.getAt(index);
37675         if(r){
37676             this.onSelect(r, index);
37677         }
37678         if(doFocus !== false && !this.blockFocus){
37679             this.el.focus();
37680         }
37681     },
37682
37683     // private
37684     restrictHeight : function(){
37685         this.innerList.dom.style.height = '';
37686         var inner = this.innerList.dom;
37687         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37688         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37689         this.list.beginUpdate();
37690         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37691         this.list.alignTo(this.el, this.listAlign);
37692         this.list.endUpdate();
37693     },
37694
37695     // private
37696     onEmptyResults : function(){
37697         this.collapse();
37698     },
37699
37700     /**
37701      * Returns true if the dropdown list is expanded, else false.
37702      */
37703     isExpanded : function(){
37704         return this.list.isVisible();
37705     },
37706
37707     /**
37708      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37709      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37710      * @param {String} value The data value of the item to select
37711      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37712      * selected item if it is not currently in view (defaults to true)
37713      * @return {Boolean} True if the value matched an item in the list, else false
37714      */
37715     selectByValue : function(v, scrollIntoView){
37716         if(v !== undefined && v !== null){
37717             var r = this.findRecord(this.valueField || this.displayField, v);
37718             if(r){
37719                 this.select(this.store.indexOf(r), scrollIntoView);
37720                 return true;
37721             }
37722         }
37723         return false;
37724     },
37725
37726     /**
37727      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37728      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37729      * @param {Number} index The zero-based index of the list item to select
37730      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37731      * selected item if it is not currently in view (defaults to true)
37732      */
37733     select : function(index, scrollIntoView){
37734         this.selectedIndex = index;
37735         this.view.select(index);
37736         if(scrollIntoView !== false){
37737             var el = this.view.getNode(index);
37738             if(el){
37739                 this.innerList.scrollChildIntoView(el, false);
37740             }
37741         }
37742     },
37743
37744     // private
37745     selectNext : function(){
37746         var ct = this.store.getCount();
37747         if(ct > 0){
37748             if(this.selectedIndex == -1){
37749                 this.select(0);
37750             }else if(this.selectedIndex < ct-1){
37751                 this.select(this.selectedIndex+1);
37752             }
37753         }
37754     },
37755
37756     // private
37757     selectPrev : function(){
37758         var ct = this.store.getCount();
37759         if(ct > 0){
37760             if(this.selectedIndex == -1){
37761                 this.select(0);
37762             }else if(this.selectedIndex != 0){
37763                 this.select(this.selectedIndex-1);
37764             }
37765         }
37766     },
37767
37768     // private
37769     onKeyUp : function(e){
37770         if(this.editable !== false && !e.isSpecialKey()){
37771             this.lastKey = e.getKey();
37772             this.dqTask.delay(this.queryDelay);
37773         }
37774     },
37775
37776     // private
37777     validateBlur : function(){
37778         return !this.list || !this.list.isVisible();   
37779     },
37780
37781     // private
37782     initQuery : function(){
37783         this.doQuery(this.getRawValue());
37784     },
37785
37786     // private
37787     doForce : function(){
37788         if(this.el.dom.value.length > 0){
37789             this.el.dom.value =
37790                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37791             this.applyEmptyText();
37792         }
37793     },
37794
37795     /**
37796      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37797      * query allowing the query action to be canceled if needed.
37798      * @param {String} query The SQL query to execute
37799      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37800      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37801      * saved in the current store (defaults to false)
37802      */
37803     doQuery : function(q, forceAll){
37804         if(q === undefined || q === null){
37805             q = '';
37806         }
37807         var qe = {
37808             query: q,
37809             forceAll: forceAll,
37810             combo: this,
37811             cancel:false
37812         };
37813         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37814             return false;
37815         }
37816         q = qe.query;
37817         forceAll = qe.forceAll;
37818         if(forceAll === true || (q.length >= this.minChars)){
37819             if(this.lastQuery != q || this.alwaysQuery){
37820                 this.lastQuery = q;
37821                 if(this.mode == 'local'){
37822                     this.selectedIndex = -1;
37823                     if(forceAll){
37824                         this.store.clearFilter();
37825                     }else{
37826                         this.store.filter(this.displayField, q);
37827                     }
37828                     this.onLoad();
37829                 }else{
37830                     this.store.baseParams[this.queryParam] = q;
37831                     this.store.load({
37832                         params: this.getParams(q)
37833                     });
37834                     this.expand();
37835                 }
37836             }else{
37837                 this.selectedIndex = -1;
37838                 this.onLoad();   
37839             }
37840         }
37841     },
37842
37843     // private
37844     getParams : function(q){
37845         var p = {};
37846         //p[this.queryParam] = q;
37847         if(this.pageSize){
37848             p.start = 0;
37849             p.limit = this.pageSize;
37850         }
37851         return p;
37852     },
37853
37854     /**
37855      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37856      */
37857     collapse : function(){
37858         if(!this.isExpanded()){
37859             return;
37860         }
37861         this.list.hide();
37862         Roo.get(document).un('mousedown', this.collapseIf, this);
37863         Roo.get(document).un('mousewheel', this.collapseIf, this);
37864         if (!this.editable) {
37865             Roo.get(document).un('keydown', this.listKeyPress, this);
37866         }
37867         this.fireEvent('collapse', this);
37868     },
37869
37870     // private
37871     collapseIf : function(e){
37872         if(!e.within(this.wrap) && !e.within(this.list)){
37873             this.collapse();
37874         }
37875     },
37876
37877     /**
37878      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37879      */
37880     expand : function(){
37881         if(this.isExpanded() || !this.hasFocus){
37882             return;
37883         }
37884         this.list.alignTo(this.el, this.listAlign);
37885         this.list.show();
37886         Roo.get(document).on('mousedown', this.collapseIf, this);
37887         Roo.get(document).on('mousewheel', this.collapseIf, this);
37888         if (!this.editable) {
37889             Roo.get(document).on('keydown', this.listKeyPress, this);
37890         }
37891         
37892         this.fireEvent('expand', this);
37893     },
37894
37895     // private
37896     // Implements the default empty TriggerField.onTriggerClick function
37897     onTriggerClick : function(){
37898         if(this.disabled){
37899             return;
37900         }
37901         if(this.isExpanded()){
37902             this.collapse();
37903             if (!this.blockFocus) {
37904                 this.el.focus();
37905             }
37906             
37907         }else {
37908             this.hasFocus = true;
37909             if(this.triggerAction == 'all') {
37910                 this.doQuery(this.allQuery, true);
37911             } else {
37912                 this.doQuery(this.getRawValue());
37913             }
37914             if (!this.blockFocus) {
37915                 this.el.focus();
37916             }
37917         }
37918     },
37919     listKeyPress : function(e)
37920     {
37921         //Roo.log('listkeypress');
37922         // scroll to first matching element based on key pres..
37923         if (e.isSpecialKey()) {
37924             return false;
37925         }
37926         var k = String.fromCharCode(e.getKey()).toUpperCase();
37927         //Roo.log(k);
37928         var match  = false;
37929         var csel = this.view.getSelectedNodes();
37930         var cselitem = false;
37931         if (csel.length) {
37932             var ix = this.view.indexOf(csel[0]);
37933             cselitem  = this.store.getAt(ix);
37934             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
37935                 cselitem = false;
37936             }
37937             
37938         }
37939         
37940         this.store.each(function(v) { 
37941             if (cselitem) {
37942                 // start at existing selection.
37943                 if (cselitem.id == v.id) {
37944                     cselitem = false;
37945                 }
37946                 return;
37947             }
37948                 
37949             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
37950                 match = this.store.indexOf(v);
37951                 return false;
37952             }
37953         }, this);
37954         
37955         if (match === false) {
37956             return true; // no more action?
37957         }
37958         // scroll to?
37959         this.view.select(match);
37960         var sn = Roo.get(this.view.getSelectedNodes()[0])
37961         sn.scrollIntoView(sn.dom.parentNode, false);
37962     }
37963
37964     /** 
37965     * @cfg {Boolean} grow 
37966     * @hide 
37967     */
37968     /** 
37969     * @cfg {Number} growMin 
37970     * @hide 
37971     */
37972     /** 
37973     * @cfg {Number} growMax 
37974     * @hide 
37975     */
37976     /**
37977      * @hide
37978      * @method autoSize
37979      */
37980 });/*
37981  * Based on:
37982  * Ext JS Library 1.1.1
37983  * Copyright(c) 2006-2007, Ext JS, LLC.
37984  *
37985  * Originally Released Under LGPL - original licence link has changed is not relivant.
37986  *
37987  * Fork - LGPL
37988  * <script type="text/javascript">
37989  */
37990 /**
37991  * @class Roo.form.Checkbox
37992  * @extends Roo.form.Field
37993  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37994  * @constructor
37995  * Creates a new Checkbox
37996  * @param {Object} config Configuration options
37997  */
37998 Roo.form.Checkbox = function(config){
37999     Roo.form.Checkbox.superclass.constructor.call(this, config);
38000     this.addEvents({
38001         /**
38002          * @event check
38003          * Fires when the checkbox is checked or unchecked.
38004              * @param {Roo.form.Checkbox} this This checkbox
38005              * @param {Boolean} checked The new checked value
38006              */
38007         check : true
38008     });
38009 };
38010
38011 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38012     /**
38013      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38014      */
38015     focusClass : undefined,
38016     /**
38017      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38018      */
38019     fieldClass: "x-form-field",
38020     /**
38021      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38022      */
38023     checked: false,
38024     /**
38025      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38026      * {tag: "input", type: "checkbox", autocomplete: "off"})
38027      */
38028     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38029     /**
38030      * @cfg {String} boxLabel The text that appears beside the checkbox
38031      */
38032     boxLabel : "",
38033     /**
38034      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38035      */  
38036     inputValue : '1',
38037     /**
38038      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38039      */
38040      valueOff: '0', // value when not checked..
38041
38042     actionMode : 'viewEl', 
38043     //
38044     // private
38045     itemCls : 'x-menu-check-item x-form-item',
38046     groupClass : 'x-menu-group-item',
38047     inputType : 'hidden',
38048     
38049     
38050     inSetChecked: false, // check that we are not calling self...
38051     
38052     inputElement: false, // real input element?
38053     basedOn: false, // ????
38054     
38055     isFormField: true, // not sure where this is needed!!!!
38056
38057     onResize : function(){
38058         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38059         if(!this.boxLabel){
38060             this.el.alignTo(this.wrap, 'c-c');
38061         }
38062     },
38063
38064     initEvents : function(){
38065         Roo.form.Checkbox.superclass.initEvents.call(this);
38066         this.el.on("click", this.onClick,  this);
38067         this.el.on("change", this.onClick,  this);
38068     },
38069
38070
38071     getResizeEl : function(){
38072         return this.wrap;
38073     },
38074
38075     getPositionEl : function(){
38076         return this.wrap;
38077     },
38078
38079     // private
38080     onRender : function(ct, position){
38081         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38082         /*
38083         if(this.inputValue !== undefined){
38084             this.el.dom.value = this.inputValue;
38085         }
38086         */
38087         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38088         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38089         var viewEl = this.wrap.createChild({ 
38090             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38091         this.viewEl = viewEl;   
38092         this.wrap.on('click', this.onClick,  this); 
38093         
38094         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38095         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38096         
38097         
38098         
38099         if(this.boxLabel){
38100             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38101         //    viewEl.on('click', this.onClick,  this); 
38102         }
38103         //if(this.checked){
38104             this.setChecked(this.checked);
38105         //}else{
38106             //this.checked = this.el.dom;
38107         //}
38108
38109     },
38110
38111     // private
38112     initValue : Roo.emptyFn,
38113
38114     /**
38115      * Returns the checked state of the checkbox.
38116      * @return {Boolean} True if checked, else false
38117      */
38118     getValue : function(){
38119         if(this.el){
38120             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38121         }
38122         return this.valueOff;
38123         
38124     },
38125
38126         // private
38127     onClick : function(){ 
38128         this.setChecked(!this.checked);
38129
38130         //if(this.el.dom.checked != this.checked){
38131         //    this.setValue(this.el.dom.checked);
38132        // }
38133     },
38134
38135     /**
38136      * Sets the checked state of the checkbox.
38137      * On is always based on a string comparison between inputValue and the param.
38138      * @param {Boolean/String} value - the value to set 
38139      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38140      */
38141     setValue : function(v,suppressEvent){
38142         
38143         
38144         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38145         //if(this.el && this.el.dom){
38146         //    this.el.dom.checked = this.checked;
38147         //    this.el.dom.defaultChecked = this.checked;
38148         //}
38149         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38150         //this.fireEvent("check", this, this.checked);
38151     },
38152     // private..
38153     setChecked : function(state,suppressEvent)
38154     {
38155         if (this.inSetChecked) {
38156             this.checked = state;
38157             return;
38158         }
38159         
38160     
38161         if(this.wrap){
38162             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38163         }
38164         this.checked = state;
38165         if(suppressEvent !== true){
38166             this.fireEvent('check', this, state);
38167         }
38168         this.inSetChecked = true;
38169         this.el.dom.value = state ? this.inputValue : this.valueOff;
38170         this.inSetChecked = false;
38171         
38172     },
38173     // handle setting of hidden value by some other method!!?!?
38174     setFromHidden: function()
38175     {
38176         if(!this.el){
38177             return;
38178         }
38179         //console.log("SET FROM HIDDEN");
38180         //alert('setFrom hidden');
38181         this.setValue(this.el.dom.value);
38182     },
38183     
38184     onDestroy : function()
38185     {
38186         if(this.viewEl){
38187             Roo.get(this.viewEl).remove();
38188         }
38189          
38190         Roo.form.Checkbox.superclass.onDestroy.call(this);
38191     }
38192
38193 });/*
38194  * Based on:
38195  * Ext JS Library 1.1.1
38196  * Copyright(c) 2006-2007, Ext JS, LLC.
38197  *
38198  * Originally Released Under LGPL - original licence link has changed is not relivant.
38199  *
38200  * Fork - LGPL
38201  * <script type="text/javascript">
38202  */
38203  
38204 /**
38205  * @class Roo.form.Radio
38206  * @extends Roo.form.Checkbox
38207  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38208  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38209  * @constructor
38210  * Creates a new Radio
38211  * @param {Object} config Configuration options
38212  */
38213 Roo.form.Radio = function(){
38214     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38215 };
38216 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38217     inputType: 'radio',
38218
38219     /**
38220      * If this radio is part of a group, it will return the selected value
38221      * @return {String}
38222      */
38223     getGroupValue : function(){
38224         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38225     }
38226 });//<script type="text/javascript">
38227
38228 /*
38229  * Ext JS Library 1.1.1
38230  * Copyright(c) 2006-2007, Ext JS, LLC.
38231  * licensing@extjs.com
38232  * 
38233  * http://www.extjs.com/license
38234  */
38235  
38236  /*
38237   * 
38238   * Known bugs:
38239   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38240   * - IE ? - no idea how much works there.
38241   * 
38242   * 
38243   * 
38244   */
38245  
38246
38247 /**
38248  * @class Ext.form.HtmlEditor
38249  * @extends Ext.form.Field
38250  * Provides a lightweight HTML Editor component.
38251  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38252  * 
38253  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38254  * supported by this editor.</b><br/><br/>
38255  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38256  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38257  */
38258 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38259       /**
38260      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38261      */
38262     toolbars : false,
38263     /**
38264      * @cfg {String} createLinkText The default text for the create link prompt
38265      */
38266     createLinkText : 'Please enter the URL for the link:',
38267     /**
38268      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38269      */
38270     defaultLinkValue : 'http:/'+'/',
38271    
38272     
38273     // id of frame..
38274     frameId: false,
38275     
38276     // private properties
38277     validationEvent : false,
38278     deferHeight: true,
38279     initialized : false,
38280     activated : false,
38281     sourceEditMode : false,
38282     onFocus : Roo.emptyFn,
38283     iframePad:3,
38284     hideMode:'offsets',
38285     defaultAutoCreate : {
38286         tag: "textarea",
38287         style:"width:500px;height:300px;",
38288         autocomplete: "off"
38289     },
38290
38291     // private
38292     initComponent : function(){
38293         this.addEvents({
38294             /**
38295              * @event initialize
38296              * Fires when the editor is fully initialized (including the iframe)
38297              * @param {HtmlEditor} this
38298              */
38299             initialize: true,
38300             /**
38301              * @event activate
38302              * Fires when the editor is first receives the focus. Any insertion must wait
38303              * until after this event.
38304              * @param {HtmlEditor} this
38305              */
38306             activate: true,
38307              /**
38308              * @event beforesync
38309              * Fires before the textarea is updated with content from the editor iframe. Return false
38310              * to cancel the sync.
38311              * @param {HtmlEditor} this
38312              * @param {String} html
38313              */
38314             beforesync: true,
38315              /**
38316              * @event beforepush
38317              * Fires before the iframe editor is updated with content from the textarea. Return false
38318              * to cancel the push.
38319              * @param {HtmlEditor} this
38320              * @param {String} html
38321              */
38322             beforepush: true,
38323              /**
38324              * @event sync
38325              * Fires when the textarea is updated with content from the editor iframe.
38326              * @param {HtmlEditor} this
38327              * @param {String} html
38328              */
38329             sync: true,
38330              /**
38331              * @event push
38332              * Fires when the iframe editor is updated with content from the textarea.
38333              * @param {HtmlEditor} this
38334              * @param {String} html
38335              */
38336             push: true,
38337              /**
38338              * @event editmodechange
38339              * Fires when the editor switches edit modes
38340              * @param {HtmlEditor} this
38341              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38342              */
38343             editmodechange: true,
38344             /**
38345              * @event editorevent
38346              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38347              * @param {HtmlEditor} this
38348              */
38349             editorevent: true
38350         })
38351     },
38352
38353     /**
38354      * Protected method that will not generally be called directly. It
38355      * is called when the editor creates its toolbar. Override this method if you need to
38356      * add custom toolbar buttons.
38357      * @param {HtmlEditor} editor
38358      */
38359     createToolbar : function(editor){
38360         if (!editor.toolbars || !editor.toolbars.length) {
38361             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38362         }
38363         
38364         for (var i =0 ; i < editor.toolbars.length;i++) {
38365             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38366             editor.toolbars[i].init(editor);
38367         }
38368          
38369         
38370     },
38371
38372     /**
38373      * Protected method that will not generally be called directly. It
38374      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38375      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38376      */
38377     getDocMarkup : function(){
38378         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38379     },
38380
38381     // private
38382     onRender : function(ct, position){
38383         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38384         this.el.dom.style.border = '0 none';
38385         this.el.dom.setAttribute('tabIndex', -1);
38386         this.el.addClass('x-hidden');
38387         if(Roo.isIE){ // fix IE 1px bogus margin
38388             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38389         }
38390         this.wrap = this.el.wrap({
38391             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38392         });
38393
38394         this.frameId = Roo.id();
38395         this.createToolbar(this);
38396         
38397         
38398         
38399         
38400       
38401         
38402         var iframe = this.wrap.createChild({
38403             tag: 'iframe',
38404             id: this.frameId,
38405             name: this.frameId,
38406             frameBorder : 'no',
38407             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38408         });
38409         
38410        // console.log(iframe);
38411         //this.wrap.dom.appendChild(iframe);
38412
38413         this.iframe = iframe.dom;
38414
38415          this.assignDocWin();
38416         
38417         this.doc.designMode = 'on';
38418        
38419         this.doc.open();
38420         this.doc.write(this.getDocMarkup());
38421         this.doc.close();
38422
38423         
38424         var task = { // must defer to wait for browser to be ready
38425             run : function(){
38426                 //console.log("run task?" + this.doc.readyState);
38427                 this.assignDocWin();
38428                 if(this.doc.body || this.doc.readyState == 'complete'){
38429                     try {
38430                         this.doc.designMode="on";
38431                     } catch (e) {
38432                         return;
38433                     }
38434                     Roo.TaskMgr.stop(task);
38435                     this.initEditor.defer(10, this);
38436                 }
38437             },
38438             interval : 10,
38439             duration:10000,
38440             scope: this
38441         };
38442         Roo.TaskMgr.start(task);
38443
38444         if(!this.width){
38445             this.setSize(this.el.getSize());
38446         }
38447     },
38448
38449     // private
38450     onResize : function(w, h){
38451         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38452         if(this.el && this.iframe){
38453             if(typeof w == 'number'){
38454                 var aw = w - this.wrap.getFrameWidth('lr');
38455                 this.el.setWidth(this.adjustWidth('textarea', aw));
38456                 this.iframe.style.width = aw + 'px';
38457             }
38458             if(typeof h == 'number'){
38459                 var tbh = 0;
38460                 for (var i =0; i < this.toolbars.length;i++) {
38461                     // fixme - ask toolbars for heights?
38462                     tbh += this.toolbars[i].tb.el.getHeight();
38463                 }
38464                 
38465                 
38466                 
38467                 
38468                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38469                 this.el.setHeight(this.adjustWidth('textarea', ah));
38470                 this.iframe.style.height = ah + 'px';
38471                 if(this.doc){
38472                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38473                 }
38474             }
38475         }
38476     },
38477
38478     /**
38479      * Toggles the editor between standard and source edit mode.
38480      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38481      */
38482     toggleSourceEdit : function(sourceEditMode){
38483         
38484         this.sourceEditMode = sourceEditMode === true;
38485         
38486         if(this.sourceEditMode){
38487           
38488             this.syncValue();
38489             this.iframe.className = 'x-hidden';
38490             this.el.removeClass('x-hidden');
38491             this.el.dom.removeAttribute('tabIndex');
38492             this.el.focus();
38493         }else{
38494              
38495             this.pushValue();
38496             this.iframe.className = '';
38497             this.el.addClass('x-hidden');
38498             this.el.dom.setAttribute('tabIndex', -1);
38499             this.deferFocus();
38500         }
38501         this.setSize(this.wrap.getSize());
38502         this.fireEvent('editmodechange', this, this.sourceEditMode);
38503     },
38504
38505     // private used internally
38506     createLink : function(){
38507         var url = prompt(this.createLinkText, this.defaultLinkValue);
38508         if(url && url != 'http:/'+'/'){
38509             this.relayCmd('createlink', url);
38510         }
38511     },
38512
38513     // private (for BoxComponent)
38514     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38515
38516     // private (for BoxComponent)
38517     getResizeEl : function(){
38518         return this.wrap;
38519     },
38520
38521     // private (for BoxComponent)
38522     getPositionEl : function(){
38523         return this.wrap;
38524     },
38525
38526     // private
38527     initEvents : function(){
38528         this.originalValue = this.getValue();
38529     },
38530
38531     /**
38532      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38533      * @method
38534      */
38535     markInvalid : Roo.emptyFn,
38536     /**
38537      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38538      * @method
38539      */
38540     clearInvalid : Roo.emptyFn,
38541
38542     setValue : function(v){
38543         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38544         this.pushValue();
38545     },
38546
38547     /**
38548      * Protected method that will not generally be called directly. If you need/want
38549      * custom HTML cleanup, this is the method you should override.
38550      * @param {String} html The HTML to be cleaned
38551      * return {String} The cleaned HTML
38552      */
38553     cleanHtml : function(html){
38554         html = String(html);
38555         if(html.length > 5){
38556             if(Roo.isSafari){ // strip safari nonsense
38557                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38558             }
38559         }
38560         if(html == '&nbsp;'){
38561             html = '';
38562         }
38563         return html;
38564     },
38565
38566     /**
38567      * Protected method that will not generally be called directly. Syncs the contents
38568      * of the editor iframe with the textarea.
38569      */
38570     syncValue : function(){
38571         if(this.initialized){
38572             var bd = (this.doc.body || this.doc.documentElement);
38573             var html = bd.innerHTML;
38574             if(Roo.isSafari){
38575                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38576                 var m = bs.match(/text-align:(.*?);/i);
38577                 if(m && m[1]){
38578                     html = '<div style="'+m[0]+'">' + html + '</div>';
38579                 }
38580             }
38581             html = this.cleanHtml(html);
38582             if(this.fireEvent('beforesync', this, html) !== false){
38583                 this.el.dom.value = html;
38584                 this.fireEvent('sync', this, html);
38585             }
38586         }
38587     },
38588
38589     /**
38590      * Protected method that will not generally be called directly. Pushes the value of the textarea
38591      * into the iframe editor.
38592      */
38593     pushValue : function(){
38594         if(this.initialized){
38595             var v = this.el.dom.value;
38596             if(v.length < 1){
38597                 v = '&#160;';
38598             }
38599             if(this.fireEvent('beforepush', this, v) !== false){
38600                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38601                 this.fireEvent('push', this, v);
38602             }
38603         }
38604     },
38605
38606     // private
38607     deferFocus : function(){
38608         this.focus.defer(10, this);
38609     },
38610
38611     // doc'ed in Field
38612     focus : function(){
38613         if(this.win && !this.sourceEditMode){
38614             this.win.focus();
38615         }else{
38616             this.el.focus();
38617         }
38618     },
38619     
38620     assignDocWin: function()
38621     {
38622         var iframe = this.iframe;
38623         
38624          if(Roo.isIE){
38625             this.doc = iframe.contentWindow.document;
38626             this.win = iframe.contentWindow;
38627         } else {
38628             if (!Roo.get(this.frameId)) {
38629                 return;
38630             }
38631             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38632             this.win = Roo.get(this.frameId).dom.contentWindow;
38633         }
38634     },
38635     
38636     // private
38637     initEditor : function(){
38638         //console.log("INIT EDITOR");
38639         this.assignDocWin();
38640         
38641         
38642         
38643         this.doc.designMode="on";
38644         this.doc.open();
38645         this.doc.write(this.getDocMarkup());
38646         this.doc.close();
38647         
38648         var dbody = (this.doc.body || this.doc.documentElement);
38649         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38650         // this copies styles from the containing element into thsi one..
38651         // not sure why we need all of this..
38652         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38653         ss['background-attachment'] = 'fixed'; // w3c
38654         dbody.bgProperties = 'fixed'; // ie
38655         Roo.DomHelper.applyStyles(dbody, ss);
38656         Roo.EventManager.on(this.doc, {
38657             'mousedown': this.onEditorEvent,
38658             'dblclick': this.onEditorEvent,
38659             'click': this.onEditorEvent,
38660             'keyup': this.onEditorEvent,
38661             buffer:100,
38662             scope: this
38663         });
38664         if(Roo.isGecko){
38665             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38666         }
38667         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38668             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38669         }
38670         this.initialized = true;
38671
38672         this.fireEvent('initialize', this);
38673         this.pushValue();
38674     },
38675
38676     // private
38677     onDestroy : function(){
38678         
38679         
38680         
38681         if(this.rendered){
38682             
38683             for (var i =0; i < this.toolbars.length;i++) {
38684                 // fixme - ask toolbars for heights?
38685                 this.toolbars[i].onDestroy();
38686             }
38687             
38688             this.wrap.dom.innerHTML = '';
38689             this.wrap.remove();
38690         }
38691     },
38692
38693     // private
38694     onFirstFocus : function(){
38695         
38696         this.assignDocWin();
38697         
38698         
38699         this.activated = true;
38700         for (var i =0; i < this.toolbars.length;i++) {
38701             this.toolbars[i].onFirstFocus();
38702         }
38703        
38704         if(Roo.isGecko){ // prevent silly gecko errors
38705             this.win.focus();
38706             var s = this.win.getSelection();
38707             if(!s.focusNode || s.focusNode.nodeType != 3){
38708                 var r = s.getRangeAt(0);
38709                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38710                 r.collapse(true);
38711                 this.deferFocus();
38712             }
38713             try{
38714                 this.execCmd('useCSS', true);
38715                 this.execCmd('styleWithCSS', false);
38716             }catch(e){}
38717         }
38718         this.fireEvent('activate', this);
38719     },
38720
38721     // private
38722     adjustFont: function(btn){
38723         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38724         //if(Roo.isSafari){ // safari
38725         //    adjust *= 2;
38726        // }
38727         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38728         if(Roo.isSafari){ // safari
38729             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38730             v =  (v < 10) ? 10 : v;
38731             v =  (v > 48) ? 48 : v;
38732             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38733             
38734         }
38735         
38736         
38737         v = Math.max(1, v+adjust);
38738         
38739         this.execCmd('FontSize', v  );
38740     },
38741
38742     onEditorEvent : function(e){
38743         this.fireEvent('editorevent', this, e);
38744       //  this.updateToolbar();
38745         this.syncValue();
38746     },
38747
38748     insertTag : function(tg)
38749     {
38750         // could be a bit smarter... -> wrap the current selected tRoo..
38751         
38752         this.execCmd("formatblock",   tg);
38753         
38754     },
38755     
38756     insertText : function(txt)
38757     {
38758         
38759         
38760         range = this.createRange();
38761         range.deleteContents();
38762                //alert(Sender.getAttribute('label'));
38763                
38764         range.insertNode(this.doc.createTextNode(txt));
38765     } ,
38766     
38767     // private
38768     relayBtnCmd : function(btn){
38769         this.relayCmd(btn.cmd);
38770     },
38771
38772     /**
38773      * Executes a Midas editor command on the editor document and performs necessary focus and
38774      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38775      * @param {String} cmd The Midas command
38776      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38777      */
38778     relayCmd : function(cmd, value){
38779         this.win.focus();
38780         this.execCmd(cmd, value);
38781         this.fireEvent('editorevent', this);
38782         //this.updateToolbar();
38783         this.deferFocus();
38784     },
38785
38786     /**
38787      * Executes a Midas editor command directly on the editor document.
38788      * For visual commands, you should use {@link #relayCmd} instead.
38789      * <b>This should only be called after the editor is initialized.</b>
38790      * @param {String} cmd The Midas command
38791      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38792      */
38793     execCmd : function(cmd, value){
38794         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38795         this.syncValue();
38796     },
38797
38798    
38799     /**
38800      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38801      * to insert tRoo.
38802      * @param {String} text
38803      */
38804     insertAtCursor : function(text){
38805         if(!this.activated){
38806             return;
38807         }
38808         if(Roo.isIE){
38809             this.win.focus();
38810             var r = this.doc.selection.createRange();
38811             if(r){
38812                 r.collapse(true);
38813                 r.pasteHTML(text);
38814                 this.syncValue();
38815                 this.deferFocus();
38816             }
38817         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38818             this.win.focus();
38819             this.execCmd('InsertHTML', text);
38820             this.deferFocus();
38821         }
38822     },
38823  // private
38824     mozKeyPress : function(e){
38825         if(e.ctrlKey){
38826             var c = e.getCharCode(), cmd;
38827           
38828             if(c > 0){
38829                 c = String.fromCharCode(c).toLowerCase();
38830                 switch(c){
38831                     case 'b':
38832                         cmd = 'bold';
38833                     break;
38834                     case 'i':
38835                         cmd = 'italic';
38836                     break;
38837                     case 'u':
38838                         cmd = 'underline';
38839                     case 'v':
38840                         this.cleanUpPaste.defer(100, this);
38841                         return;
38842                     break;
38843                 }
38844                 if(cmd){
38845                     this.win.focus();
38846                     this.execCmd(cmd);
38847                     this.deferFocus();
38848                     e.preventDefault();
38849                 }
38850                 
38851             }
38852         }
38853     },
38854
38855     // private
38856     fixKeys : function(){ // load time branching for fastest keydown performance
38857         if(Roo.isIE){
38858             return function(e){
38859                 var k = e.getKey(), r;
38860                 if(k == e.TAB){
38861                     e.stopEvent();
38862                     r = this.doc.selection.createRange();
38863                     if(r){
38864                         r.collapse(true);
38865                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38866                         this.deferFocus();
38867                     }
38868                     return;
38869                 }
38870                 
38871                 if(k == e.ENTER){
38872                     r = this.doc.selection.createRange();
38873                     if(r){
38874                         var target = r.parentElement();
38875                         if(!target || target.tagName.toLowerCase() != 'li'){
38876                             e.stopEvent();
38877                             r.pasteHTML('<br />');
38878                             r.collapse(false);
38879                             r.select();
38880                         }
38881                     }
38882                 }
38883                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38884                     this.cleanUpPaste.defer(100, this);
38885                     return;
38886                 }
38887                 
38888                 
38889             };
38890         }else if(Roo.isOpera){
38891             return function(e){
38892                 var k = e.getKey();
38893                 if(k == e.TAB){
38894                     e.stopEvent();
38895                     this.win.focus();
38896                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38897                     this.deferFocus();
38898                 }
38899                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38900                     this.cleanUpPaste.defer(100, this);
38901                     return;
38902                 }
38903                 
38904             };
38905         }else if(Roo.isSafari){
38906             return function(e){
38907                 var k = e.getKey();
38908                 
38909                 if(k == e.TAB){
38910                     e.stopEvent();
38911                     this.execCmd('InsertText','\t');
38912                     this.deferFocus();
38913                     return;
38914                 }
38915                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38916                     this.cleanUpPaste.defer(100, this);
38917                     return;
38918                 }
38919                 
38920              };
38921         }
38922     }(),
38923     
38924     getAllAncestors: function()
38925     {
38926         var p = this.getSelectedNode();
38927         var a = [];
38928         if (!p) {
38929             a.push(p); // push blank onto stack..
38930             p = this.getParentElement();
38931         }
38932         
38933         
38934         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38935             a.push(p);
38936             p = p.parentNode;
38937         }
38938         a.push(this.doc.body);
38939         return a;
38940     },
38941     lastSel : false,
38942     lastSelNode : false,
38943     
38944     
38945     getSelection : function() 
38946     {
38947         this.assignDocWin();
38948         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38949     },
38950     
38951     getSelectedNode: function() 
38952     {
38953         // this may only work on Gecko!!!
38954         
38955         // should we cache this!!!!
38956         
38957         
38958         
38959          
38960         var range = this.createRange(this.getSelection());
38961         
38962         if (Roo.isIE) {
38963             var parent = range.parentElement();
38964             while (true) {
38965                 var testRange = range.duplicate();
38966                 testRange.moveToElementText(parent);
38967                 if (testRange.inRange(range)) {
38968                     break;
38969                 }
38970                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38971                     break;
38972                 }
38973                 parent = parent.parentElement;
38974             }
38975             return parent;
38976         }
38977         
38978         
38979         var ar = range.endContainer.childNodes;
38980         if (!ar.length) {
38981             ar = range.commonAncestorContainer.childNodes;
38982             //alert(ar.length);
38983         }
38984         var nodes = [];
38985         var other_nodes = [];
38986         var has_other_nodes = false;
38987         for (var i=0;i<ar.length;i++) {
38988             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38989                 continue;
38990             }
38991             // fullly contained node.
38992             
38993             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38994                 nodes.push(ar[i]);
38995                 continue;
38996             }
38997             
38998             // probably selected..
38999             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39000                 other_nodes.push(ar[i]);
39001                 continue;
39002             }
39003             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39004                 continue;
39005             }
39006             
39007             
39008             has_other_nodes = true;
39009         }
39010         if (!nodes.length && other_nodes.length) {
39011             nodes= other_nodes;
39012         }
39013         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39014             return false;
39015         }
39016         
39017         return nodes[0];
39018     },
39019     createRange: function(sel)
39020     {
39021         // this has strange effects when using with 
39022         // top toolbar - not sure if it's a great idea.
39023         //this.editor.contentWindow.focus();
39024         if (typeof sel != "undefined") {
39025             try {
39026                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39027             } catch(e) {
39028                 return this.doc.createRange();
39029             }
39030         } else {
39031             return this.doc.createRange();
39032         }
39033     },
39034     getParentElement: function()
39035     {
39036         
39037         this.assignDocWin();
39038         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39039         
39040         var range = this.createRange(sel);
39041          
39042         try {
39043             var p = range.commonAncestorContainer;
39044             while (p.nodeType == 3) { // text node
39045                 p = p.parentNode;
39046             }
39047             return p;
39048         } catch (e) {
39049             return null;
39050         }
39051     
39052     },
39053     
39054     
39055     
39056     // BC Hacks - cause I cant work out what i was trying to do..
39057     rangeIntersectsNode : function(range, node)
39058     {
39059         var nodeRange = node.ownerDocument.createRange();
39060         try {
39061             nodeRange.selectNode(node);
39062         }
39063         catch (e) {
39064             nodeRange.selectNodeContents(node);
39065         }
39066
39067         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39068                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39069     },
39070     rangeCompareNode : function(range, node) {
39071         var nodeRange = node.ownerDocument.createRange();
39072         try {
39073             nodeRange.selectNode(node);
39074         } catch (e) {
39075             nodeRange.selectNodeContents(node);
39076         }
39077         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39078         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39079
39080         if (nodeIsBefore && !nodeIsAfter)
39081             return 0;
39082         if (!nodeIsBefore && nodeIsAfter)
39083             return 1;
39084         if (nodeIsBefore && nodeIsAfter)
39085             return 2;
39086
39087         return 3;
39088     },
39089
39090     // private? - in a new class?
39091     cleanUpPaste :  function()
39092     {
39093         // cleans up the whole document..
39094       //  console.log('cleanuppaste');
39095         this.cleanUpChildren(this.doc.body)
39096         
39097         
39098     },
39099     cleanUpChildren : function (n)
39100     {
39101         if (!n.childNodes.length) {
39102             return;
39103         }
39104         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39105            this.cleanUpChild(n.childNodes[i]);
39106         }
39107     },
39108     
39109     
39110         
39111     
39112     cleanUpChild : function (node)
39113     {
39114         //console.log(node);
39115         if (node.nodeName == "#text") {
39116             // clean up silly Windows -- stuff?
39117             return; 
39118         }
39119         if (node.nodeName == "#comment") {
39120             node.parentNode.removeChild(node);
39121             // clean up silly Windows -- stuff?
39122             return; 
39123         }
39124         
39125         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39126             // remove node.
39127             node.parentNode.removeChild(node);
39128             return;
39129             
39130         }
39131         if (!node.attributes || !node.attributes.length) {
39132             this.cleanUpChildren(node);
39133             return;
39134         }
39135         
39136         function cleanAttr(n,v)
39137         {
39138             
39139             if (v.match(/^\./) || v.match(/^\//)) {
39140                 return;
39141             }
39142             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39143                 return;
39144             }
39145             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39146             node.removeAttribute(n);
39147             
39148         }
39149         
39150         function cleanStyle(n,v)
39151         {
39152             if (v.match(/expression/)) { //XSS?? should we even bother..
39153                 node.removeAttribute(n);
39154                 return;
39155             }
39156             
39157             
39158             var parts = v.split(/;/);
39159             Roo.each(parts, function(p) {
39160                 p = p.replace(/\s+/g,'');
39161                 if (!p.length) {
39162                     return;
39163                 }
39164                 var l = p.split(':').shift().replace(/\s+/g,'');
39165                 
39166                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39167                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39168                     node.removeAttribute(n);
39169                     return false;
39170                 }
39171             });
39172             
39173             
39174         }
39175         
39176         
39177         for (var i = node.attributes.length-1; i > -1 ; i--) {
39178             var a = node.attributes[i];
39179             //console.log(a);
39180             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39181                 node.removeAttribute(a.name);
39182                 return;
39183             }
39184             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39185                 cleanAttr(a.name,a.value); // fixme..
39186                 return;
39187             }
39188             if (a.name == 'style') {
39189                 cleanStyle(a.name,a.value);
39190             }
39191             /// clean up MS crap..
39192             if (a.name == 'class') {
39193                 if (a.value.match(/^Mso/)) {
39194                     node.className = '';
39195                 }
39196             }
39197             
39198             // style cleanup!?
39199             // class cleanup?
39200             
39201         }
39202         
39203         
39204         this.cleanUpChildren(node);
39205         
39206         
39207     }
39208     
39209     
39210     // hide stuff that is not compatible
39211     /**
39212      * @event blur
39213      * @hide
39214      */
39215     /**
39216      * @event change
39217      * @hide
39218      */
39219     /**
39220      * @event focus
39221      * @hide
39222      */
39223     /**
39224      * @event specialkey
39225      * @hide
39226      */
39227     /**
39228      * @cfg {String} fieldClass @hide
39229      */
39230     /**
39231      * @cfg {String} focusClass @hide
39232      */
39233     /**
39234      * @cfg {String} autoCreate @hide
39235      */
39236     /**
39237      * @cfg {String} inputType @hide
39238      */
39239     /**
39240      * @cfg {String} invalidClass @hide
39241      */
39242     /**
39243      * @cfg {String} invalidText @hide
39244      */
39245     /**
39246      * @cfg {String} msgFx @hide
39247      */
39248     /**
39249      * @cfg {String} validateOnBlur @hide
39250      */
39251 });
39252
39253 Roo.form.HtmlEditor.white = [
39254         'area', 'br', 'img', 'input', 'hr', 'wbr',
39255         
39256        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39257        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39258        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39259        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39260        'table',   'ul',         'xmp', 
39261        
39262        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39263       'thead',   'tr', 
39264      
39265       'dir', 'menu', 'ol', 'ul', 'dl',
39266        
39267       'embed',  'object'
39268 ];
39269
39270
39271 Roo.form.HtmlEditor.black = [
39272     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39273         'applet', // 
39274         'base',   'basefont', 'bgsound', 'blink',  'body', 
39275         'frame',  'frameset', 'head',    'html',   'ilayer', 
39276         'iframe', 'layer',  'link',     'meta',    'object',   
39277         'script', 'style' ,'title',  'xml' // clean later..
39278 ];
39279 Roo.form.HtmlEditor.clean = [
39280     'script', 'style', 'title', 'xml'
39281 ];
39282
39283 // attributes..
39284
39285 Roo.form.HtmlEditor.ablack = [
39286     'on'
39287 ];
39288     
39289 Roo.form.HtmlEditor.aclean = [ 
39290     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39291 ];
39292
39293 // protocols..
39294 Roo.form.HtmlEditor.pwhite= [
39295         'http',  'https',  'mailto'
39296 ];
39297
39298 Roo.form.HtmlEditor.cwhite= [
39299         'text-align',
39300         'font-size'
39301 ];
39302
39303 // <script type="text/javascript">
39304 /*
39305  * Based on
39306  * Ext JS Library 1.1.1
39307  * Copyright(c) 2006-2007, Ext JS, LLC.
39308  *  
39309  
39310  */
39311
39312 /**
39313  * @class Roo.form.HtmlEditorToolbar1
39314  * Basic Toolbar
39315  * 
39316  * Usage:
39317  *
39318  new Roo.form.HtmlEditor({
39319     ....
39320     toolbars : [
39321         new Roo.form.HtmlEditorToolbar1({
39322             disable : { fonts: 1 , format: 1, ..., ... , ...],
39323             btns : [ .... ]
39324         })
39325     }
39326      
39327  * 
39328  * @cfg {Object} disable List of elements to disable..
39329  * @cfg {Array} btns List of additional buttons.
39330  * 
39331  * 
39332  * NEEDS Extra CSS? 
39333  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39334  */
39335  
39336 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39337 {
39338     
39339     Roo.apply(this, config);
39340     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39341     // dont call parent... till later.
39342 }
39343
39344 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39345     
39346     tb: false,
39347     
39348     rendered: false,
39349     
39350     editor : false,
39351     /**
39352      * @cfg {Object} disable  List of toolbar elements to disable
39353          
39354      */
39355     disable : false,
39356       /**
39357      * @cfg {Array} fontFamilies An array of available font families
39358      */
39359     fontFamilies : [
39360         'Arial',
39361         'Courier New',
39362         'Tahoma',
39363         'Times New Roman',
39364         'Verdana'
39365     ],
39366     
39367     specialChars : [
39368            "&#169;",
39369           "&#174;",     
39370           "&#8482;",    
39371           "&#163;" ,    
39372          // "&#8212;",    
39373           "&#8230;",    
39374           "&#247;" ,    
39375         //  "&#225;" ,     ?? a acute?
39376            "&#8364;"    , //Euro
39377        //   "&#8220;"    ,
39378         //  "&#8221;"    ,
39379         //  "&#8226;"    ,
39380           "&#176;"  //   , // degrees
39381
39382          // "&#233;"     , // e ecute
39383          // "&#250;"     , // u ecute?
39384     ],
39385     inputElements : [ 
39386             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39387             "input:submit", "input:button", "select", "textarea", "label" ],
39388     formats : [
39389         ["p"] ,  
39390         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39391         ["pre"],[ "code"], 
39392         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39393     ],
39394      /**
39395      * @cfg {String} defaultFont default font to use.
39396      */
39397     defaultFont: 'tahoma',
39398    
39399     fontSelect : false,
39400     
39401     
39402     formatCombo : false,
39403     
39404     init : function(editor)
39405     {
39406         this.editor = editor;
39407         
39408         
39409         var fid = editor.frameId;
39410         var etb = this;
39411         function btn(id, toggle, handler){
39412             var xid = fid + '-'+ id ;
39413             return {
39414                 id : xid,
39415                 cmd : id,
39416                 cls : 'x-btn-icon x-edit-'+id,
39417                 enableToggle:toggle !== false,
39418                 scope: editor, // was editor...
39419                 handler:handler||editor.relayBtnCmd,
39420                 clickEvent:'mousedown',
39421                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39422                 tabIndex:-1
39423             };
39424         }
39425         
39426         
39427         
39428         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39429         this.tb = tb;
39430          // stop form submits
39431         tb.el.on('click', function(e){
39432             e.preventDefault(); // what does this do?
39433         });
39434
39435         if(!this.disable.font && !Roo.isSafari){
39436             /* why no safari for fonts
39437             editor.fontSelect = tb.el.createChild({
39438                 tag:'select',
39439                 tabIndex: -1,
39440                 cls:'x-font-select',
39441                 html: editor.createFontOptions()
39442             });
39443             editor.fontSelect.on('change', function(){
39444                 var font = editor.fontSelect.dom.value;
39445                 editor.relayCmd('fontname', font);
39446                 editor.deferFocus();
39447             }, editor);
39448             tb.add(
39449                 editor.fontSelect.dom,
39450                 '-'
39451             );
39452             */
39453         };
39454         if(!this.disable.formats){
39455             this.formatCombo = new Roo.form.ComboBox({
39456                 store: new Roo.data.SimpleStore({
39457                     id : 'tag',
39458                     fields: ['tag'],
39459                     data : this.formats // from states.js
39460                 }),
39461                 blockFocus : true,
39462                 //autoCreate : {tag: "div",  size: "20"},
39463                 displayField:'tag',
39464                 typeAhead: false,
39465                 mode: 'local',
39466                 editable : false,
39467                 triggerAction: 'all',
39468                 emptyText:'Add tag',
39469                 selectOnFocus:true,
39470                 width:135,
39471                 listeners : {
39472                     'select': function(c, r, i) {
39473                         editor.insertTag(r.get('tag'));
39474                         editor.focus();
39475                     }
39476                 }
39477
39478             });
39479             tb.addField(this.formatCombo);
39480             
39481         }
39482         
39483         if(!this.disable.format){
39484             tb.add(
39485                 btn('bold'),
39486                 btn('italic'),
39487                 btn('underline')
39488             );
39489         };
39490         if(!this.disable.fontSize){
39491             tb.add(
39492                 '-',
39493                 
39494                 
39495                 btn('increasefontsize', false, editor.adjustFont),
39496                 btn('decreasefontsize', false, editor.adjustFont)
39497             );
39498         };
39499         
39500         
39501         if(this.disable.colors){
39502             tb.add(
39503                 '-', {
39504                     id:editor.frameId +'-forecolor',
39505                     cls:'x-btn-icon x-edit-forecolor',
39506                     clickEvent:'mousedown',
39507                     tooltip: this.buttonTips['forecolor'] || undefined,
39508                     tabIndex:-1,
39509                     menu : new Roo.menu.ColorMenu({
39510                         allowReselect: true,
39511                         focus: Roo.emptyFn,
39512                         value:'000000',
39513                         plain:true,
39514                         selectHandler: function(cp, color){
39515                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39516                             editor.deferFocus();
39517                         },
39518                         scope: editor,
39519                         clickEvent:'mousedown'
39520                     })
39521                 }, {
39522                     id:editor.frameId +'backcolor',
39523                     cls:'x-btn-icon x-edit-backcolor',
39524                     clickEvent:'mousedown',
39525                     tooltip: this.buttonTips['backcolor'] || undefined,
39526                     tabIndex:-1,
39527                     menu : new Roo.menu.ColorMenu({
39528                         focus: Roo.emptyFn,
39529                         value:'FFFFFF',
39530                         plain:true,
39531                         allowReselect: true,
39532                         selectHandler: function(cp, color){
39533                             if(Roo.isGecko){
39534                                 editor.execCmd('useCSS', false);
39535                                 editor.execCmd('hilitecolor', color);
39536                                 editor.execCmd('useCSS', true);
39537                                 editor.deferFocus();
39538                             }else{
39539                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39540                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39541                                 editor.deferFocus();
39542                             }
39543                         },
39544                         scope:editor,
39545                         clickEvent:'mousedown'
39546                     })
39547                 }
39548             );
39549         };
39550         // now add all the items...
39551         
39552
39553         if(!this.disable.alignments){
39554             tb.add(
39555                 '-',
39556                 btn('justifyleft'),
39557                 btn('justifycenter'),
39558                 btn('justifyright')
39559             );
39560         };
39561
39562         //if(!Roo.isSafari){
39563             if(!this.disable.links){
39564                 tb.add(
39565                     '-',
39566                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39567                 );
39568             };
39569
39570             if(!this.disable.lists){
39571                 tb.add(
39572                     '-',
39573                     btn('insertorderedlist'),
39574                     btn('insertunorderedlist')
39575                 );
39576             }
39577             if(!this.disable.sourceEdit){
39578                 tb.add(
39579                     '-',
39580                     btn('sourceedit', true, function(btn){
39581                         this.toggleSourceEdit(btn.pressed);
39582                     })
39583                 );
39584             }
39585         //}
39586         
39587         var smenu = { };
39588         // special menu.. - needs to be tidied up..
39589         if (!this.disable.special) {
39590             smenu = {
39591                 text: "&#169;",
39592                 cls: 'x-edit-none',
39593                 menu : {
39594                     items : []
39595                    }
39596             };
39597             for (var i =0; i < this.specialChars.length; i++) {
39598                 smenu.menu.items.push({
39599                     
39600                     html: this.specialChars[i],
39601                     handler: function(a,b) {
39602                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39603                         
39604                     },
39605                     tabIndex:-1
39606                 });
39607             }
39608             
39609             
39610             tb.add(smenu);
39611             
39612             
39613         }
39614         if (this.btns) {
39615             for(var i =0; i< this.btns.length;i++) {
39616                 var b = this.btns[i];
39617                 b.cls =  'x-edit-none';
39618                 b.scope = editor;
39619                 tb.add(b);
39620             }
39621         
39622         }
39623         
39624         
39625         
39626         // disable everything...
39627         
39628         this.tb.items.each(function(item){
39629            if(item.id != editor.frameId+ '-sourceedit'){
39630                 item.disable();
39631             }
39632         });
39633         this.rendered = true;
39634         
39635         // the all the btns;
39636         editor.on('editorevent', this.updateToolbar, this);
39637         // other toolbars need to implement this..
39638         //editor.on('editmodechange', this.updateToolbar, this);
39639     },
39640     
39641     
39642     
39643     /**
39644      * Protected method that will not generally be called directly. It triggers
39645      * a toolbar update by reading the markup state of the current selection in the editor.
39646      */
39647     updateToolbar: function(){
39648
39649         if(!this.editor.activated){
39650             this.editor.onFirstFocus();
39651             return;
39652         }
39653
39654         var btns = this.tb.items.map, 
39655             doc = this.editor.doc,
39656             frameId = this.editor.frameId;
39657
39658         if(!this.disable.font && !Roo.isSafari){
39659             /*
39660             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39661             if(name != this.fontSelect.dom.value){
39662                 this.fontSelect.dom.value = name;
39663             }
39664             */
39665         }
39666         if(!this.disable.format){
39667             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39668             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39669             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39670         }
39671         if(!this.disable.alignments){
39672             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39673             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39674             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39675         }
39676         if(!Roo.isSafari && !this.disable.lists){
39677             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39678             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39679         }
39680         
39681         var ans = this.editor.getAllAncestors();
39682         if (this.formatCombo) {
39683             
39684             
39685             var store = this.formatCombo.store;
39686             this.formatCombo.setValue("");
39687             for (var i =0; i < ans.length;i++) {
39688                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39689                     // select it..
39690                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39691                     break;
39692                 }
39693             }
39694         }
39695         
39696         
39697         
39698         // hides menus... - so this cant be on a menu...
39699         Roo.menu.MenuMgr.hideAll();
39700
39701         //this.editorsyncValue();
39702     },
39703    
39704     
39705     createFontOptions : function(){
39706         var buf = [], fs = this.fontFamilies, ff, lc;
39707         for(var i = 0, len = fs.length; i< len; i++){
39708             ff = fs[i];
39709             lc = ff.toLowerCase();
39710             buf.push(
39711                 '<option value="',lc,'" style="font-family:',ff,';"',
39712                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39713                     ff,
39714                 '</option>'
39715             );
39716         }
39717         return buf.join('');
39718     },
39719     
39720     toggleSourceEdit : function(sourceEditMode){
39721         if(sourceEditMode === undefined){
39722             sourceEditMode = !this.sourceEditMode;
39723         }
39724         this.sourceEditMode = sourceEditMode === true;
39725         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39726         // just toggle the button?
39727         if(btn.pressed !== this.editor.sourceEditMode){
39728             btn.toggle(this.editor.sourceEditMode);
39729             return;
39730         }
39731         
39732         if(this.sourceEditMode){
39733             this.tb.items.each(function(item){
39734                 if(item.cmd != 'sourceedit'){
39735                     item.disable();
39736                 }
39737             });
39738           
39739         }else{
39740             if(this.initialized){
39741                 this.tb.items.each(function(item){
39742                     item.enable();
39743                 });
39744             }
39745             
39746         }
39747         // tell the editor that it's been pressed..
39748         this.editor.toggleSourceEdit(sourceEditMode);
39749        
39750     },
39751      /**
39752      * Object collection of toolbar tooltips for the buttons in the editor. The key
39753      * is the command id associated with that button and the value is a valid QuickTips object.
39754      * For example:
39755 <pre><code>
39756 {
39757     bold : {
39758         title: 'Bold (Ctrl+B)',
39759         text: 'Make the selected text bold.',
39760         cls: 'x-html-editor-tip'
39761     },
39762     italic : {
39763         title: 'Italic (Ctrl+I)',
39764         text: 'Make the selected text italic.',
39765         cls: 'x-html-editor-tip'
39766     },
39767     ...
39768 </code></pre>
39769     * @type Object
39770      */
39771     buttonTips : {
39772         bold : {
39773             title: 'Bold (Ctrl+B)',
39774             text: 'Make the selected text bold.',
39775             cls: 'x-html-editor-tip'
39776         },
39777         italic : {
39778             title: 'Italic (Ctrl+I)',
39779             text: 'Make the selected text italic.',
39780             cls: 'x-html-editor-tip'
39781         },
39782         underline : {
39783             title: 'Underline (Ctrl+U)',
39784             text: 'Underline the selected text.',
39785             cls: 'x-html-editor-tip'
39786         },
39787         increasefontsize : {
39788             title: 'Grow Text',
39789             text: 'Increase the font size.',
39790             cls: 'x-html-editor-tip'
39791         },
39792         decreasefontsize : {
39793             title: 'Shrink Text',
39794             text: 'Decrease the font size.',
39795             cls: 'x-html-editor-tip'
39796         },
39797         backcolor : {
39798             title: 'Text Highlight Color',
39799             text: 'Change the background color of the selected text.',
39800             cls: 'x-html-editor-tip'
39801         },
39802         forecolor : {
39803             title: 'Font Color',
39804             text: 'Change the color of the selected text.',
39805             cls: 'x-html-editor-tip'
39806         },
39807         justifyleft : {
39808             title: 'Align Text Left',
39809             text: 'Align text to the left.',
39810             cls: 'x-html-editor-tip'
39811         },
39812         justifycenter : {
39813             title: 'Center Text',
39814             text: 'Center text in the editor.',
39815             cls: 'x-html-editor-tip'
39816         },
39817         justifyright : {
39818             title: 'Align Text Right',
39819             text: 'Align text to the right.',
39820             cls: 'x-html-editor-tip'
39821         },
39822         insertunorderedlist : {
39823             title: 'Bullet List',
39824             text: 'Start a bulleted list.',
39825             cls: 'x-html-editor-tip'
39826         },
39827         insertorderedlist : {
39828             title: 'Numbered List',
39829             text: 'Start a numbered list.',
39830             cls: 'x-html-editor-tip'
39831         },
39832         createlink : {
39833             title: 'Hyperlink',
39834             text: 'Make the selected text a hyperlink.',
39835             cls: 'x-html-editor-tip'
39836         },
39837         sourceedit : {
39838             title: 'Source Edit',
39839             text: 'Switch to source editing mode.',
39840             cls: 'x-html-editor-tip'
39841         }
39842     },
39843     // private
39844     onDestroy : function(){
39845         if(this.rendered){
39846             
39847             this.tb.items.each(function(item){
39848                 if(item.menu){
39849                     item.menu.removeAll();
39850                     if(item.menu.el){
39851                         item.menu.el.destroy();
39852                     }
39853                 }
39854                 item.destroy();
39855             });
39856              
39857         }
39858     },
39859     onFirstFocus: function() {
39860         this.tb.items.each(function(item){
39861            item.enable();
39862         });
39863     }
39864 });
39865
39866
39867
39868
39869 // <script type="text/javascript">
39870 /*
39871  * Based on
39872  * Ext JS Library 1.1.1
39873  * Copyright(c) 2006-2007, Ext JS, LLC.
39874  *  
39875  
39876  */
39877
39878  
39879 /**
39880  * @class Roo.form.HtmlEditor.ToolbarContext
39881  * Context Toolbar
39882  * 
39883  * Usage:
39884  *
39885  new Roo.form.HtmlEditor({
39886     ....
39887     toolbars : [
39888         new Roo.form.HtmlEditor.ToolbarStandard(),
39889         new Roo.form.HtmlEditor.ToolbarContext()
39890         })
39891     }
39892      
39893  * 
39894  * @config : {Object} disable List of elements to disable.. (not done yet.)
39895  * 
39896  * 
39897  */
39898
39899 Roo.form.HtmlEditor.ToolbarContext = function(config)
39900 {
39901     
39902     Roo.apply(this, config);
39903     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39904     // dont call parent... till later.
39905 }
39906 Roo.form.HtmlEditor.ToolbarContext.types = {
39907     'IMG' : {
39908         width : {
39909             title: "Width",
39910             width: 40
39911         },
39912         height:  {
39913             title: "Height",
39914             width: 40
39915         },
39916         align: {
39917             title: "Align",
39918             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39919             width : 80
39920             
39921         },
39922         border: {
39923             title: "Border",
39924             width: 40
39925         },
39926         alt: {
39927             title: "Alt",
39928             width: 120
39929         },
39930         src : {
39931             title: "Src",
39932             width: 220
39933         }
39934         
39935     },
39936     'A' : {
39937         name : {
39938             title: "Name",
39939             width: 50
39940         },
39941         href:  {
39942             title: "Href",
39943             width: 220
39944         } // border?
39945         
39946     },
39947     'TABLE' : {
39948         rows : {
39949             title: "Rows",
39950             width: 20
39951         },
39952         cols : {
39953             title: "Cols",
39954             width: 20
39955         },
39956         width : {
39957             title: "Width",
39958             width: 40
39959         },
39960         height : {
39961             title: "Height",
39962             width: 40
39963         },
39964         border : {
39965             title: "Border",
39966             width: 20
39967         }
39968     },
39969     'TD' : {
39970         width : {
39971             title: "Width",
39972             width: 40
39973         },
39974         height : {
39975             title: "Height",
39976             width: 40
39977         },   
39978         align: {
39979             title: "Align",
39980             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39981             width: 40
39982         },
39983         valign: {
39984             title: "Valign",
39985             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39986             width: 40
39987         },
39988         colspan: {
39989             title: "Colspan",
39990             width: 20
39991             
39992         }
39993     },
39994     'INPUT' : {
39995         name : {
39996             title: "name",
39997             width: 120
39998         },
39999         value : {
40000             title: "Value",
40001             width: 120
40002         },
40003         width : {
40004             title: "Width",
40005             width: 40
40006         }
40007     },
40008     'LABEL' : {
40009         'for' : {
40010             title: "For",
40011             width: 120
40012         }
40013     },
40014     'TEXTAREA' : {
40015           name : {
40016             title: "name",
40017             width: 120
40018         },
40019         rows : {
40020             title: "Rows",
40021             width: 20
40022         },
40023         cols : {
40024             title: "Cols",
40025             width: 20
40026         }
40027     },
40028     'SELECT' : {
40029         name : {
40030             title: "name",
40031             width: 120
40032         },
40033         selectoptions : {
40034             title: "Options",
40035             width: 200
40036         }
40037     },
40038     'BODY' : {
40039         title : {
40040             title: "title",
40041             width: 120,
40042             disabled : true
40043         }
40044     }
40045 };
40046
40047
40048
40049 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40050     
40051     tb: false,
40052     
40053     rendered: false,
40054     
40055     editor : false,
40056     /**
40057      * @cfg {Object} disable  List of toolbar elements to disable
40058          
40059      */
40060     disable : false,
40061     
40062     
40063     
40064     toolbars : false,
40065     
40066     init : function(editor)
40067     {
40068         this.editor = editor;
40069         
40070         
40071         var fid = editor.frameId;
40072         var etb = this;
40073         function btn(id, toggle, handler){
40074             var xid = fid + '-'+ id ;
40075             return {
40076                 id : xid,
40077                 cmd : id,
40078                 cls : 'x-btn-icon x-edit-'+id,
40079                 enableToggle:toggle !== false,
40080                 scope: editor, // was editor...
40081                 handler:handler||editor.relayBtnCmd,
40082                 clickEvent:'mousedown',
40083                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40084                 tabIndex:-1
40085             };
40086         }
40087         // create a new element.
40088         var wdiv = editor.wrap.createChild({
40089                 tag: 'div'
40090             }, editor.wrap.dom.firstChild.nextSibling, true);
40091         
40092         // can we do this more than once??
40093         
40094          // stop form submits
40095       
40096  
40097         // disable everything...
40098         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40099         this.toolbars = {};
40100            
40101         for (var i in  ty) {
40102           
40103             this.toolbars[i] = this.buildToolbar(ty[i],i);
40104         }
40105         this.tb = this.toolbars.BODY;
40106         this.tb.el.show();
40107         
40108          
40109         this.rendered = true;
40110         
40111         // the all the btns;
40112         editor.on('editorevent', this.updateToolbar, this);
40113         // other toolbars need to implement this..
40114         //editor.on('editmodechange', this.updateToolbar, this);
40115     },
40116     
40117     
40118     
40119     /**
40120      * Protected method that will not generally be called directly. It triggers
40121      * a toolbar update by reading the markup state of the current selection in the editor.
40122      */
40123     updateToolbar: function(){
40124
40125         if(!this.editor.activated){
40126             this.editor.onFirstFocus();
40127             return;
40128         }
40129
40130         
40131         var ans = this.editor.getAllAncestors();
40132         
40133         // pick
40134         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40135         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40136         sel = sel ? sel : this.editor.doc.body;
40137         sel = sel.tagName.length ? sel : this.editor.doc.body;
40138         var tn = sel.tagName.toUpperCase();
40139         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40140         tn = sel.tagName.toUpperCase();
40141         if (this.tb.name  == tn) {
40142             return; // no change
40143         }
40144         this.tb.el.hide();
40145         ///console.log("show: " + tn);
40146         this.tb =  this.toolbars[tn];
40147         this.tb.el.show();
40148         this.tb.fields.each(function(e) {
40149             e.setValue(sel.getAttribute(e.name));
40150         });
40151         this.tb.selectedNode = sel;
40152         
40153         
40154         Roo.menu.MenuMgr.hideAll();
40155
40156         //this.editorsyncValue();
40157     },
40158    
40159        
40160     // private
40161     onDestroy : function(){
40162         if(this.rendered){
40163             
40164             this.tb.items.each(function(item){
40165                 if(item.menu){
40166                     item.menu.removeAll();
40167                     if(item.menu.el){
40168                         item.menu.el.destroy();
40169                     }
40170                 }
40171                 item.destroy();
40172             });
40173              
40174         }
40175     },
40176     onFirstFocus: function() {
40177         // need to do this for all the toolbars..
40178         this.tb.items.each(function(item){
40179            item.enable();
40180         });
40181     },
40182     buildToolbar: function(tlist, nm)
40183     {
40184         var editor = this.editor;
40185          // create a new element.
40186         var wdiv = editor.wrap.createChild({
40187                 tag: 'div'
40188             }, editor.wrap.dom.firstChild.nextSibling, true);
40189         
40190        
40191         var tb = new Roo.Toolbar(wdiv);
40192         tb.add(nm+ ":&nbsp;");
40193         for (var i in tlist) {
40194             var item = tlist[i];
40195             tb.add(item.title + ":&nbsp;");
40196             if (item.opts) {
40197                 // fixme
40198                 
40199               
40200                 tb.addField( new Roo.form.ComboBox({
40201                     store: new Roo.data.SimpleStore({
40202                         id : 'val',
40203                         fields: ['val'],
40204                         data : item.opts // from states.js
40205                     }),
40206                     name : i,
40207                     displayField:'val',
40208                     typeAhead: false,
40209                     mode: 'local',
40210                     editable : false,
40211                     triggerAction: 'all',
40212                     emptyText:'Select',
40213                     selectOnFocus:true,
40214                     width: item.width ? item.width  : 130,
40215                     listeners : {
40216                         'select': function(c, r, i) {
40217                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40218                         }
40219                     }
40220
40221                 }));
40222                 continue;
40223                     
40224                 
40225                 
40226                 
40227                 
40228                 tb.addField( new Roo.form.TextField({
40229                     name: i,
40230                     width: 100,
40231                     //allowBlank:false,
40232                     value: ''
40233                 }));
40234                 continue;
40235             }
40236             tb.addField( new Roo.form.TextField({
40237                 name: i,
40238                 width: item.width,
40239                 //allowBlank:true,
40240                 value: '',
40241                 listeners: {
40242                     'change' : function(f, nv, ov) {
40243                         tb.selectedNode.setAttribute(f.name, nv);
40244                     }
40245                 }
40246             }));
40247              
40248         }
40249         tb.el.on('click', function(e){
40250             e.preventDefault(); // what does this do?
40251         });
40252         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40253         tb.el.hide();
40254         tb.name = nm;
40255         // dont need to disable them... as they will get hidden
40256         return tb;
40257          
40258         
40259     }
40260     
40261     
40262     
40263     
40264 });
40265
40266
40267
40268
40269
40270 /*
40271  * Based on:
40272  * Ext JS Library 1.1.1
40273  * Copyright(c) 2006-2007, Ext JS, LLC.
40274  *
40275  * Originally Released Under LGPL - original licence link has changed is not relivant.
40276  *
40277  * Fork - LGPL
40278  * <script type="text/javascript">
40279  */
40280  
40281 /**
40282  * @class Roo.form.BasicForm
40283  * @extends Roo.util.Observable
40284  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40285  * @constructor
40286  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40287  * @param {Object} config Configuration options
40288  */
40289 Roo.form.BasicForm = function(el, config){
40290     this.allItems = [];
40291     this.childForms = [];
40292     Roo.apply(this, config);
40293     /*
40294      * The Roo.form.Field items in this form.
40295      * @type MixedCollection
40296      */
40297      
40298      
40299     this.items = new Roo.util.MixedCollection(false, function(o){
40300         return o.id || (o.id = Roo.id());
40301     });
40302     this.addEvents({
40303         /**
40304          * @event beforeaction
40305          * Fires before any action is performed. Return false to cancel the action.
40306          * @param {Form} this
40307          * @param {Action} action The action to be performed
40308          */
40309         beforeaction: true,
40310         /**
40311          * @event actionfailed
40312          * Fires when an action fails.
40313          * @param {Form} this
40314          * @param {Action} action The action that failed
40315          */
40316         actionfailed : true,
40317         /**
40318          * @event actioncomplete
40319          * Fires when an action is completed.
40320          * @param {Form} this
40321          * @param {Action} action The action that completed
40322          */
40323         actioncomplete : true
40324     });
40325     if(el){
40326         this.initEl(el);
40327     }
40328     Roo.form.BasicForm.superclass.constructor.call(this);
40329 };
40330
40331 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40332     /**
40333      * @cfg {String} method
40334      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40335      */
40336     /**
40337      * @cfg {DataReader} reader
40338      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40339      * This is optional as there is built-in support for processing JSON.
40340      */
40341     /**
40342      * @cfg {DataReader} errorReader
40343      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40344      * This is completely optional as there is built-in support for processing JSON.
40345      */
40346     /**
40347      * @cfg {String} url
40348      * The URL to use for form actions if one isn't supplied in the action options.
40349      */
40350     /**
40351      * @cfg {Boolean} fileUpload
40352      * Set to true if this form is a file upload.
40353      */
40354     /**
40355      * @cfg {Object} baseParams
40356      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40357      */
40358     /**
40359      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40360      */
40361     timeout: 30,
40362
40363     // private
40364     activeAction : null,
40365
40366     /**
40367      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40368      * or setValues() data instead of when the form was first created.
40369      */
40370     trackResetOnLoad : false,
40371     
40372     
40373     /**
40374      * childForms - used for multi-tab forms
40375      * @type {Array}
40376      */
40377     childForms : false,
40378     
40379     /**
40380      * allItems - full list of fields.
40381      * @type {Array}
40382      */
40383     allItems : false,
40384     
40385     /**
40386      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40387      * element by passing it or its id or mask the form itself by passing in true.
40388      * @type Mixed
40389      */
40390     waitMsgTarget : undefined,
40391
40392     // private
40393     initEl : function(el){
40394         this.el = Roo.get(el);
40395         this.id = this.el.id || Roo.id();
40396         this.el.on('submit', this.onSubmit, this);
40397         this.el.addClass('x-form');
40398     },
40399
40400     // private
40401     onSubmit : function(e){
40402         e.stopEvent();
40403     },
40404
40405     /**
40406      * Returns true if client-side validation on the form is successful.
40407      * @return Boolean
40408      */
40409     isValid : function(){
40410         var valid = true;
40411         this.items.each(function(f){
40412            if(!f.validate()){
40413                valid = false;
40414            }
40415         });
40416         return valid;
40417     },
40418
40419     /**
40420      * Returns true if any fields in this form have changed since their original load.
40421      * @return Boolean
40422      */
40423     isDirty : function(){
40424         var dirty = false;
40425         this.items.each(function(f){
40426            if(f.isDirty()){
40427                dirty = true;
40428                return false;
40429            }
40430         });
40431         return dirty;
40432     },
40433
40434     /**
40435      * Performs a predefined action (submit or load) or custom actions you define on this form.
40436      * @param {String} actionName The name of the action type
40437      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40438      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40439      * accept other config options):
40440      * <pre>
40441 Property          Type             Description
40442 ----------------  ---------------  ----------------------------------------------------------------------------------
40443 url               String           The url for the action (defaults to the form's url)
40444 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40445 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40446 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40447                                    validate the form on the client (defaults to false)
40448      * </pre>
40449      * @return {BasicForm} this
40450      */
40451     doAction : function(action, options){
40452         if(typeof action == 'string'){
40453             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40454         }
40455         if(this.fireEvent('beforeaction', this, action) !== false){
40456             this.beforeAction(action);
40457             action.run.defer(100, action);
40458         }
40459         return this;
40460     },
40461
40462     /**
40463      * Shortcut to do a submit action.
40464      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40465      * @return {BasicForm} this
40466      */
40467     submit : function(options){
40468         this.doAction('submit', options);
40469         return this;
40470     },
40471
40472     /**
40473      * Shortcut to do a load action.
40474      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40475      * @return {BasicForm} this
40476      */
40477     load : function(options){
40478         this.doAction('load', options);
40479         return this;
40480     },
40481
40482     /**
40483      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40484      * @param {Record} record The record to edit
40485      * @return {BasicForm} this
40486      */
40487     updateRecord : function(record){
40488         record.beginEdit();
40489         var fs = record.fields;
40490         fs.each(function(f){
40491             var field = this.findField(f.name);
40492             if(field){
40493                 record.set(f.name, field.getValue());
40494             }
40495         }, this);
40496         record.endEdit();
40497         return this;
40498     },
40499
40500     /**
40501      * Loads an Roo.data.Record into this form.
40502      * @param {Record} record The record to load
40503      * @return {BasicForm} this
40504      */
40505     loadRecord : function(record){
40506         this.setValues(record.data);
40507         return this;
40508     },
40509
40510     // private
40511     beforeAction : function(action){
40512         var o = action.options;
40513         if(o.waitMsg){
40514             if(this.waitMsgTarget === true){
40515                 this.el.mask(o.waitMsg, 'x-mask-loading');
40516             }else if(this.waitMsgTarget){
40517                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40518                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
40519             }else{
40520                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
40521             }
40522         }
40523     },
40524
40525     // private
40526     afterAction : function(action, success){
40527         this.activeAction = null;
40528         var o = action.options;
40529         if(o.waitMsg){
40530             if(this.waitMsgTarget === true){
40531                 this.el.unmask();
40532             }else if(this.waitMsgTarget){
40533                 this.waitMsgTarget.unmask();
40534             }else{
40535                 Roo.MessageBox.updateProgress(1);
40536                 Roo.MessageBox.hide();
40537             }
40538         }
40539         if(success){
40540             if(o.reset){
40541                 this.reset();
40542             }
40543             Roo.callback(o.success, o.scope, [this, action]);
40544             this.fireEvent('actioncomplete', this, action);
40545         }else{
40546             Roo.callback(o.failure, o.scope, [this, action]);
40547             this.fireEvent('actionfailed', this, action);
40548         }
40549     },
40550
40551     /**
40552      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40553      * @param {String} id The value to search for
40554      * @return Field
40555      */
40556     findField : function(id){
40557         var field = this.items.get(id);
40558         if(!field){
40559             this.items.each(function(f){
40560                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40561                     field = f;
40562                     return false;
40563                 }
40564             });
40565         }
40566         return field || null;
40567     },
40568
40569     /**
40570      * Add a secondary form to this one, 
40571      * Used to provide tabbed forms. One form is primary, with hidden values 
40572      * which mirror the elements from the other forms.
40573      * 
40574      * @param {Roo.form.Form} form to add.
40575      * 
40576      */
40577     addForm : function(form)
40578     {
40579        
40580         if (this.childForms.indexOf(form) > -1) {
40581             // already added..
40582             return;
40583         }
40584         this.childForms.push(form);
40585         var n = '';
40586         Roo.each(form.allItems, function (fe) {
40587             
40588             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40589             if (this.findField(n)) { // already added..
40590                 return;
40591             }
40592             var add = new Roo.form.Hidden({
40593                 name : n
40594             });
40595             add.render(this.el);
40596             
40597             this.add( add );
40598         }, this);
40599         
40600     },
40601     /**
40602      * Mark fields in this form invalid in bulk.
40603      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40604      * @return {BasicForm} this
40605      */
40606     markInvalid : function(errors){
40607         if(errors instanceof Array){
40608             for(var i = 0, len = errors.length; i < len; i++){
40609                 var fieldError = errors[i];
40610                 var f = this.findField(fieldError.id);
40611                 if(f){
40612                     f.markInvalid(fieldError.msg);
40613                 }
40614             }
40615         }else{
40616             var field, id;
40617             for(id in errors){
40618                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40619                     field.markInvalid(errors[id]);
40620                 }
40621             }
40622         }
40623         Roo.each(this.childForms || [], function (f) {
40624             f.markInvalid(errors);
40625         });
40626         
40627         return this;
40628     },
40629
40630     /**
40631      * Set values for fields in this form in bulk.
40632      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40633      * @return {BasicForm} this
40634      */
40635     setValues : function(values){
40636         if(values instanceof Array){ // array of objects
40637             for(var i = 0, len = values.length; i < len; i++){
40638                 var v = values[i];
40639                 var f = this.findField(v.id);
40640                 if(f){
40641                     f.setValue(v.value);
40642                     if(this.trackResetOnLoad){
40643                         f.originalValue = f.getValue();
40644                     }
40645                 }
40646             }
40647         }else{ // object hash
40648             var field, id;
40649             for(id in values){
40650                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40651                     
40652                     if (field.setFromData && 
40653                         field.valueField && 
40654                         field.displayField &&
40655                         // combos' with local stores can 
40656                         // be queried via setValue()
40657                         // to set their value..
40658                         (field.store && !field.store.isLocal)
40659                         ) {
40660                         // it's a combo
40661                         var sd = { };
40662                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40663                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40664                         field.setFromData(sd);
40665                         
40666                     } else {
40667                         field.setValue(values[id]);
40668                     }
40669                     
40670                     
40671                     if(this.trackResetOnLoad){
40672                         field.originalValue = field.getValue();
40673                     }
40674                 }
40675             }
40676         }
40677          
40678         Roo.each(this.childForms || [], function (f) {
40679             f.setValues(values);
40680         });
40681                 
40682         return this;
40683     },
40684
40685     /**
40686      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40687      * they are returned as an array.
40688      * @param {Boolean} asString
40689      * @return {Object}
40690      */
40691     getValues : function(asString){
40692         if (this.childForms) {
40693             // copy values from the child forms
40694             Roo.each(this.childForms, function (f) {
40695                 this.setValues(f.getValues());
40696             }, this);
40697         }
40698         
40699         
40700         
40701         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40702         if(asString === true){
40703             return fs;
40704         }
40705         return Roo.urlDecode(fs);
40706     },
40707     
40708     /**
40709      * Returns the fields in this form as an object with key/value pairs. 
40710      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40711      * @return {Object}
40712      */
40713     getFieldValues : function()
40714     {
40715         if (this.childForms) {
40716             // copy values from the child forms
40717             Roo.each(this.childForms, function (f) {
40718                 this.setValues(f.getValues());
40719             }, this);
40720         }
40721         
40722         var ret = {};
40723         this.items.each(function(f){
40724             if (!f.getName()) {
40725                 return;
40726             }
40727             var v = f.getValue();
40728             if ((typeof(v) == 'object') && f.getRawValue) {
40729                 v = f.getRawValue() ; // dates..
40730             }
40731             ret[f.getName()] = v;
40732         });
40733         
40734         return ret;
40735     },
40736
40737     /**
40738      * Clears all invalid messages in this form.
40739      * @return {BasicForm} this
40740      */
40741     clearInvalid : function(){
40742         this.items.each(function(f){
40743            f.clearInvalid();
40744         });
40745         
40746         Roo.each(this.childForms || [], function (f) {
40747             f.clearInvalid();
40748         });
40749         
40750         
40751         return this;
40752     },
40753
40754     /**
40755      * Resets this form.
40756      * @return {BasicForm} this
40757      */
40758     reset : function(){
40759         this.items.each(function(f){
40760             f.reset();
40761         });
40762         
40763         Roo.each(this.childForms || [], function (f) {
40764             f.reset();
40765         });
40766        
40767         
40768         return this;
40769     },
40770
40771     /**
40772      * Add Roo.form components to this form.
40773      * @param {Field} field1
40774      * @param {Field} field2 (optional)
40775      * @param {Field} etc (optional)
40776      * @return {BasicForm} this
40777      */
40778     add : function(){
40779         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40780         return this;
40781     },
40782
40783
40784     /**
40785      * Removes a field from the items collection (does NOT remove its markup).
40786      * @param {Field} field
40787      * @return {BasicForm} this
40788      */
40789     remove : function(field){
40790         this.items.remove(field);
40791         return this;
40792     },
40793
40794     /**
40795      * Looks at the fields in this form, checks them for an id attribute,
40796      * and calls applyTo on the existing dom element with that id.
40797      * @return {BasicForm} this
40798      */
40799     render : function(){
40800         this.items.each(function(f){
40801             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40802                 f.applyTo(f.id);
40803             }
40804         });
40805         return this;
40806     },
40807
40808     /**
40809      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40810      * @param {Object} values
40811      * @return {BasicForm} this
40812      */
40813     applyToFields : function(o){
40814         this.items.each(function(f){
40815            Roo.apply(f, o);
40816         });
40817         return this;
40818     },
40819
40820     /**
40821      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40822      * @param {Object} values
40823      * @return {BasicForm} this
40824      */
40825     applyIfToFields : function(o){
40826         this.items.each(function(f){
40827            Roo.applyIf(f, o);
40828         });
40829         return this;
40830     }
40831 });
40832
40833 // back compat
40834 Roo.BasicForm = Roo.form.BasicForm;/*
40835  * Based on:
40836  * Ext JS Library 1.1.1
40837  * Copyright(c) 2006-2007, Ext JS, LLC.
40838  *
40839  * Originally Released Under LGPL - original licence link has changed is not relivant.
40840  *
40841  * Fork - LGPL
40842  * <script type="text/javascript">
40843  */
40844
40845 /**
40846  * @class Roo.form.Form
40847  * @extends Roo.form.BasicForm
40848  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40849  * @constructor
40850  * @param {Object} config Configuration options
40851  */
40852 Roo.form.Form = function(config){
40853     var xitems =  [];
40854     if (config.items) {
40855         xitems = config.items;
40856         delete config.items;
40857     }
40858    
40859     
40860     Roo.form.Form.superclass.constructor.call(this, null, config);
40861     this.url = this.url || this.action;
40862     if(!this.root){
40863         this.root = new Roo.form.Layout(Roo.applyIf({
40864             id: Roo.id()
40865         }, config));
40866     }
40867     this.active = this.root;
40868     /**
40869      * Array of all the buttons that have been added to this form via {@link addButton}
40870      * @type Array
40871      */
40872     this.buttons = [];
40873     this.allItems = [];
40874     this.addEvents({
40875         /**
40876          * @event clientvalidation
40877          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40878          * @param {Form} this
40879          * @param {Boolean} valid true if the form has passed client-side validation
40880          */
40881         clientvalidation: true,
40882         /**
40883          * @event rendered
40884          * Fires when the form is rendered
40885          * @param {Roo.form.Form} form
40886          */
40887         rendered : true
40888     });
40889     
40890     if (this.progressUrl) {
40891             // push a hidden field onto the list of fields..
40892             this.addxtype( {
40893                     xns: Roo.form, 
40894                     xtype : 'Hidden', 
40895                     name : 'UPLOAD_IDENTIFIER' 
40896             });
40897         }
40898         
40899     
40900     Roo.each(xitems, this.addxtype, this);
40901     
40902     
40903     
40904 };
40905
40906 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40907     /**
40908      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40909      */
40910     /**
40911      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40912      */
40913     /**
40914      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40915      */
40916     buttonAlign:'center',
40917
40918     /**
40919      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40920      */
40921     minButtonWidth:75,
40922
40923     /**
40924      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40925      * This property cascades to child containers if not set.
40926      */
40927     labelAlign:'left',
40928
40929     /**
40930      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40931      * fires a looping event with that state. This is required to bind buttons to the valid
40932      * state using the config value formBind:true on the button.
40933      */
40934     monitorValid : false,
40935
40936     /**
40937      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40938      */
40939     monitorPoll : 200,
40940     
40941     /**
40942      * @cfg {String} progressUrl - Url to return progress data 
40943      */
40944     
40945     progressUrl : false,
40946   
40947     /**
40948      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40949      * fields are added and the column is closed. If no fields are passed the column remains open
40950      * until end() is called.
40951      * @param {Object} config The config to pass to the column
40952      * @param {Field} field1 (optional)
40953      * @param {Field} field2 (optional)
40954      * @param {Field} etc (optional)
40955      * @return Column The column container object
40956      */
40957     column : function(c){
40958         var col = new Roo.form.Column(c);
40959         this.start(col);
40960         if(arguments.length > 1){ // duplicate code required because of Opera
40961             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40962             this.end();
40963         }
40964         return col;
40965     },
40966
40967     /**
40968      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40969      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40970      * until end() is called.
40971      * @param {Object} config The config to pass to the fieldset
40972      * @param {Field} field1 (optional)
40973      * @param {Field} field2 (optional)
40974      * @param {Field} etc (optional)
40975      * @return FieldSet The fieldset container object
40976      */
40977     fieldset : function(c){
40978         var fs = new Roo.form.FieldSet(c);
40979         this.start(fs);
40980         if(arguments.length > 1){ // duplicate code required because of Opera
40981             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40982             this.end();
40983         }
40984         return fs;
40985     },
40986
40987     /**
40988      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40989      * fields are added and the container is closed. If no fields are passed the container remains open
40990      * until end() is called.
40991      * @param {Object} config The config to pass to the Layout
40992      * @param {Field} field1 (optional)
40993      * @param {Field} field2 (optional)
40994      * @param {Field} etc (optional)
40995      * @return Layout The container object
40996      */
40997     container : function(c){
40998         var l = new Roo.form.Layout(c);
40999         this.start(l);
41000         if(arguments.length > 1){ // duplicate code required because of Opera
41001             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41002             this.end();
41003         }
41004         return l;
41005     },
41006
41007     /**
41008      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41009      * @param {Object} container A Roo.form.Layout or subclass of Layout
41010      * @return {Form} this
41011      */
41012     start : function(c){
41013         // cascade label info
41014         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41015         this.active.stack.push(c);
41016         c.ownerCt = this.active;
41017         this.active = c;
41018         return this;
41019     },
41020
41021     /**
41022      * Closes the current open container
41023      * @return {Form} this
41024      */
41025     end : function(){
41026         if(this.active == this.root){
41027             return this;
41028         }
41029         this.active = this.active.ownerCt;
41030         return this;
41031     },
41032
41033     /**
41034      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41035      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41036      * as the label of the field.
41037      * @param {Field} field1
41038      * @param {Field} field2 (optional)
41039      * @param {Field} etc. (optional)
41040      * @return {Form} this
41041      */
41042     add : function(){
41043         this.active.stack.push.apply(this.active.stack, arguments);
41044         this.allItems.push.apply(this.allItems,arguments);
41045         var r = [];
41046         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41047             if(a[i].isFormField){
41048                 r.push(a[i]);
41049             }
41050         }
41051         if(r.length > 0){
41052             Roo.form.Form.superclass.add.apply(this, r);
41053         }
41054         return this;
41055     },
41056     
41057
41058     
41059     
41060     
41061      /**
41062      * Find any element that has been added to a form, using it's ID or name
41063      * This can include framesets, columns etc. along with regular fields..
41064      * @param {String} id - id or name to find.
41065      
41066      * @return {Element} e - or false if nothing found.
41067      */
41068     findbyId : function(id)
41069     {
41070         var ret = false;
41071         if (!id) {
41072             return ret;
41073         }
41074         Ext.each(this.allItems, function(f){
41075             if (f.id == id || f.name == id ){
41076                 ret = f;
41077                 return false;
41078             }
41079         });
41080         return ret;
41081     },
41082
41083     
41084     
41085     /**
41086      * Render this form into the passed container. This should only be called once!
41087      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41088      * @return {Form} this
41089      */
41090     render : function(ct)
41091     {
41092         
41093         
41094         
41095         ct = Roo.get(ct);
41096         var o = this.autoCreate || {
41097             tag: 'form',
41098             method : this.method || 'POST',
41099             id : this.id || Roo.id()
41100         };
41101         this.initEl(ct.createChild(o));
41102
41103         this.root.render(this.el);
41104         
41105        
41106              
41107         this.items.each(function(f){
41108             f.render('x-form-el-'+f.id);
41109         });
41110
41111         if(this.buttons.length > 0){
41112             // tables are required to maintain order and for correct IE layout
41113             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41114                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41115                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41116             }}, null, true);
41117             var tr = tb.getElementsByTagName('tr')[0];
41118             for(var i = 0, len = this.buttons.length; i < len; i++) {
41119                 var b = this.buttons[i];
41120                 var td = document.createElement('td');
41121                 td.className = 'x-form-btn-td';
41122                 b.render(tr.appendChild(td));
41123             }
41124         }
41125         if(this.monitorValid){ // initialize after render
41126             this.startMonitoring();
41127         }
41128         this.fireEvent('rendered', this);
41129         return this;
41130     },
41131
41132     /**
41133      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41134      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41135      * object or a valid Roo.DomHelper element config
41136      * @param {Function} handler The function called when the button is clicked
41137      * @param {Object} scope (optional) The scope of the handler function
41138      * @return {Roo.Button}
41139      */
41140     addButton : function(config, handler, scope){
41141         var bc = {
41142             handler: handler,
41143             scope: scope,
41144             minWidth: this.minButtonWidth,
41145             hideParent:true
41146         };
41147         if(typeof config == "string"){
41148             bc.text = config;
41149         }else{
41150             Roo.apply(bc, config);
41151         }
41152         var btn = new Roo.Button(null, bc);
41153         this.buttons.push(btn);
41154         return btn;
41155     },
41156
41157      /**
41158      * Adds a series of form elements (using the xtype property as the factory method.
41159      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41160      * @param {Object} config 
41161      */
41162     
41163     addxtype : function()
41164     {
41165         var ar = Array.prototype.slice.call(arguments, 0);
41166         var ret = false;
41167         for(var i = 0; i < ar.length; i++) {
41168             if (!ar[i]) {
41169                 continue; // skip -- if this happends something invalid got sent, we 
41170                 // should ignore it, as basically that interface element will not show up
41171                 // and that should be pretty obvious!!
41172             }
41173             
41174             if (Roo.form[ar[i].xtype]) {
41175                 ar[i].form = this;
41176                 var fe = Roo.factory(ar[i], Roo.form);
41177                 if (!ret) {
41178                     ret = fe;
41179                 }
41180                 fe.form = this;
41181                 if (fe.store) {
41182                     fe.store.form = this;
41183                 }
41184                 if (fe.isLayout) {  
41185                          
41186                     this.start(fe);
41187                     this.allItems.push(fe);
41188                     if (fe.items && fe.addxtype) {
41189                         fe.addxtype.apply(fe, fe.items);
41190                         delete fe.items;
41191                     }
41192                      this.end();
41193                     continue;
41194                 }
41195                 
41196                 
41197                  
41198                 this.add(fe);
41199               //  console.log('adding ' + ar[i].xtype);
41200             }
41201             if (ar[i].xtype == 'Button') {  
41202                 //console.log('adding button');
41203                 //console.log(ar[i]);
41204                 this.addButton(ar[i]);
41205                 this.allItems.push(fe);
41206                 continue;
41207             }
41208             
41209             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41210                 alert('end is not supported on xtype any more, use items');
41211             //    this.end();
41212             //    //console.log('adding end');
41213             }
41214             
41215         }
41216         return ret;
41217     },
41218     
41219     /**
41220      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41221      * option "monitorValid"
41222      */
41223     startMonitoring : function(){
41224         if(!this.bound){
41225             this.bound = true;
41226             Roo.TaskMgr.start({
41227                 run : this.bindHandler,
41228                 interval : this.monitorPoll || 200,
41229                 scope: this
41230             });
41231         }
41232     },
41233
41234     /**
41235      * Stops monitoring of the valid state of this form
41236      */
41237     stopMonitoring : function(){
41238         this.bound = false;
41239     },
41240
41241     // private
41242     bindHandler : function(){
41243         if(!this.bound){
41244             return false; // stops binding
41245         }
41246         var valid = true;
41247         this.items.each(function(f){
41248             if(!f.isValid(true)){
41249                 valid = false;
41250                 return false;
41251             }
41252         });
41253         for(var i = 0, len = this.buttons.length; i < len; i++){
41254             var btn = this.buttons[i];
41255             if(btn.formBind === true && btn.disabled === valid){
41256                 btn.setDisabled(!valid);
41257             }
41258         }
41259         this.fireEvent('clientvalidation', this, valid);
41260     }
41261     
41262     
41263     
41264     
41265     
41266     
41267     
41268     
41269 });
41270
41271
41272 // back compat
41273 Roo.Form = Roo.form.Form;
41274 /*
41275  * Based on:
41276  * Ext JS Library 1.1.1
41277  * Copyright(c) 2006-2007, Ext JS, LLC.
41278  *
41279  * Originally Released Under LGPL - original licence link has changed is not relivant.
41280  *
41281  * Fork - LGPL
41282  * <script type="text/javascript">
41283  */
41284  
41285  /**
41286  * @class Roo.form.Action
41287  * Internal Class used to handle form actions
41288  * @constructor
41289  * @param {Roo.form.BasicForm} el The form element or its id
41290  * @param {Object} config Configuration options
41291  */
41292  
41293  
41294 // define the action interface
41295 Roo.form.Action = function(form, options){
41296     this.form = form;
41297     this.options = options || {};
41298 };
41299 /**
41300  * Client Validation Failed
41301  * @const 
41302  */
41303 Roo.form.Action.CLIENT_INVALID = 'client';
41304 /**
41305  * Server Validation Failed
41306  * @const 
41307  */
41308  Roo.form.Action.SERVER_INVALID = 'server';
41309  /**
41310  * Connect to Server Failed
41311  * @const 
41312  */
41313 Roo.form.Action.CONNECT_FAILURE = 'connect';
41314 /**
41315  * Reading Data from Server Failed
41316  * @const 
41317  */
41318 Roo.form.Action.LOAD_FAILURE = 'load';
41319
41320 Roo.form.Action.prototype = {
41321     type : 'default',
41322     failureType : undefined,
41323     response : undefined,
41324     result : undefined,
41325
41326     // interface method
41327     run : function(options){
41328
41329     },
41330
41331     // interface method
41332     success : function(response){
41333
41334     },
41335
41336     // interface method
41337     handleResponse : function(response){
41338
41339     },
41340
41341     // default connection failure
41342     failure : function(response){
41343         this.response = response;
41344         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41345         this.form.afterAction(this, false);
41346     },
41347
41348     processResponse : function(response){
41349         this.response = response;
41350         if(!response.responseText){
41351             return true;
41352         }
41353         this.result = this.handleResponse(response);
41354         return this.result;
41355     },
41356
41357     // utility functions used internally
41358     getUrl : function(appendParams){
41359         var url = this.options.url || this.form.url || this.form.el.dom.action;
41360         if(appendParams){
41361             var p = this.getParams();
41362             if(p){
41363                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41364             }
41365         }
41366         return url;
41367     },
41368
41369     getMethod : function(){
41370         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41371     },
41372
41373     getParams : function(){
41374         var bp = this.form.baseParams;
41375         var p = this.options.params;
41376         if(p){
41377             if(typeof p == "object"){
41378                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41379             }else if(typeof p == 'string' && bp){
41380                 p += '&' + Roo.urlEncode(bp);
41381             }
41382         }else if(bp){
41383             p = Roo.urlEncode(bp);
41384         }
41385         return p;
41386     },
41387
41388     createCallback : function(){
41389         return {
41390             success: this.success,
41391             failure: this.failure,
41392             scope: this,
41393             timeout: (this.form.timeout*1000),
41394             upload: this.form.fileUpload ? this.success : undefined
41395         };
41396     }
41397 };
41398
41399 Roo.form.Action.Submit = function(form, options){
41400     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41401 };
41402
41403 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41404     type : 'submit',
41405
41406     haveProgress : false,
41407     uploadComplete : false,
41408     
41409     // uploadProgress indicator.
41410     uploadProgress : function()
41411     {
41412         if (!this.form.progressUrl) {
41413             return;
41414         }
41415         
41416         if (!this.haveProgress) {
41417             Roo.MessageBox.progress("Uploading", "Uploading");
41418         }
41419         if (this.uploadComplete) {
41420            Roo.MessageBox.hide();
41421            return;
41422         }
41423         
41424         this.haveProgress = true;
41425    
41426         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41427         
41428         var c = new Roo.data.Connection();
41429         c.request({
41430             url : this.form.progressUrl,
41431             params: {
41432                 id : uid
41433             },
41434             method: 'GET',
41435             success : function(req){
41436                //console.log(data);
41437                 var rdata = false;
41438                 var edata;
41439                 try  {
41440                    rdata = Roo.decode(req.responseText)
41441                 } catch (e) {
41442                     Roo.log("Invalid data from server..");
41443                     Roo.log(edata);
41444                     return;
41445                 }
41446                 if (!rdata || !rdata.success) {
41447                     Roo.log(rdata);
41448                     return;
41449                 }
41450                 var data = rdata.data;
41451                 
41452                 if (this.uploadComplete) {
41453                    Roo.MessageBox.hide();
41454                    return;
41455                 }
41456                    
41457                 if (data){
41458                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41459                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41460                     );
41461                 }
41462                 this.uploadProgress.defer(2000,this);
41463             },
41464        
41465             failure: function(data) {
41466                 Roo.log('progress url failed ');
41467                 Roo.log(data);
41468             },
41469             scope : this
41470         });
41471            
41472     },
41473     
41474     
41475     run : function()
41476     {
41477         // run get Values on the form, so it syncs any secondary forms.
41478         this.form.getValues();
41479         
41480         var o = this.options;
41481         var method = this.getMethod();
41482         var isPost = method == 'POST';
41483         if(o.clientValidation === false || this.form.isValid()){
41484             
41485             if (this.form.progressUrl) {
41486                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41487                     (new Date() * 1) + '' + Math.random());
41488                     
41489             } 
41490             
41491             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41492                 form:this.form.el.dom,
41493                 url:this.getUrl(!isPost),
41494                 method: method,
41495                 params:isPost ? this.getParams() : null,
41496                 isUpload: this.form.fileUpload
41497             }));
41498             
41499             this.uploadProgress();
41500
41501         }else if (o.clientValidation !== false){ // client validation failed
41502             this.failureType = Roo.form.Action.CLIENT_INVALID;
41503             this.form.afterAction(this, false);
41504         }
41505     },
41506
41507     success : function(response)
41508     {
41509         this.uploadComplete= true;
41510         if (this.haveProgress) {
41511             Roo.MessageBox.hide();
41512         }
41513         
41514         var result = this.processResponse(response);
41515         if(result === true || result.success){
41516             this.form.afterAction(this, true);
41517             return;
41518         }
41519         if(result.errors){
41520             this.form.markInvalid(result.errors);
41521             this.failureType = Roo.form.Action.SERVER_INVALID;
41522         }
41523         this.form.afterAction(this, false);
41524     },
41525     failure : function(response)
41526     {
41527         this.uploadComplete= true;
41528         if (this.haveProgress) {
41529             Roo.MessageBox.hide();
41530         }
41531         
41532         this.response = response;
41533         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41534         this.form.afterAction(this, false);
41535     },
41536     
41537     handleResponse : function(response){
41538         if(this.form.errorReader){
41539             var rs = this.form.errorReader.read(response);
41540             var errors = [];
41541             if(rs.records){
41542                 for(var i = 0, len = rs.records.length; i < len; i++) {
41543                     var r = rs.records[i];
41544                     errors[i] = r.data;
41545                 }
41546             }
41547             if(errors.length < 1){
41548                 errors = null;
41549             }
41550             return {
41551                 success : rs.success,
41552                 errors : errors
41553             };
41554         }
41555         var ret = false;
41556         try {
41557             ret = Roo.decode(response.responseText);
41558         } catch (e) {
41559             ret = {
41560                 success: false,
41561                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41562                 errors : []
41563             };
41564         }
41565         return ret;
41566         
41567     }
41568 });
41569
41570
41571 Roo.form.Action.Load = function(form, options){
41572     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41573     this.reader = this.form.reader;
41574 };
41575
41576 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41577     type : 'load',
41578
41579     run : function(){
41580         Roo.Ajax.request(Roo.apply(
41581                 this.createCallback(), {
41582                     method:this.getMethod(),
41583                     url:this.getUrl(false),
41584                     params:this.getParams()
41585         }));
41586     },
41587
41588     success : function(response){
41589         var result = this.processResponse(response);
41590         if(result === true || !result.success || !result.data){
41591             this.failureType = Roo.form.Action.LOAD_FAILURE;
41592             this.form.afterAction(this, false);
41593             return;
41594         }
41595         this.form.clearInvalid();
41596         this.form.setValues(result.data);
41597         this.form.afterAction(this, true);
41598     },
41599
41600     handleResponse : function(response){
41601         if(this.form.reader){
41602             var rs = this.form.reader.read(response);
41603             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41604             return {
41605                 success : rs.success,
41606                 data : data
41607             };
41608         }
41609         return Roo.decode(response.responseText);
41610     }
41611 });
41612
41613 Roo.form.Action.ACTION_TYPES = {
41614     'load' : Roo.form.Action.Load,
41615     'submit' : Roo.form.Action.Submit
41616 };/*
41617  * Based on:
41618  * Ext JS Library 1.1.1
41619  * Copyright(c) 2006-2007, Ext JS, LLC.
41620  *
41621  * Originally Released Under LGPL - original licence link has changed is not relivant.
41622  *
41623  * Fork - LGPL
41624  * <script type="text/javascript">
41625  */
41626  
41627 /**
41628  * @class Roo.form.Layout
41629  * @extends Roo.Component
41630  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41631  * @constructor
41632  * @param {Object} config Configuration options
41633  */
41634 Roo.form.Layout = function(config){
41635     var xitems = [];
41636     if (config.items) {
41637         xitems = config.items;
41638         delete config.items;
41639     }
41640     Roo.form.Layout.superclass.constructor.call(this, config);
41641     this.stack = [];
41642     Roo.each(xitems, this.addxtype, this);
41643      
41644 };
41645
41646 Roo.extend(Roo.form.Layout, Roo.Component, {
41647     /**
41648      * @cfg {String/Object} autoCreate
41649      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41650      */
41651     /**
41652      * @cfg {String/Object/Function} style
41653      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41654      * a function which returns such a specification.
41655      */
41656     /**
41657      * @cfg {String} labelAlign
41658      * Valid values are "left," "top" and "right" (defaults to "left")
41659      */
41660     /**
41661      * @cfg {Number} labelWidth
41662      * Fixed width in pixels of all field labels (defaults to undefined)
41663      */
41664     /**
41665      * @cfg {Boolean} clear
41666      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41667      */
41668     clear : true,
41669     /**
41670      * @cfg {String} labelSeparator
41671      * The separator to use after field labels (defaults to ':')
41672      */
41673     labelSeparator : ':',
41674     /**
41675      * @cfg {Boolean} hideLabels
41676      * True to suppress the display of field labels in this layout (defaults to false)
41677      */
41678     hideLabels : false,
41679
41680     // private
41681     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41682     
41683     isLayout : true,
41684     
41685     // private
41686     onRender : function(ct, position){
41687         if(this.el){ // from markup
41688             this.el = Roo.get(this.el);
41689         }else {  // generate
41690             var cfg = this.getAutoCreate();
41691             this.el = ct.createChild(cfg, position);
41692         }
41693         if(this.style){
41694             this.el.applyStyles(this.style);
41695         }
41696         if(this.labelAlign){
41697             this.el.addClass('x-form-label-'+this.labelAlign);
41698         }
41699         if(this.hideLabels){
41700             this.labelStyle = "display:none";
41701             this.elementStyle = "padding-left:0;";
41702         }else{
41703             if(typeof this.labelWidth == 'number'){
41704                 this.labelStyle = "width:"+this.labelWidth+"px;";
41705                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41706             }
41707             if(this.labelAlign == 'top'){
41708                 this.labelStyle = "width:auto;";
41709                 this.elementStyle = "padding-left:0;";
41710             }
41711         }
41712         var stack = this.stack;
41713         var slen = stack.length;
41714         if(slen > 0){
41715             if(!this.fieldTpl){
41716                 var t = new Roo.Template(
41717                     '<div class="x-form-item {5}">',
41718                         '<label for="{0}" style="{2}">{1}{4}</label>',
41719                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41720                         '</div>',
41721                     '</div><div class="x-form-clear-left"></div>'
41722                 );
41723                 t.disableFormats = true;
41724                 t.compile();
41725                 Roo.form.Layout.prototype.fieldTpl = t;
41726             }
41727             for(var i = 0; i < slen; i++) {
41728                 if(stack[i].isFormField){
41729                     this.renderField(stack[i]);
41730                 }else{
41731                     this.renderComponent(stack[i]);
41732                 }
41733             }
41734         }
41735         if(this.clear){
41736             this.el.createChild({cls:'x-form-clear'});
41737         }
41738     },
41739
41740     // private
41741     renderField : function(f){
41742         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41743                f.id, //0
41744                f.fieldLabel, //1
41745                f.labelStyle||this.labelStyle||'', //2
41746                this.elementStyle||'', //3
41747                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41748                f.itemCls||this.itemCls||''  //5
41749        ], true).getPrevSibling());
41750     },
41751
41752     // private
41753     renderComponent : function(c){
41754         c.render(c.isLayout ? this.el : this.el.createChild());    
41755     },
41756     /**
41757      * Adds a object form elements (using the xtype property as the factory method.)
41758      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41759      * @param {Object} config 
41760      */
41761     addxtype : function(o)
41762     {
41763         // create the lement.
41764         o.form = this.form;
41765         var fe = Roo.factory(o, Roo.form);
41766         this.form.allItems.push(fe);
41767         this.stack.push(fe);
41768         
41769         if (fe.isFormField) {
41770             this.form.items.add(fe);
41771         }
41772          
41773         return fe;
41774     }
41775 });
41776
41777 /**
41778  * @class Roo.form.Column
41779  * @extends Roo.form.Layout
41780  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41781  * @constructor
41782  * @param {Object} config Configuration options
41783  */
41784 Roo.form.Column = function(config){
41785     Roo.form.Column.superclass.constructor.call(this, config);
41786 };
41787
41788 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41789     /**
41790      * @cfg {Number/String} width
41791      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41792      */
41793     /**
41794      * @cfg {String/Object} autoCreate
41795      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41796      */
41797
41798     // private
41799     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41800
41801     // private
41802     onRender : function(ct, position){
41803         Roo.form.Column.superclass.onRender.call(this, ct, position);
41804         if(this.width){
41805             this.el.setWidth(this.width);
41806         }
41807     }
41808 });
41809
41810
41811 /**
41812  * @class Roo.form.Row
41813  * @extends Roo.form.Layout
41814  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41815  * @constructor
41816  * @param {Object} config Configuration options
41817  */
41818
41819  
41820 Roo.form.Row = function(config){
41821     Roo.form.Row.superclass.constructor.call(this, config);
41822 };
41823  
41824 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41825       /**
41826      * @cfg {Number/String} width
41827      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41828      */
41829     /**
41830      * @cfg {Number/String} height
41831      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41832      */
41833     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41834     
41835     padWidth : 20,
41836     // private
41837     onRender : function(ct, position){
41838         //console.log('row render');
41839         if(!this.rowTpl){
41840             var t = new Roo.Template(
41841                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41842                     '<label for="{0}" style="{2}">{1}{4}</label>',
41843                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41844                     '</div>',
41845                 '</div>'
41846             );
41847             t.disableFormats = true;
41848             t.compile();
41849             Roo.form.Layout.prototype.rowTpl = t;
41850         }
41851         this.fieldTpl = this.rowTpl;
41852         
41853         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41854         var labelWidth = 100;
41855         
41856         if ((this.labelAlign != 'top')) {
41857             if (typeof this.labelWidth == 'number') {
41858                 labelWidth = this.labelWidth
41859             }
41860             this.padWidth =  20 + labelWidth;
41861             
41862         }
41863         
41864         Roo.form.Column.superclass.onRender.call(this, ct, position);
41865         if(this.width){
41866             this.el.setWidth(this.width);
41867         }
41868         if(this.height){
41869             this.el.setHeight(this.height);
41870         }
41871     },
41872     
41873     // private
41874     renderField : function(f){
41875         f.fieldEl = this.fieldTpl.append(this.el, [
41876                f.id, f.fieldLabel,
41877                f.labelStyle||this.labelStyle||'',
41878                this.elementStyle||'',
41879                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41880                f.itemCls||this.itemCls||'',
41881                f.width ? f.width + this.padWidth : 160 + this.padWidth
41882        ],true);
41883     }
41884 });
41885  
41886
41887 /**
41888  * @class Roo.form.FieldSet
41889  * @extends Roo.form.Layout
41890  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41891  * @constructor
41892  * @param {Object} config Configuration options
41893  */
41894 Roo.form.FieldSet = function(config){
41895     Roo.form.FieldSet.superclass.constructor.call(this, config);
41896 };
41897
41898 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41899     /**
41900      * @cfg {String} legend
41901      * The text to display as the legend for the FieldSet (defaults to '')
41902      */
41903     /**
41904      * @cfg {String/Object} autoCreate
41905      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41906      */
41907
41908     // private
41909     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41910
41911     // private
41912     onRender : function(ct, position){
41913         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41914         if(this.legend){
41915             this.setLegend(this.legend);
41916         }
41917     },
41918
41919     // private
41920     setLegend : function(text){
41921         if(this.rendered){
41922             this.el.child('legend').update(text);
41923         }
41924     }
41925 });/*
41926  * Based on:
41927  * Ext JS Library 1.1.1
41928  * Copyright(c) 2006-2007, Ext JS, LLC.
41929  *
41930  * Originally Released Under LGPL - original licence link has changed is not relivant.
41931  *
41932  * Fork - LGPL
41933  * <script type="text/javascript">
41934  */
41935 /**
41936  * @class Roo.form.VTypes
41937  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41938  * @singleton
41939  */
41940 Roo.form.VTypes = function(){
41941     // closure these in so they are only created once.
41942     var alpha = /^[a-zA-Z_]+$/;
41943     var alphanum = /^[a-zA-Z0-9_]+$/;
41944     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41945     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41946
41947     // All these messages and functions are configurable
41948     return {
41949         /**
41950          * The function used to validate email addresses
41951          * @param {String} value The email address
41952          */
41953         'email' : function(v){
41954             return email.test(v);
41955         },
41956         /**
41957          * The error text to display when the email validation function returns false
41958          * @type String
41959          */
41960         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41961         /**
41962          * The keystroke filter mask to be applied on email input
41963          * @type RegExp
41964          */
41965         'emailMask' : /[a-z0-9_\.\-@]/i,
41966
41967         /**
41968          * The function used to validate URLs
41969          * @param {String} value The URL
41970          */
41971         'url' : function(v){
41972             return url.test(v);
41973         },
41974         /**
41975          * The error text to display when the url validation function returns false
41976          * @type String
41977          */
41978         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41979         
41980         /**
41981          * The function used to validate alpha values
41982          * @param {String} value The value
41983          */
41984         'alpha' : function(v){
41985             return alpha.test(v);
41986         },
41987         /**
41988          * The error text to display when the alpha validation function returns false
41989          * @type String
41990          */
41991         'alphaText' : 'This field should only contain letters and _',
41992         /**
41993          * The keystroke filter mask to be applied on alpha input
41994          * @type RegExp
41995          */
41996         'alphaMask' : /[a-z_]/i,
41997
41998         /**
41999          * The function used to validate alphanumeric values
42000          * @param {String} value The value
42001          */
42002         'alphanum' : function(v){
42003             return alphanum.test(v);
42004         },
42005         /**
42006          * The error text to display when the alphanumeric validation function returns false
42007          * @type String
42008          */
42009         'alphanumText' : 'This field should only contain letters, numbers and _',
42010         /**
42011          * The keystroke filter mask to be applied on alphanumeric input
42012          * @type RegExp
42013          */
42014         'alphanumMask' : /[a-z0-9_]/i
42015     };
42016 }();//<script type="text/javascript">
42017
42018 /**
42019  * @class Roo.form.FCKeditor
42020  * @extends Roo.form.TextArea
42021  * Wrapper around the FCKEditor http://www.fckeditor.net
42022  * @constructor
42023  * Creates a new FCKeditor
42024  * @param {Object} config Configuration options
42025  */
42026 Roo.form.FCKeditor = function(config){
42027     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42028     this.addEvents({
42029          /**
42030          * @event editorinit
42031          * Fired when the editor is initialized - you can add extra handlers here..
42032          * @param {FCKeditor} this
42033          * @param {Object} the FCK object.
42034          */
42035         editorinit : true
42036     });
42037     
42038     
42039 };
42040 Roo.form.FCKeditor.editors = { };
42041 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42042 {
42043     //defaultAutoCreate : {
42044     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42045     //},
42046     // private
42047     /**
42048      * @cfg {Object} fck options - see fck manual for details.
42049      */
42050     fckconfig : false,
42051     
42052     /**
42053      * @cfg {Object} fck toolbar set (Basic or Default)
42054      */
42055     toolbarSet : 'Basic',
42056     /**
42057      * @cfg {Object} fck BasePath
42058      */ 
42059     basePath : '/fckeditor/',
42060     
42061     
42062     frame : false,
42063     
42064     value : '',
42065     
42066    
42067     onRender : function(ct, position)
42068     {
42069         if(!this.el){
42070             this.defaultAutoCreate = {
42071                 tag: "textarea",
42072                 style:"width:300px;height:60px;",
42073                 autocomplete: "off"
42074             };
42075         }
42076         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42077         /*
42078         if(this.grow){
42079             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42080             if(this.preventScrollbars){
42081                 this.el.setStyle("overflow", "hidden");
42082             }
42083             this.el.setHeight(this.growMin);
42084         }
42085         */
42086         //console.log('onrender' + this.getId() );
42087         Roo.form.FCKeditor.editors[this.getId()] = this;
42088          
42089
42090         this.replaceTextarea() ;
42091         
42092     },
42093     
42094     getEditor : function() {
42095         return this.fckEditor;
42096     },
42097     /**
42098      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42099      * @param {Mixed} value The value to set
42100      */
42101     
42102     
42103     setValue : function(value)
42104     {
42105         //console.log('setValue: ' + value);
42106         
42107         if(typeof(value) == 'undefined') { // not sure why this is happending...
42108             return;
42109         }
42110         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42111         
42112         //if(!this.el || !this.getEditor()) {
42113         //    this.value = value;
42114             //this.setValue.defer(100,this,[value]);    
42115         //    return;
42116         //} 
42117         
42118         if(!this.getEditor()) {
42119             return;
42120         }
42121         
42122         this.getEditor().SetData(value);
42123         
42124         //
42125
42126     },
42127
42128     /**
42129      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42130      * @return {Mixed} value The field value
42131      */
42132     getValue : function()
42133     {
42134         
42135         if (this.frame && this.frame.dom.style.display == 'none') {
42136             return Roo.form.FCKeditor.superclass.getValue.call(this);
42137         }
42138         
42139         if(!this.el || !this.getEditor()) {
42140            
42141            // this.getValue.defer(100,this); 
42142             return this.value;
42143         }
42144        
42145         
42146         var value=this.getEditor().GetData();
42147         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42148         return Roo.form.FCKeditor.superclass.getValue.call(this);
42149         
42150
42151     },
42152
42153     /**
42154      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42155      * @return {Mixed} value The field value
42156      */
42157     getRawValue : function()
42158     {
42159         if (this.frame && this.frame.dom.style.display == 'none') {
42160             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42161         }
42162         
42163         if(!this.el || !this.getEditor()) {
42164             //this.getRawValue.defer(100,this); 
42165             return this.value;
42166             return;
42167         }
42168         
42169         
42170         
42171         var value=this.getEditor().GetData();
42172         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42173         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42174          
42175     },
42176     
42177     setSize : function(w,h) {
42178         
42179         
42180         
42181         //if (this.frame && this.frame.dom.style.display == 'none') {
42182         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42183         //    return;
42184         //}
42185         //if(!this.el || !this.getEditor()) {
42186         //    this.setSize.defer(100,this, [w,h]); 
42187         //    return;
42188         //}
42189         
42190         
42191         
42192         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42193         
42194         this.frame.dom.setAttribute('width', w);
42195         this.frame.dom.setAttribute('height', h);
42196         this.frame.setSize(w,h);
42197         
42198     },
42199     
42200     toggleSourceEdit : function(value) {
42201         
42202       
42203          
42204         this.el.dom.style.display = value ? '' : 'none';
42205         this.frame.dom.style.display = value ?  'none' : '';
42206         
42207     },
42208     
42209     
42210     focus: function(tag)
42211     {
42212         if (this.frame.dom.style.display == 'none') {
42213             return Roo.form.FCKeditor.superclass.focus.call(this);
42214         }
42215         if(!this.el || !this.getEditor()) {
42216             this.focus.defer(100,this, [tag]); 
42217             return;
42218         }
42219         
42220         
42221         
42222         
42223         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42224         this.getEditor().Focus();
42225         if (tgs.length) {
42226             if (!this.getEditor().Selection.GetSelection()) {
42227                 this.focus.defer(100,this, [tag]); 
42228                 return;
42229             }
42230             
42231             
42232             var r = this.getEditor().EditorDocument.createRange();
42233             r.setStart(tgs[0],0);
42234             r.setEnd(tgs[0],0);
42235             this.getEditor().Selection.GetSelection().removeAllRanges();
42236             this.getEditor().Selection.GetSelection().addRange(r);
42237             this.getEditor().Focus();
42238         }
42239         
42240     },
42241     
42242     
42243     
42244     replaceTextarea : function()
42245     {
42246         if ( document.getElementById( this.getId() + '___Frame' ) )
42247             return ;
42248         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42249         //{
42250             // We must check the elements firstly using the Id and then the name.
42251         var oTextarea = document.getElementById( this.getId() );
42252         
42253         var colElementsByName = document.getElementsByName( this.getId() ) ;
42254          
42255         oTextarea.style.display = 'none' ;
42256
42257         if ( oTextarea.tabIndex ) {            
42258             this.TabIndex = oTextarea.tabIndex ;
42259         }
42260         
42261         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42262         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42263         this.frame = Roo.get(this.getId() + '___Frame')
42264     },
42265     
42266     _getConfigHtml : function()
42267     {
42268         var sConfig = '' ;
42269
42270         for ( var o in this.fckconfig ) {
42271             sConfig += sConfig.length > 0  ? '&amp;' : '';
42272             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42273         }
42274
42275         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42276     },
42277     
42278     
42279     _getIFrameHtml : function()
42280     {
42281         var sFile = 'fckeditor.html' ;
42282         /* no idea what this is about..
42283         try
42284         {
42285             if ( (/fcksource=true/i).test( window.top.location.search ) )
42286                 sFile = 'fckeditor.original.html' ;
42287         }
42288         catch (e) { 
42289         */
42290
42291         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42292         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42293         
42294         
42295         var html = '<iframe id="' + this.getId() +
42296             '___Frame" src="' + sLink +
42297             '" width="' + this.width +
42298             '" height="' + this.height + '"' +
42299             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42300             ' frameborder="0" scrolling="no"></iframe>' ;
42301
42302         return html ;
42303     },
42304     
42305     _insertHtmlBefore : function( html, element )
42306     {
42307         if ( element.insertAdjacentHTML )       {
42308             // IE
42309             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42310         } else { // Gecko
42311             var oRange = document.createRange() ;
42312             oRange.setStartBefore( element ) ;
42313             var oFragment = oRange.createContextualFragment( html );
42314             element.parentNode.insertBefore( oFragment, element ) ;
42315         }
42316     }
42317     
42318     
42319   
42320     
42321     
42322     
42323     
42324
42325 });
42326
42327 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42328
42329 function FCKeditor_OnComplete(editorInstance){
42330     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42331     f.fckEditor = editorInstance;
42332     //console.log("loaded");
42333     f.fireEvent('editorinit', f, editorInstance);
42334
42335   
42336
42337  
42338
42339
42340
42341
42342
42343
42344
42345
42346
42347
42348
42349
42350
42351
42352
42353 //<script type="text/javascript">
42354 /**
42355  * @class Roo.form.GridField
42356  * @extends Roo.form.Field
42357  * Embed a grid (or editable grid into a form)
42358  * STATUS ALPHA
42359  * 
42360  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42361  * it needs 
42362  * xgrid.store = Roo.data.Store
42363  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42364  * xgrid.store.reader = Roo.data.JsonReader 
42365  * 
42366  * 
42367  * @constructor
42368  * Creates a new GridField
42369  * @param {Object} config Configuration options
42370  */
42371 Roo.form.GridField = function(config){
42372     Roo.form.GridField.superclass.constructor.call(this, config);
42373      
42374 };
42375
42376 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42377     /**
42378      * @cfg {Number} width  - used to restrict width of grid..
42379      */
42380     width : 100,
42381     /**
42382      * @cfg {Number} height - used to restrict height of grid..
42383      */
42384     height : 50,
42385      /**
42386      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42387          * 
42388          *}
42389      */
42390     xgrid : false, 
42391     /**
42392      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42393      * {tag: "input", type: "checkbox", autocomplete: "off"})
42394      */
42395    // defaultAutoCreate : { tag: 'div' },
42396     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42397     /**
42398      * @cfg {String} addTitle Text to include for adding a title.
42399      */
42400     addTitle : false,
42401     //
42402     onResize : function(){
42403         Roo.form.Field.superclass.onResize.apply(this, arguments);
42404     },
42405
42406     initEvents : function(){
42407         // Roo.form.Checkbox.superclass.initEvents.call(this);
42408         // has no events...
42409        
42410     },
42411
42412
42413     getResizeEl : function(){
42414         return this.wrap;
42415     },
42416
42417     getPositionEl : function(){
42418         return this.wrap;
42419     },
42420
42421     // private
42422     onRender : function(ct, position){
42423         
42424         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42425         var style = this.style;
42426         delete this.style;
42427         
42428         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42429         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42430         this.viewEl = this.wrap.createChild({ tag: 'div' });
42431         if (style) {
42432             this.viewEl.applyStyles(style);
42433         }
42434         if (this.width) {
42435             this.viewEl.setWidth(this.width);
42436         }
42437         if (this.height) {
42438             this.viewEl.setHeight(this.height);
42439         }
42440         //if(this.inputValue !== undefined){
42441         //this.setValue(this.value);
42442         
42443         
42444         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42445         
42446         
42447         this.grid.render();
42448         this.grid.getDataSource().on('remove', this.refreshValue, this);
42449         this.grid.getDataSource().on('update', this.refreshValue, this);
42450         this.grid.on('afteredit', this.refreshValue, this);
42451  
42452     },
42453      
42454     
42455     /**
42456      * Sets the value of the item. 
42457      * @param {String} either an object  or a string..
42458      */
42459     setValue : function(v){
42460         //this.value = v;
42461         v = v || []; // empty set..
42462         // this does not seem smart - it really only affects memoryproxy grids..
42463         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42464             var ds = this.grid.getDataSource();
42465             // assumes a json reader..
42466             var data = {}
42467             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42468             ds.loadData( data);
42469         }
42470         Roo.form.GridField.superclass.setValue.call(this, v);
42471         this.refreshValue();
42472         // should load data in the grid really....
42473     },
42474     
42475     // private
42476     refreshValue: function() {
42477          var val = [];
42478         this.grid.getDataSource().each(function(r) {
42479             val.push(r.data);
42480         });
42481         this.el.dom.value = Roo.encode(val);
42482     }
42483     
42484      
42485     
42486     
42487 });/*
42488  * Based on:
42489  * Ext JS Library 1.1.1
42490  * Copyright(c) 2006-2007, Ext JS, LLC.
42491  *
42492  * Originally Released Under LGPL - original licence link has changed is not relivant.
42493  *
42494  * Fork - LGPL
42495  * <script type="text/javascript">
42496  */
42497 /**
42498  * @class Roo.form.DisplayField
42499  * @extends Roo.form.Field
42500  * A generic Field to display non-editable data.
42501  * @constructor
42502  * Creates a new Display Field item.
42503  * @param {Object} config Configuration options
42504  */
42505 Roo.form.DisplayField = function(config){
42506     Roo.form.DisplayField.superclass.constructor.call(this, config);
42507     
42508 };
42509
42510 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42511     inputType:      'hidden',
42512     allowBlank:     true,
42513     readOnly:         true,
42514     
42515  
42516     /**
42517      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42518      */
42519     focusClass : undefined,
42520     /**
42521      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42522      */
42523     fieldClass: 'x-form-field',
42524     
42525      /**
42526      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42527      */
42528     valueRenderer: undefined,
42529     
42530     width: 100,
42531     /**
42532      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42533      * {tag: "input", type: "checkbox", autocomplete: "off"})
42534      */
42535      
42536  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42537
42538     onResize : function(){
42539         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42540         
42541     },
42542
42543     initEvents : function(){
42544         // Roo.form.Checkbox.superclass.initEvents.call(this);
42545         // has no events...
42546        
42547     },
42548
42549
42550     getResizeEl : function(){
42551         return this.wrap;
42552     },
42553
42554     getPositionEl : function(){
42555         return this.wrap;
42556     },
42557
42558     // private
42559     onRender : function(ct, position){
42560         
42561         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42562         //if(this.inputValue !== undefined){
42563         this.wrap = this.el.wrap();
42564         
42565         this.viewEl = this.wrap.createChild({ tag: 'div'});
42566         
42567         if (this.bodyStyle) {
42568             this.viewEl.applyStyles(this.bodyStyle);
42569         }
42570         //this.viewEl.setStyle('padding', '2px');
42571         
42572         this.setValue(this.value);
42573         
42574     },
42575 /*
42576     // private
42577     initValue : Roo.emptyFn,
42578
42579   */
42580
42581         // private
42582     onClick : function(){
42583         
42584     },
42585
42586     /**
42587      * Sets the checked state of the checkbox.
42588      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42589      */
42590     setValue : function(v){
42591         this.value = v;
42592         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42593         // this might be called before we have a dom element..
42594         if (!this.viewEl) {
42595             return;
42596         }
42597         this.viewEl.dom.innerHTML = html;
42598         Roo.form.DisplayField.superclass.setValue.call(this, v);
42599
42600     }
42601 });//<script type="text/javasscript">
42602  
42603
42604 /**
42605  * @class Roo.DDView
42606  * A DnD enabled version of Roo.View.
42607  * @param {Element/String} container The Element in which to create the View.
42608  * @param {String} tpl The template string used to create the markup for each element of the View
42609  * @param {Object} config The configuration properties. These include all the config options of
42610  * {@link Roo.View} plus some specific to this class.<br>
42611  * <p>
42612  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42613  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42614  * <p>
42615  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42616 .x-view-drag-insert-above {
42617         border-top:1px dotted #3366cc;
42618 }
42619 .x-view-drag-insert-below {
42620         border-bottom:1px dotted #3366cc;
42621 }
42622 </code></pre>
42623  * 
42624  */
42625  
42626 Roo.DDView = function(container, tpl, config) {
42627     Roo.DDView.superclass.constructor.apply(this, arguments);
42628     this.getEl().setStyle("outline", "0px none");
42629     this.getEl().unselectable();
42630     if (this.dragGroup) {
42631                 this.setDraggable(this.dragGroup.split(","));
42632     }
42633     if (this.dropGroup) {
42634                 this.setDroppable(this.dropGroup.split(","));
42635     }
42636     if (this.deletable) {
42637         this.setDeletable();
42638     }
42639     this.isDirtyFlag = false;
42640         this.addEvents({
42641                 "drop" : true
42642         });
42643 };
42644
42645 Roo.extend(Roo.DDView, Roo.View, {
42646 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42647 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42648 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42649 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42650
42651         isFormField: true,
42652
42653         reset: Roo.emptyFn,
42654         
42655         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42656
42657         validate: function() {
42658                 return true;
42659         },
42660         
42661         destroy: function() {
42662                 this.purgeListeners();
42663                 this.getEl.removeAllListeners();
42664                 this.getEl().remove();
42665                 if (this.dragZone) {
42666                         if (this.dragZone.destroy) {
42667                                 this.dragZone.destroy();
42668                         }
42669                 }
42670                 if (this.dropZone) {
42671                         if (this.dropZone.destroy) {
42672                                 this.dropZone.destroy();
42673                         }
42674                 }
42675         },
42676
42677 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42678         getName: function() {
42679                 return this.name;
42680         },
42681
42682 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42683         setValue: function(v) {
42684                 if (!this.store) {
42685                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42686                 }
42687                 var data = {};
42688                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42689                 this.store.proxy = new Roo.data.MemoryProxy(data);
42690                 this.store.load();
42691         },
42692
42693 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42694         getValue: function() {
42695                 var result = '(';
42696                 this.store.each(function(rec) {
42697                         result += rec.id + ',';
42698                 });
42699                 return result.substr(0, result.length - 1) + ')';
42700         },
42701         
42702         getIds: function() {
42703                 var i = 0, result = new Array(this.store.getCount());
42704                 this.store.each(function(rec) {
42705                         result[i++] = rec.id;
42706                 });
42707                 return result;
42708         },
42709         
42710         isDirty: function() {
42711                 return this.isDirtyFlag;
42712         },
42713
42714 /**
42715  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42716  *      whole Element becomes the target, and this causes the drop gesture to append.
42717  */
42718     getTargetFromEvent : function(e) {
42719                 var target = e.getTarget();
42720                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42721                 target = target.parentNode;
42722                 }
42723                 if (!target) {
42724                         target = this.el.dom.lastChild || this.el.dom;
42725                 }
42726                 return target;
42727     },
42728
42729 /**
42730  *      Create the drag data which consists of an object which has the property "ddel" as
42731  *      the drag proxy element. 
42732  */
42733     getDragData : function(e) {
42734         var target = this.findItemFromChild(e.getTarget());
42735                 if(target) {
42736                         this.handleSelection(e);
42737                         var selNodes = this.getSelectedNodes();
42738             var dragData = {
42739                 source: this,
42740                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42741                 nodes: selNodes,
42742                 records: []
42743                         };
42744                         var selectedIndices = this.getSelectedIndexes();
42745                         for (var i = 0; i < selectedIndices.length; i++) {
42746                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42747                         }
42748                         if (selNodes.length == 1) {
42749                                 dragData.ddel = target.cloneNode(true); // the div element
42750                         } else {
42751                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42752                                 div.className = 'multi-proxy';
42753                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42754                                         div.appendChild(selNodes[i].cloneNode(true));
42755                                 }
42756                                 dragData.ddel = div;
42757                         }
42758             //console.log(dragData)
42759             //console.log(dragData.ddel.innerHTML)
42760                         return dragData;
42761                 }
42762         //console.log('nodragData')
42763                 return false;
42764     },
42765     
42766 /**     Specify to which ddGroup items in this DDView may be dragged. */
42767     setDraggable: function(ddGroup) {
42768         if (ddGroup instanceof Array) {
42769                 Roo.each(ddGroup, this.setDraggable, this);
42770                 return;
42771         }
42772         if (this.dragZone) {
42773                 this.dragZone.addToGroup(ddGroup);
42774         } else {
42775                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42776                                 containerScroll: true,
42777                                 ddGroup: ddGroup 
42778
42779                         });
42780 //                      Draggability implies selection. DragZone's mousedown selects the element.
42781                         if (!this.multiSelect) { this.singleSelect = true; }
42782
42783 //                      Wire the DragZone's handlers up to methods in *this*
42784                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42785                 }
42786     },
42787
42788 /**     Specify from which ddGroup this DDView accepts drops. */
42789     setDroppable: function(ddGroup) {
42790         if (ddGroup instanceof Array) {
42791                 Roo.each(ddGroup, this.setDroppable, this);
42792                 return;
42793         }
42794         if (this.dropZone) {
42795                 this.dropZone.addToGroup(ddGroup);
42796         } else {
42797                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42798                                 containerScroll: true,
42799                                 ddGroup: ddGroup
42800                         });
42801
42802 //                      Wire the DropZone's handlers up to methods in *this*
42803                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42804                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42805                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42806                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42807                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42808                 }
42809     },
42810
42811 /**     Decide whether to drop above or below a View node. */
42812     getDropPoint : function(e, n, dd){
42813         if (n == this.el.dom) { return "above"; }
42814                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42815                 var c = t + (b - t) / 2;
42816                 var y = Roo.lib.Event.getPageY(e);
42817                 if(y <= c) {
42818                         return "above";
42819                 }else{
42820                         return "below";
42821                 }
42822     },
42823
42824     onNodeEnter : function(n, dd, e, data){
42825                 return false;
42826     },
42827     
42828     onNodeOver : function(n, dd, e, data){
42829                 var pt = this.getDropPoint(e, n, dd);
42830                 // set the insert point style on the target node
42831                 var dragElClass = this.dropNotAllowed;
42832                 if (pt) {
42833                         var targetElClass;
42834                         if (pt == "above"){
42835                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42836                                 targetElClass = "x-view-drag-insert-above";
42837                         } else {
42838                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42839                                 targetElClass = "x-view-drag-insert-below";
42840                         }
42841                         if (this.lastInsertClass != targetElClass){
42842                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42843                                 this.lastInsertClass = targetElClass;
42844                         }
42845                 }
42846                 return dragElClass;
42847         },
42848
42849     onNodeOut : function(n, dd, e, data){
42850                 this.removeDropIndicators(n);
42851     },
42852
42853     onNodeDrop : function(n, dd, e, data){
42854         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42855                 return false;
42856         }
42857         var pt = this.getDropPoint(e, n, dd);
42858                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42859                 if (pt == "below") { insertAt++; }
42860                 for (var i = 0; i < data.records.length; i++) {
42861                         var r = data.records[i];
42862                         var dup = this.store.getById(r.id);
42863                         if (dup && (dd != this.dragZone)) {
42864                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42865                         } else {
42866                                 if (data.copy) {
42867                                         this.store.insert(insertAt++, r.copy());
42868                                 } else {
42869                                         data.source.isDirtyFlag = true;
42870                                         r.store.remove(r);
42871                                         this.store.insert(insertAt++, r);
42872                                 }
42873                                 this.isDirtyFlag = true;
42874                         }
42875                 }
42876                 this.dragZone.cachedTarget = null;
42877                 return true;
42878     },
42879
42880     removeDropIndicators : function(n){
42881                 if(n){
42882                         Roo.fly(n).removeClass([
42883                                 "x-view-drag-insert-above",
42884                                 "x-view-drag-insert-below"]);
42885                         this.lastInsertClass = "_noclass";
42886                 }
42887     },
42888
42889 /**
42890  *      Utility method. Add a delete option to the DDView's context menu.
42891  *      @param {String} imageUrl The URL of the "delete" icon image.
42892  */
42893         setDeletable: function(imageUrl) {
42894                 if (!this.singleSelect && !this.multiSelect) {
42895                         this.singleSelect = true;
42896                 }
42897                 var c = this.getContextMenu();
42898                 this.contextMenu.on("itemclick", function(item) {
42899                         switch (item.id) {
42900                                 case "delete":
42901                                         this.remove(this.getSelectedIndexes());
42902                                         break;
42903                         }
42904                 }, this);
42905                 this.contextMenu.add({
42906                         icon: imageUrl,
42907                         id: "delete",
42908                         text: 'Delete'
42909                 });
42910         },
42911         
42912 /**     Return the context menu for this DDView. */
42913         getContextMenu: function() {
42914                 if (!this.contextMenu) {
42915 //                      Create the View's context menu
42916                         this.contextMenu = new Roo.menu.Menu({
42917                                 id: this.id + "-contextmenu"
42918                         });
42919                         this.el.on("contextmenu", this.showContextMenu, this);
42920                 }
42921                 return this.contextMenu;
42922         },
42923         
42924         disableContextMenu: function() {
42925                 if (this.contextMenu) {
42926                         this.el.un("contextmenu", this.showContextMenu, this);
42927                 }
42928         },
42929
42930         showContextMenu: function(e, item) {
42931         item = this.findItemFromChild(e.getTarget());
42932                 if (item) {
42933                         e.stopEvent();
42934                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42935                         this.contextMenu.showAt(e.getXY());
42936             }
42937     },
42938
42939 /**
42940  *      Remove {@link Roo.data.Record}s at the specified indices.
42941  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42942  */
42943     remove: function(selectedIndices) {
42944                 selectedIndices = [].concat(selectedIndices);
42945                 for (var i = 0; i < selectedIndices.length; i++) {
42946                         var rec = this.store.getAt(selectedIndices[i]);
42947                         this.store.remove(rec);
42948                 }
42949     },
42950
42951 /**
42952  *      Double click fires the event, but also, if this is draggable, and there is only one other
42953  *      related DropZone, it transfers the selected node.
42954  */
42955     onDblClick : function(e){
42956         var item = this.findItemFromChild(e.getTarget());
42957         if(item){
42958             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42959                 return false;
42960             }
42961             if (this.dragGroup) {
42962                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42963                     while (targets.indexOf(this.dropZone) > -1) {
42964                             targets.remove(this.dropZone);
42965                                 }
42966                     if (targets.length == 1) {
42967                                         this.dragZone.cachedTarget = null;
42968                         var el = Roo.get(targets[0].getEl());
42969                         var box = el.getBox(true);
42970                         targets[0].onNodeDrop(el.dom, {
42971                                 target: el.dom,
42972                                 xy: [box.x, box.y + box.height - 1]
42973                         }, null, this.getDragData(e));
42974                     }
42975                 }
42976         }
42977     },
42978     
42979     handleSelection: function(e) {
42980                 this.dragZone.cachedTarget = null;
42981         var item = this.findItemFromChild(e.getTarget());
42982         if (!item) {
42983                 this.clearSelections(true);
42984                 return;
42985         }
42986                 if (item && (this.multiSelect || this.singleSelect)){
42987                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42988                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42989                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42990                                 this.unselect(item);
42991                         } else {
42992                                 this.select(item, this.multiSelect && e.ctrlKey);
42993                                 this.lastSelection = item;
42994                         }
42995                 }
42996     },
42997
42998     onItemClick : function(item, index, e){
42999                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43000                         return false;
43001                 }
43002                 return true;
43003     },
43004
43005     unselect : function(nodeInfo, suppressEvent){
43006                 var node = this.getNode(nodeInfo);
43007                 if(node && this.isSelected(node)){
43008                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43009                                 Roo.fly(node).removeClass(this.selectedClass);
43010                                 this.selections.remove(node);
43011                                 if(!suppressEvent){
43012                                         this.fireEvent("selectionchange", this, this.selections);
43013                                 }
43014                         }
43015                 }
43016     }
43017 });
43018 /*
43019  * Based on:
43020  * Ext JS Library 1.1.1
43021  * Copyright(c) 2006-2007, Ext JS, LLC.
43022  *
43023  * Originally Released Under LGPL - original licence link has changed is not relivant.
43024  *
43025  * Fork - LGPL
43026  * <script type="text/javascript">
43027  */
43028  
43029 /**
43030  * @class Roo.LayoutManager
43031  * @extends Roo.util.Observable
43032  * Base class for layout managers.
43033  */
43034 Roo.LayoutManager = function(container, config){
43035     Roo.LayoutManager.superclass.constructor.call(this);
43036     this.el = Roo.get(container);
43037     // ie scrollbar fix
43038     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43039         document.body.scroll = "no";
43040     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43041         this.el.position('relative');
43042     }
43043     this.id = this.el.id;
43044     this.el.addClass("x-layout-container");
43045     /** false to disable window resize monitoring @type Boolean */
43046     this.monitorWindowResize = true;
43047     this.regions = {};
43048     this.addEvents({
43049         /**
43050          * @event layout
43051          * Fires when a layout is performed. 
43052          * @param {Roo.LayoutManager} this
43053          */
43054         "layout" : true,
43055         /**
43056          * @event regionresized
43057          * Fires when the user resizes a region. 
43058          * @param {Roo.LayoutRegion} region The resized region
43059          * @param {Number} newSize The new size (width for east/west, height for north/south)
43060          */
43061         "regionresized" : true,
43062         /**
43063          * @event regioncollapsed
43064          * Fires when a region is collapsed. 
43065          * @param {Roo.LayoutRegion} region The collapsed region
43066          */
43067         "regioncollapsed" : true,
43068         /**
43069          * @event regionexpanded
43070          * Fires when a region is expanded.  
43071          * @param {Roo.LayoutRegion} region The expanded region
43072          */
43073         "regionexpanded" : true
43074     });
43075     this.updating = false;
43076     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43077 };
43078
43079 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43080     /**
43081      * Returns true if this layout is currently being updated
43082      * @return {Boolean}
43083      */
43084     isUpdating : function(){
43085         return this.updating; 
43086     },
43087     
43088     /**
43089      * Suspend the LayoutManager from doing auto-layouts while
43090      * making multiple add or remove calls
43091      */
43092     beginUpdate : function(){
43093         this.updating = true;    
43094     },
43095     
43096     /**
43097      * Restore auto-layouts and optionally disable the manager from performing a layout
43098      * @param {Boolean} noLayout true to disable a layout update 
43099      */
43100     endUpdate : function(noLayout){
43101         this.updating = false;
43102         if(!noLayout){
43103             this.layout();
43104         }    
43105     },
43106     
43107     layout: function(){
43108         
43109     },
43110     
43111     onRegionResized : function(region, newSize){
43112         this.fireEvent("regionresized", region, newSize);
43113         this.layout();
43114     },
43115     
43116     onRegionCollapsed : function(region){
43117         this.fireEvent("regioncollapsed", region);
43118     },
43119     
43120     onRegionExpanded : function(region){
43121         this.fireEvent("regionexpanded", region);
43122     },
43123         
43124     /**
43125      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43126      * performs box-model adjustments.
43127      * @return {Object} The size as an object {width: (the width), height: (the height)}
43128      */
43129     getViewSize : function(){
43130         var size;
43131         if(this.el.dom != document.body){
43132             size = this.el.getSize();
43133         }else{
43134             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43135         }
43136         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43137         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43138         return size;
43139     },
43140     
43141     /**
43142      * Returns the Element this layout is bound to.
43143      * @return {Roo.Element}
43144      */
43145     getEl : function(){
43146         return this.el;
43147     },
43148     
43149     /**
43150      * Returns the specified region.
43151      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43152      * @return {Roo.LayoutRegion}
43153      */
43154     getRegion : function(target){
43155         return this.regions[target.toLowerCase()];
43156     },
43157     
43158     onWindowResize : function(){
43159         if(this.monitorWindowResize){
43160             this.layout();
43161         }
43162     }
43163 });/*
43164  * Based on:
43165  * Ext JS Library 1.1.1
43166  * Copyright(c) 2006-2007, Ext JS, LLC.
43167  *
43168  * Originally Released Under LGPL - original licence link has changed is not relivant.
43169  *
43170  * Fork - LGPL
43171  * <script type="text/javascript">
43172  */
43173 /**
43174  * @class Roo.BorderLayout
43175  * @extends Roo.LayoutManager
43176  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43177  * please see: <br><br>
43178  * <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>
43179  * <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>
43180  * Example:
43181  <pre><code>
43182  var layout = new Roo.BorderLayout(document.body, {
43183     north: {
43184         initialSize: 25,
43185         titlebar: false
43186     },
43187     west: {
43188         split:true,
43189         initialSize: 200,
43190         minSize: 175,
43191         maxSize: 400,
43192         titlebar: true,
43193         collapsible: true
43194     },
43195     east: {
43196         split:true,
43197         initialSize: 202,
43198         minSize: 175,
43199         maxSize: 400,
43200         titlebar: true,
43201         collapsible: true
43202     },
43203     south: {
43204         split:true,
43205         initialSize: 100,
43206         minSize: 100,
43207         maxSize: 200,
43208         titlebar: true,
43209         collapsible: true
43210     },
43211     center: {
43212         titlebar: true,
43213         autoScroll:true,
43214         resizeTabs: true,
43215         minTabWidth: 50,
43216         preferredTabWidth: 150
43217     }
43218 });
43219
43220 // shorthand
43221 var CP = Roo.ContentPanel;
43222
43223 layout.beginUpdate();
43224 layout.add("north", new CP("north", "North"));
43225 layout.add("south", new CP("south", {title: "South", closable: true}));
43226 layout.add("west", new CP("west", {title: "West"}));
43227 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43228 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43229 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43230 layout.getRegion("center").showPanel("center1");
43231 layout.endUpdate();
43232 </code></pre>
43233
43234 <b>The container the layout is rendered into can be either the body element or any other element.
43235 If it is not the body element, the container needs to either be an absolute positioned element,
43236 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43237 the container size if it is not the body element.</b>
43238
43239 * @constructor
43240 * Create a new BorderLayout
43241 * @param {String/HTMLElement/Element} container The container this layout is bound to
43242 * @param {Object} config Configuration options
43243  */
43244 Roo.BorderLayout = function(container, config){
43245     config = config || {};
43246     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43247     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43248     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43249         var target = this.factory.validRegions[i];
43250         if(config[target]){
43251             this.addRegion(target, config[target]);
43252         }
43253     }
43254 };
43255
43256 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43257     /**
43258      * Creates and adds a new region if it doesn't already exist.
43259      * @param {String} target The target region key (north, south, east, west or center).
43260      * @param {Object} config The regions config object
43261      * @return {BorderLayoutRegion} The new region
43262      */
43263     addRegion : function(target, config){
43264         if(!this.regions[target]){
43265             var r = this.factory.create(target, this, config);
43266             this.bindRegion(target, r);
43267         }
43268         return this.regions[target];
43269     },
43270
43271     // private (kinda)
43272     bindRegion : function(name, r){
43273         this.regions[name] = r;
43274         r.on("visibilitychange", this.layout, this);
43275         r.on("paneladded", this.layout, this);
43276         r.on("panelremoved", this.layout, this);
43277         r.on("invalidated", this.layout, this);
43278         r.on("resized", this.onRegionResized, this);
43279         r.on("collapsed", this.onRegionCollapsed, this);
43280         r.on("expanded", this.onRegionExpanded, this);
43281     },
43282
43283     /**
43284      * Performs a layout update.
43285      */
43286     layout : function(){
43287         if(this.updating) return;
43288         var size = this.getViewSize();
43289         var w = size.width;
43290         var h = size.height;
43291         var centerW = w;
43292         var centerH = h;
43293         var centerY = 0;
43294         var centerX = 0;
43295         //var x = 0, y = 0;
43296
43297         var rs = this.regions;
43298         var north = rs["north"];
43299         var south = rs["south"]; 
43300         var west = rs["west"];
43301         var east = rs["east"];
43302         var center = rs["center"];
43303         //if(this.hideOnLayout){ // not supported anymore
43304             //c.el.setStyle("display", "none");
43305         //}
43306         if(north && north.isVisible()){
43307             var b = north.getBox();
43308             var m = north.getMargins();
43309             b.width = w - (m.left+m.right);
43310             b.x = m.left;
43311             b.y = m.top;
43312             centerY = b.height + b.y + m.bottom;
43313             centerH -= centerY;
43314             north.updateBox(this.safeBox(b));
43315         }
43316         if(south && south.isVisible()){
43317             var b = south.getBox();
43318             var m = south.getMargins();
43319             b.width = w - (m.left+m.right);
43320             b.x = m.left;
43321             var totalHeight = (b.height + m.top + m.bottom);
43322             b.y = h - totalHeight + m.top;
43323             centerH -= totalHeight;
43324             south.updateBox(this.safeBox(b));
43325         }
43326         if(west && west.isVisible()){
43327             var b = west.getBox();
43328             var m = west.getMargins();
43329             b.height = centerH - (m.top+m.bottom);
43330             b.x = m.left;
43331             b.y = centerY + m.top;
43332             var totalWidth = (b.width + m.left + m.right);
43333             centerX += totalWidth;
43334             centerW -= totalWidth;
43335             west.updateBox(this.safeBox(b));
43336         }
43337         if(east && east.isVisible()){
43338             var b = east.getBox();
43339             var m = east.getMargins();
43340             b.height = centerH - (m.top+m.bottom);
43341             var totalWidth = (b.width + m.left + m.right);
43342             b.x = w - totalWidth + m.left;
43343             b.y = centerY + m.top;
43344             centerW -= totalWidth;
43345             east.updateBox(this.safeBox(b));
43346         }
43347         if(center){
43348             var m = center.getMargins();
43349             var centerBox = {
43350                 x: centerX + m.left,
43351                 y: centerY + m.top,
43352                 width: centerW - (m.left+m.right),
43353                 height: centerH - (m.top+m.bottom)
43354             };
43355             //if(this.hideOnLayout){
43356                 //center.el.setStyle("display", "block");
43357             //}
43358             center.updateBox(this.safeBox(centerBox));
43359         }
43360         this.el.repaint();
43361         this.fireEvent("layout", this);
43362     },
43363
43364     // private
43365     safeBox : function(box){
43366         box.width = Math.max(0, box.width);
43367         box.height = Math.max(0, box.height);
43368         return box;
43369     },
43370
43371     /**
43372      * Adds a ContentPanel (or subclass) to this layout.
43373      * @param {String} target The target region key (north, south, east, west or center).
43374      * @param {Roo.ContentPanel} panel The panel to add
43375      * @return {Roo.ContentPanel} The added panel
43376      */
43377     add : function(target, panel){
43378          
43379         target = target.toLowerCase();
43380         return this.regions[target].add(panel);
43381     },
43382
43383     /**
43384      * Remove a ContentPanel (or subclass) to this layout.
43385      * @param {String} target The target region key (north, south, east, west or center).
43386      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43387      * @return {Roo.ContentPanel} The removed panel
43388      */
43389     remove : function(target, panel){
43390         target = target.toLowerCase();
43391         return this.regions[target].remove(panel);
43392     },
43393
43394     /**
43395      * Searches all regions for a panel with the specified id
43396      * @param {String} panelId
43397      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43398      */
43399     findPanel : function(panelId){
43400         var rs = this.regions;
43401         for(var target in rs){
43402             if(typeof rs[target] != "function"){
43403                 var p = rs[target].getPanel(panelId);
43404                 if(p){
43405                     return p;
43406                 }
43407             }
43408         }
43409         return null;
43410     },
43411
43412     /**
43413      * Searches all regions for a panel with the specified id and activates (shows) it.
43414      * @param {String/ContentPanel} panelId The panels id or the panel itself
43415      * @return {Roo.ContentPanel} The shown panel or null
43416      */
43417     showPanel : function(panelId) {
43418       var rs = this.regions;
43419       for(var target in rs){
43420          var r = rs[target];
43421          if(typeof r != "function"){
43422             if(r.hasPanel(panelId)){
43423                return r.showPanel(panelId);
43424             }
43425          }
43426       }
43427       return null;
43428    },
43429
43430    /**
43431      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43432      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43433      */
43434     restoreState : function(provider){
43435         if(!provider){
43436             provider = Roo.state.Manager;
43437         }
43438         var sm = new Roo.LayoutStateManager();
43439         sm.init(this, provider);
43440     },
43441
43442     /**
43443      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43444      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43445      * a valid ContentPanel config object.  Example:
43446      * <pre><code>
43447 // Create the main layout
43448 var layout = new Roo.BorderLayout('main-ct', {
43449     west: {
43450         split:true,
43451         minSize: 175,
43452         titlebar: true
43453     },
43454     center: {
43455         title:'Components'
43456     }
43457 }, 'main-ct');
43458
43459 // Create and add multiple ContentPanels at once via configs
43460 layout.batchAdd({
43461    west: {
43462        id: 'source-files',
43463        autoCreate:true,
43464        title:'Ext Source Files',
43465        autoScroll:true,
43466        fitToFrame:true
43467    },
43468    center : {
43469        el: cview,
43470        autoScroll:true,
43471        fitToFrame:true,
43472        toolbar: tb,
43473        resizeEl:'cbody'
43474    }
43475 });
43476 </code></pre>
43477      * @param {Object} regions An object containing ContentPanel configs by region name
43478      */
43479     batchAdd : function(regions){
43480         this.beginUpdate();
43481         for(var rname in regions){
43482             var lr = this.regions[rname];
43483             if(lr){
43484                 this.addTypedPanels(lr, regions[rname]);
43485             }
43486         }
43487         this.endUpdate();
43488     },
43489
43490     // private
43491     addTypedPanels : function(lr, ps){
43492         if(typeof ps == 'string'){
43493             lr.add(new Roo.ContentPanel(ps));
43494         }
43495         else if(ps instanceof Array){
43496             for(var i =0, len = ps.length; i < len; i++){
43497                 this.addTypedPanels(lr, ps[i]);
43498             }
43499         }
43500         else if(!ps.events){ // raw config?
43501             var el = ps.el;
43502             delete ps.el; // prevent conflict
43503             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43504         }
43505         else {  // panel object assumed!
43506             lr.add(ps);
43507         }
43508     },
43509     /**
43510      * Adds a xtype elements to the layout.
43511      * <pre><code>
43512
43513 layout.addxtype({
43514        xtype : 'ContentPanel',
43515        region: 'west',
43516        items: [ .... ]
43517    }
43518 );
43519
43520 layout.addxtype({
43521         xtype : 'NestedLayoutPanel',
43522         region: 'west',
43523         layout: {
43524            center: { },
43525            west: { }   
43526         },
43527         items : [ ... list of content panels or nested layout panels.. ]
43528    }
43529 );
43530 </code></pre>
43531      * @param {Object} cfg Xtype definition of item to add.
43532      */
43533     addxtype : function(cfg)
43534     {
43535         // basically accepts a pannel...
43536         // can accept a layout region..!?!?
43537        // console.log('BorderLayout add ' + cfg.xtype)
43538         
43539         if (!cfg.xtype.match(/Panel$/)) {
43540             return false;
43541         }
43542         var ret = false;
43543         var region = cfg.region;
43544         delete cfg.region;
43545         
43546           
43547         var xitems = [];
43548         if (cfg.items) {
43549             xitems = cfg.items;
43550             delete cfg.items;
43551         }
43552         
43553         
43554         switch(cfg.xtype) 
43555         {
43556             case 'ContentPanel':  // ContentPanel (el, cfg)
43557             case 'ScrollPanel':  // ContentPanel (el, cfg)
43558                 if(cfg.autoCreate) {
43559                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43560                 } else {
43561                     var el = this.el.createChild();
43562                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43563                 }
43564                 
43565                 this.add(region, ret);
43566                 break;
43567             
43568             
43569             case 'TreePanel': // our new panel!
43570                 cfg.el = this.el.createChild();
43571                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43572                 this.add(region, ret);
43573                 break;
43574             
43575             case 'NestedLayoutPanel': 
43576                 // create a new Layout (which is  a Border Layout...
43577                 var el = this.el.createChild();
43578                 var clayout = cfg.layout;
43579                 delete cfg.layout;
43580                 clayout.items   = clayout.items  || [];
43581                 // replace this exitems with the clayout ones..
43582                 xitems = clayout.items;
43583                  
43584                 
43585                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43586                     cfg.background = false;
43587                 }
43588                 var layout = new Roo.BorderLayout(el, clayout);
43589                 
43590                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43591                 //console.log('adding nested layout panel '  + cfg.toSource());
43592                 this.add(region, ret);
43593                 
43594                 break;
43595                 
43596             case 'GridPanel': 
43597             
43598                 // needs grid and region
43599                 
43600                 //var el = this.getRegion(region).el.createChild();
43601                 var el = this.el.createChild();
43602                 // create the grid first...
43603                 
43604                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43605                 delete cfg.grid;
43606                 if (region == 'center' && this.active ) {
43607                     cfg.background = false;
43608                 }
43609                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43610                 
43611                 this.add(region, ret);
43612                 if (cfg.background) {
43613                     ret.on('activate', function(gp) {
43614                         if (!gp.grid.rendered) {
43615                             gp.grid.render();
43616                         }
43617                     });
43618                 } else {
43619                     grid.render();
43620                 }
43621                 break;
43622            
43623                
43624                 
43625                 
43626             default: 
43627                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43628                 return;
43629              // GridPanel (grid, cfg)
43630             
43631         }
43632         this.beginUpdate();
43633         // add children..
43634         Roo.each(xitems, function(i)  {
43635             ret.addxtype(i);
43636         });
43637         this.endUpdate();
43638         return ret;
43639         
43640     }
43641 });
43642
43643 /**
43644  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43645  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43646  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43647  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43648  * <pre><code>
43649 // shorthand
43650 var CP = Roo.ContentPanel;
43651
43652 var layout = Roo.BorderLayout.create({
43653     north: {
43654         initialSize: 25,
43655         titlebar: false,
43656         panels: [new CP("north", "North")]
43657     },
43658     west: {
43659         split:true,
43660         initialSize: 200,
43661         minSize: 175,
43662         maxSize: 400,
43663         titlebar: true,
43664         collapsible: true,
43665         panels: [new CP("west", {title: "West"})]
43666     },
43667     east: {
43668         split:true,
43669         initialSize: 202,
43670         minSize: 175,
43671         maxSize: 400,
43672         titlebar: true,
43673         collapsible: true,
43674         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43675     },
43676     south: {
43677         split:true,
43678         initialSize: 100,
43679         minSize: 100,
43680         maxSize: 200,
43681         titlebar: true,
43682         collapsible: true,
43683         panels: [new CP("south", {title: "South", closable: true})]
43684     },
43685     center: {
43686         titlebar: true,
43687         autoScroll:true,
43688         resizeTabs: true,
43689         minTabWidth: 50,
43690         preferredTabWidth: 150,
43691         panels: [
43692             new CP("center1", {title: "Close Me", closable: true}),
43693             new CP("center2", {title: "Center Panel", closable: false})
43694         ]
43695     }
43696 }, document.body);
43697
43698 layout.getRegion("center").showPanel("center1");
43699 </code></pre>
43700  * @param config
43701  * @param targetEl
43702  */
43703 Roo.BorderLayout.create = function(config, targetEl){
43704     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43705     layout.beginUpdate();
43706     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43707     for(var j = 0, jlen = regions.length; j < jlen; j++){
43708         var lr = regions[j];
43709         if(layout.regions[lr] && config[lr].panels){
43710             var r = layout.regions[lr];
43711             var ps = config[lr].panels;
43712             layout.addTypedPanels(r, ps);
43713         }
43714     }
43715     layout.endUpdate();
43716     return layout;
43717 };
43718
43719 // private
43720 Roo.BorderLayout.RegionFactory = {
43721     // private
43722     validRegions : ["north","south","east","west","center"],
43723
43724     // private
43725     create : function(target, mgr, config){
43726         target = target.toLowerCase();
43727         if(config.lightweight || config.basic){
43728             return new Roo.BasicLayoutRegion(mgr, config, target);
43729         }
43730         switch(target){
43731             case "north":
43732                 return new Roo.NorthLayoutRegion(mgr, config);
43733             case "south":
43734                 return new Roo.SouthLayoutRegion(mgr, config);
43735             case "east":
43736                 return new Roo.EastLayoutRegion(mgr, config);
43737             case "west":
43738                 return new Roo.WestLayoutRegion(mgr, config);
43739             case "center":
43740                 return new Roo.CenterLayoutRegion(mgr, config);
43741         }
43742         throw 'Layout region "'+target+'" not supported.';
43743     }
43744 };/*
43745  * Based on:
43746  * Ext JS Library 1.1.1
43747  * Copyright(c) 2006-2007, Ext JS, LLC.
43748  *
43749  * Originally Released Under LGPL - original licence link has changed is not relivant.
43750  *
43751  * Fork - LGPL
43752  * <script type="text/javascript">
43753  */
43754  
43755 /**
43756  * @class Roo.BasicLayoutRegion
43757  * @extends Roo.util.Observable
43758  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43759  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43760  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43761  */
43762 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43763     this.mgr = mgr;
43764     this.position  = pos;
43765     this.events = {
43766         /**
43767          * @scope Roo.BasicLayoutRegion
43768          */
43769         
43770         /**
43771          * @event beforeremove
43772          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43773          * @param {Roo.LayoutRegion} this
43774          * @param {Roo.ContentPanel} panel The panel
43775          * @param {Object} e The cancel event object
43776          */
43777         "beforeremove" : true,
43778         /**
43779          * @event invalidated
43780          * Fires when the layout for this region is changed.
43781          * @param {Roo.LayoutRegion} this
43782          */
43783         "invalidated" : true,
43784         /**
43785          * @event visibilitychange
43786          * Fires when this region is shown or hidden 
43787          * @param {Roo.LayoutRegion} this
43788          * @param {Boolean} visibility true or false
43789          */
43790         "visibilitychange" : true,
43791         /**
43792          * @event paneladded
43793          * Fires when a panel is added. 
43794          * @param {Roo.LayoutRegion} this
43795          * @param {Roo.ContentPanel} panel The panel
43796          */
43797         "paneladded" : true,
43798         /**
43799          * @event panelremoved
43800          * Fires when a panel is removed. 
43801          * @param {Roo.LayoutRegion} this
43802          * @param {Roo.ContentPanel} panel The panel
43803          */
43804         "panelremoved" : true,
43805         /**
43806          * @event collapsed
43807          * Fires when this region is collapsed.
43808          * @param {Roo.LayoutRegion} this
43809          */
43810         "collapsed" : true,
43811         /**
43812          * @event expanded
43813          * Fires when this region is expanded.
43814          * @param {Roo.LayoutRegion} this
43815          */
43816         "expanded" : true,
43817         /**
43818          * @event slideshow
43819          * Fires when this region is slid into view.
43820          * @param {Roo.LayoutRegion} this
43821          */
43822         "slideshow" : true,
43823         /**
43824          * @event slidehide
43825          * Fires when this region slides out of view. 
43826          * @param {Roo.LayoutRegion} this
43827          */
43828         "slidehide" : true,
43829         /**
43830          * @event panelactivated
43831          * Fires when a panel is activated. 
43832          * @param {Roo.LayoutRegion} this
43833          * @param {Roo.ContentPanel} panel The activated panel
43834          */
43835         "panelactivated" : true,
43836         /**
43837          * @event resized
43838          * Fires when the user resizes this region. 
43839          * @param {Roo.LayoutRegion} this
43840          * @param {Number} newSize The new size (width for east/west, height for north/south)
43841          */
43842         "resized" : true
43843     };
43844     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43845     this.panels = new Roo.util.MixedCollection();
43846     this.panels.getKey = this.getPanelId.createDelegate(this);
43847     this.box = null;
43848     this.activePanel = null;
43849     // ensure listeners are added...
43850     
43851     if (config.listeners || config.events) {
43852         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43853             listeners : config.listeners || {},
43854             events : config.events || {}
43855         });
43856     }
43857     
43858     if(skipConfig !== true){
43859         this.applyConfig(config);
43860     }
43861 };
43862
43863 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43864     getPanelId : function(p){
43865         return p.getId();
43866     },
43867     
43868     applyConfig : function(config){
43869         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43870         this.config = config;
43871         
43872     },
43873     
43874     /**
43875      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43876      * the width, for horizontal (north, south) the height.
43877      * @param {Number} newSize The new width or height
43878      */
43879     resizeTo : function(newSize){
43880         var el = this.el ? this.el :
43881                  (this.activePanel ? this.activePanel.getEl() : null);
43882         if(el){
43883             switch(this.position){
43884                 case "east":
43885                 case "west":
43886                     el.setWidth(newSize);
43887                     this.fireEvent("resized", this, newSize);
43888                 break;
43889                 case "north":
43890                 case "south":
43891                     el.setHeight(newSize);
43892                     this.fireEvent("resized", this, newSize);
43893                 break;                
43894             }
43895         }
43896     },
43897     
43898     getBox : function(){
43899         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43900     },
43901     
43902     getMargins : function(){
43903         return this.margins;
43904     },
43905     
43906     updateBox : function(box){
43907         this.box = box;
43908         var el = this.activePanel.getEl();
43909         el.dom.style.left = box.x + "px";
43910         el.dom.style.top = box.y + "px";
43911         this.activePanel.setSize(box.width, box.height);
43912     },
43913     
43914     /**
43915      * Returns the container element for this region.
43916      * @return {Roo.Element}
43917      */
43918     getEl : function(){
43919         return this.activePanel;
43920     },
43921     
43922     /**
43923      * Returns true if this region is currently visible.
43924      * @return {Boolean}
43925      */
43926     isVisible : function(){
43927         return this.activePanel ? true : false;
43928     },
43929     
43930     setActivePanel : function(panel){
43931         panel = this.getPanel(panel);
43932         if(this.activePanel && this.activePanel != panel){
43933             this.activePanel.setActiveState(false);
43934             this.activePanel.getEl().setLeftTop(-10000,-10000);
43935         }
43936         this.activePanel = panel;
43937         panel.setActiveState(true);
43938         if(this.box){
43939             panel.setSize(this.box.width, this.box.height);
43940         }
43941         this.fireEvent("panelactivated", this, panel);
43942         this.fireEvent("invalidated");
43943     },
43944     
43945     /**
43946      * Show the specified panel.
43947      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43948      * @return {Roo.ContentPanel} The shown panel or null
43949      */
43950     showPanel : function(panel){
43951         if(panel = this.getPanel(panel)){
43952             this.setActivePanel(panel);
43953         }
43954         return panel;
43955     },
43956     
43957     /**
43958      * Get the active panel for this region.
43959      * @return {Roo.ContentPanel} The active panel or null
43960      */
43961     getActivePanel : function(){
43962         return this.activePanel;
43963     },
43964     
43965     /**
43966      * Add the passed ContentPanel(s)
43967      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43968      * @return {Roo.ContentPanel} The panel added (if only one was added)
43969      */
43970     add : function(panel){
43971         if(arguments.length > 1){
43972             for(var i = 0, len = arguments.length; i < len; i++) {
43973                 this.add(arguments[i]);
43974             }
43975             return null;
43976         }
43977         if(this.hasPanel(panel)){
43978             this.showPanel(panel);
43979             return panel;
43980         }
43981         var el = panel.getEl();
43982         if(el.dom.parentNode != this.mgr.el.dom){
43983             this.mgr.el.dom.appendChild(el.dom);
43984         }
43985         if(panel.setRegion){
43986             panel.setRegion(this);
43987         }
43988         this.panels.add(panel);
43989         el.setStyle("position", "absolute");
43990         if(!panel.background){
43991             this.setActivePanel(panel);
43992             if(this.config.initialSize && this.panels.getCount()==1){
43993                 this.resizeTo(this.config.initialSize);
43994             }
43995         }
43996         this.fireEvent("paneladded", this, panel);
43997         return panel;
43998     },
43999     
44000     /**
44001      * Returns true if the panel is in this region.
44002      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44003      * @return {Boolean}
44004      */
44005     hasPanel : function(panel){
44006         if(typeof panel == "object"){ // must be panel obj
44007             panel = panel.getId();
44008         }
44009         return this.getPanel(panel) ? true : false;
44010     },
44011     
44012     /**
44013      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44014      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44015      * @param {Boolean} preservePanel Overrides the config preservePanel option
44016      * @return {Roo.ContentPanel} The panel that was removed
44017      */
44018     remove : function(panel, preservePanel){
44019         panel = this.getPanel(panel);
44020         if(!panel){
44021             return null;
44022         }
44023         var e = {};
44024         this.fireEvent("beforeremove", this, panel, e);
44025         if(e.cancel === true){
44026             return null;
44027         }
44028         var panelId = panel.getId();
44029         this.panels.removeKey(panelId);
44030         return panel;
44031     },
44032     
44033     /**
44034      * Returns the panel specified or null if it's not in this region.
44035      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44036      * @return {Roo.ContentPanel}
44037      */
44038     getPanel : function(id){
44039         if(typeof id == "object"){ // must be panel obj
44040             return id;
44041         }
44042         return this.panels.get(id);
44043     },
44044     
44045     /**
44046      * Returns this regions position (north/south/east/west/center).
44047      * @return {String} 
44048      */
44049     getPosition: function(){
44050         return this.position;    
44051     }
44052 });/*
44053  * Based on:
44054  * Ext JS Library 1.1.1
44055  * Copyright(c) 2006-2007, Ext JS, LLC.
44056  *
44057  * Originally Released Under LGPL - original licence link has changed is not relivant.
44058  *
44059  * Fork - LGPL
44060  * <script type="text/javascript">
44061  */
44062  
44063 /**
44064  * @class Roo.LayoutRegion
44065  * @extends Roo.BasicLayoutRegion
44066  * This class represents a region in a layout manager.
44067  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44068  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44069  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44070  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44071  * @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})
44072  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44073  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44074  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44075  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44076  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44077  * @cfg {String} title The title for the region (overrides panel titles)
44078  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44079  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44080  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44081  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44082  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44083  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44084  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44085  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44086  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44087  * @cfg {Boolean} showPin True to show a pin button
44088 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44089 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44090 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44091 * @cfg {Number} width  For East/West panels
44092 * @cfg {Number} height For North/South panels
44093 * @cfg {Boolean} split To show the splitter
44094  */
44095 Roo.LayoutRegion = function(mgr, config, pos){
44096     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44097     var dh = Roo.DomHelper;
44098     /** This region's container element 
44099     * @type Roo.Element */
44100     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44101     /** This region's title element 
44102     * @type Roo.Element */
44103
44104     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44105         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44106         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44107     ]}, true);
44108     this.titleEl.enableDisplayMode();
44109     /** This region's title text element 
44110     * @type HTMLElement */
44111     this.titleTextEl = this.titleEl.dom.firstChild;
44112     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44113     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44114     this.closeBtn.enableDisplayMode();
44115     this.closeBtn.on("click", this.closeClicked, this);
44116     this.closeBtn.hide();
44117
44118     this.createBody(config);
44119     this.visible = true;
44120     this.collapsed = false;
44121
44122     if(config.hideWhenEmpty){
44123         this.hide();
44124         this.on("paneladded", this.validateVisibility, this);
44125         this.on("panelremoved", this.validateVisibility, this);
44126     }
44127     this.applyConfig(config);
44128 };
44129
44130 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44131
44132     createBody : function(){
44133         /** This region's body element 
44134         * @type Roo.Element */
44135         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44136     },
44137
44138     applyConfig : function(c){
44139         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44140             var dh = Roo.DomHelper;
44141             if(c.titlebar !== false){
44142                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44143                 this.collapseBtn.on("click", this.collapse, this);
44144                 this.collapseBtn.enableDisplayMode();
44145
44146                 if(c.showPin === true || this.showPin){
44147                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44148                     this.stickBtn.enableDisplayMode();
44149                     this.stickBtn.on("click", this.expand, this);
44150                     this.stickBtn.hide();
44151                 }
44152             }
44153             /** This region's collapsed element
44154             * @type Roo.Element */
44155             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44156                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44157             ]}, true);
44158             if(c.floatable !== false){
44159                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44160                this.collapsedEl.on("click", this.collapseClick, this);
44161             }
44162
44163             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44164                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44165                    id: "message", unselectable: "on", style:{"float":"left"}});
44166                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44167              }
44168             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44169             this.expandBtn.on("click", this.expand, this);
44170         }
44171         if(this.collapseBtn){
44172             this.collapseBtn.setVisible(c.collapsible == true);
44173         }
44174         this.cmargins = c.cmargins || this.cmargins ||
44175                          (this.position == "west" || this.position == "east" ?
44176                              {top: 0, left: 2, right:2, bottom: 0} :
44177                              {top: 2, left: 0, right:0, bottom: 2});
44178         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44179         this.bottomTabs = c.tabPosition != "top";
44180         this.autoScroll = c.autoScroll || false;
44181         if(this.autoScroll){
44182             this.bodyEl.setStyle("overflow", "auto");
44183         }else{
44184             this.bodyEl.setStyle("overflow", "hidden");
44185         }
44186         //if(c.titlebar !== false){
44187             if((!c.titlebar && !c.title) || c.titlebar === false){
44188                 this.titleEl.hide();
44189             }else{
44190                 this.titleEl.show();
44191                 if(c.title){
44192                     this.titleTextEl.innerHTML = c.title;
44193                 }
44194             }
44195         //}
44196         this.duration = c.duration || .30;
44197         this.slideDuration = c.slideDuration || .45;
44198         this.config = c;
44199         if(c.collapsed){
44200             this.collapse(true);
44201         }
44202         if(c.hidden){
44203             this.hide();
44204         }
44205     },
44206     /**
44207      * Returns true if this region is currently visible.
44208      * @return {Boolean}
44209      */
44210     isVisible : function(){
44211         return this.visible;
44212     },
44213
44214     /**
44215      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44216      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44217      */
44218     setCollapsedTitle : function(title){
44219         title = title || "&#160;";
44220         if(this.collapsedTitleTextEl){
44221             this.collapsedTitleTextEl.innerHTML = title;
44222         }
44223     },
44224
44225     getBox : function(){
44226         var b;
44227         if(!this.collapsed){
44228             b = this.el.getBox(false, true);
44229         }else{
44230             b = this.collapsedEl.getBox(false, true);
44231         }
44232         return b;
44233     },
44234
44235     getMargins : function(){
44236         return this.collapsed ? this.cmargins : this.margins;
44237     },
44238
44239     highlight : function(){
44240         this.el.addClass("x-layout-panel-dragover");
44241     },
44242
44243     unhighlight : function(){
44244         this.el.removeClass("x-layout-panel-dragover");
44245     },
44246
44247     updateBox : function(box){
44248         this.box = box;
44249         if(!this.collapsed){
44250             this.el.dom.style.left = box.x + "px";
44251             this.el.dom.style.top = box.y + "px";
44252             this.updateBody(box.width, box.height);
44253         }else{
44254             this.collapsedEl.dom.style.left = box.x + "px";
44255             this.collapsedEl.dom.style.top = box.y + "px";
44256             this.collapsedEl.setSize(box.width, box.height);
44257         }
44258         if(this.tabs){
44259             this.tabs.autoSizeTabs();
44260         }
44261     },
44262
44263     updateBody : function(w, h){
44264         if(w !== null){
44265             this.el.setWidth(w);
44266             w -= this.el.getBorderWidth("rl");
44267             if(this.config.adjustments){
44268                 w += this.config.adjustments[0];
44269             }
44270         }
44271         if(h !== null){
44272             this.el.setHeight(h);
44273             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44274             h -= this.el.getBorderWidth("tb");
44275             if(this.config.adjustments){
44276                 h += this.config.adjustments[1];
44277             }
44278             this.bodyEl.setHeight(h);
44279             if(this.tabs){
44280                 h = this.tabs.syncHeight(h);
44281             }
44282         }
44283         if(this.panelSize){
44284             w = w !== null ? w : this.panelSize.width;
44285             h = h !== null ? h : this.panelSize.height;
44286         }
44287         if(this.activePanel){
44288             var el = this.activePanel.getEl();
44289             w = w !== null ? w : el.getWidth();
44290             h = h !== null ? h : el.getHeight();
44291             this.panelSize = {width: w, height: h};
44292             this.activePanel.setSize(w, h);
44293         }
44294         if(Roo.isIE && this.tabs){
44295             this.tabs.el.repaint();
44296         }
44297     },
44298
44299     /**
44300      * Returns the container element for this region.
44301      * @return {Roo.Element}
44302      */
44303     getEl : function(){
44304         return this.el;
44305     },
44306
44307     /**
44308      * Hides this region.
44309      */
44310     hide : function(){
44311         if(!this.collapsed){
44312             this.el.dom.style.left = "-2000px";
44313             this.el.hide();
44314         }else{
44315             this.collapsedEl.dom.style.left = "-2000px";
44316             this.collapsedEl.hide();
44317         }
44318         this.visible = false;
44319         this.fireEvent("visibilitychange", this, false);
44320     },
44321
44322     /**
44323      * Shows this region if it was previously hidden.
44324      */
44325     show : function(){
44326         if(!this.collapsed){
44327             this.el.show();
44328         }else{
44329             this.collapsedEl.show();
44330         }
44331         this.visible = true;
44332         this.fireEvent("visibilitychange", this, true);
44333     },
44334
44335     closeClicked : function(){
44336         if(this.activePanel){
44337             this.remove(this.activePanel);
44338         }
44339     },
44340
44341     collapseClick : function(e){
44342         if(this.isSlid){
44343            e.stopPropagation();
44344            this.slideIn();
44345         }else{
44346            e.stopPropagation();
44347            this.slideOut();
44348         }
44349     },
44350
44351     /**
44352      * Collapses this region.
44353      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44354      */
44355     collapse : function(skipAnim){
44356         if(this.collapsed) return;
44357         this.collapsed = true;
44358         if(this.split){
44359             this.split.el.hide();
44360         }
44361         if(this.config.animate && skipAnim !== true){
44362             this.fireEvent("invalidated", this);
44363             this.animateCollapse();
44364         }else{
44365             this.el.setLocation(-20000,-20000);
44366             this.el.hide();
44367             this.collapsedEl.show();
44368             this.fireEvent("collapsed", this);
44369             this.fireEvent("invalidated", this);
44370         }
44371     },
44372
44373     animateCollapse : function(){
44374         // overridden
44375     },
44376
44377     /**
44378      * Expands this region if it was previously collapsed.
44379      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44380      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44381      */
44382     expand : function(e, skipAnim){
44383         if(e) e.stopPropagation();
44384         if(!this.collapsed || this.el.hasActiveFx()) return;
44385         if(this.isSlid){
44386             this.afterSlideIn();
44387             skipAnim = true;
44388         }
44389         this.collapsed = false;
44390         if(this.config.animate && skipAnim !== true){
44391             this.animateExpand();
44392         }else{
44393             this.el.show();
44394             if(this.split){
44395                 this.split.el.show();
44396             }
44397             this.collapsedEl.setLocation(-2000,-2000);
44398             this.collapsedEl.hide();
44399             this.fireEvent("invalidated", this);
44400             this.fireEvent("expanded", this);
44401         }
44402     },
44403
44404     animateExpand : function(){
44405         // overridden
44406     },
44407
44408     initTabs : function(){
44409         this.bodyEl.setStyle("overflow", "hidden");
44410         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44411             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44412             disableTooltips: this.config.disableTabTips
44413         });
44414         if(this.config.hideTabs){
44415             ts.stripWrap.setDisplayed(false);
44416         }
44417         this.tabs = ts;
44418         ts.resizeTabs = this.config.resizeTabs === true;
44419         ts.minTabWidth = this.config.minTabWidth || 40;
44420         ts.maxTabWidth = this.config.maxTabWidth || 250;
44421         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44422         ts.monitorResize = false;
44423         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44424         ts.bodyEl.addClass('x-layout-tabs-body');
44425         this.panels.each(this.initPanelAsTab, this);
44426     },
44427
44428     initPanelAsTab : function(panel){
44429         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44430                     this.config.closeOnTab && panel.isClosable());
44431         if(panel.tabTip !== undefined){
44432             ti.setTooltip(panel.tabTip);
44433         }
44434         ti.on("activate", function(){
44435               this.setActivePanel(panel);
44436         }, this);
44437         if(this.config.closeOnTab){
44438             ti.on("beforeclose", function(t, e){
44439                 e.cancel = true;
44440                 this.remove(panel);
44441             }, this);
44442         }
44443         return ti;
44444     },
44445
44446     updatePanelTitle : function(panel, title){
44447         if(this.activePanel == panel){
44448             this.updateTitle(title);
44449         }
44450         if(this.tabs){
44451             var ti = this.tabs.getTab(panel.getEl().id);
44452             ti.setText(title);
44453             if(panel.tabTip !== undefined){
44454                 ti.setTooltip(panel.tabTip);
44455             }
44456         }
44457     },
44458
44459     updateTitle : function(title){
44460         if(this.titleTextEl && !this.config.title){
44461             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44462         }
44463     },
44464
44465     setActivePanel : function(panel){
44466         panel = this.getPanel(panel);
44467         if(this.activePanel && this.activePanel != panel){
44468             this.activePanel.setActiveState(false);
44469         }
44470         this.activePanel = panel;
44471         panel.setActiveState(true);
44472         if(this.panelSize){
44473             panel.setSize(this.panelSize.width, this.panelSize.height);
44474         }
44475         if(this.closeBtn){
44476             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44477         }
44478         this.updateTitle(panel.getTitle());
44479         if(this.tabs){
44480             this.fireEvent("invalidated", this);
44481         }
44482         this.fireEvent("panelactivated", this, panel);
44483     },
44484
44485     /**
44486      * Shows the specified panel.
44487      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44488      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44489      */
44490     showPanel : function(panel){
44491         if(panel = this.getPanel(panel)){
44492             if(this.tabs){
44493                 var tab = this.tabs.getTab(panel.getEl().id);
44494                 if(tab.isHidden()){
44495                     this.tabs.unhideTab(tab.id);
44496                 }
44497                 tab.activate();
44498             }else{
44499                 this.setActivePanel(panel);
44500             }
44501         }
44502         return panel;
44503     },
44504
44505     /**
44506      * Get the active panel for this region.
44507      * @return {Roo.ContentPanel} The active panel or null
44508      */
44509     getActivePanel : function(){
44510         return this.activePanel;
44511     },
44512
44513     validateVisibility : function(){
44514         if(this.panels.getCount() < 1){
44515             this.updateTitle("&#160;");
44516             this.closeBtn.hide();
44517             this.hide();
44518         }else{
44519             if(!this.isVisible()){
44520                 this.show();
44521             }
44522         }
44523     },
44524
44525     /**
44526      * Adds the passed ContentPanel(s) to this region.
44527      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44528      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44529      */
44530     add : function(panel){
44531         if(arguments.length > 1){
44532             for(var i = 0, len = arguments.length; i < len; i++) {
44533                 this.add(arguments[i]);
44534             }
44535             return null;
44536         }
44537         if(this.hasPanel(panel)){
44538             this.showPanel(panel);
44539             return panel;
44540         }
44541         panel.setRegion(this);
44542         this.panels.add(panel);
44543         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44544             this.bodyEl.dom.appendChild(panel.getEl().dom);
44545             if(panel.background !== true){
44546                 this.setActivePanel(panel);
44547             }
44548             this.fireEvent("paneladded", this, panel);
44549             return panel;
44550         }
44551         if(!this.tabs){
44552             this.initTabs();
44553         }else{
44554             this.initPanelAsTab(panel);
44555         }
44556         if(panel.background !== true){
44557             this.tabs.activate(panel.getEl().id);
44558         }
44559         this.fireEvent("paneladded", this, panel);
44560         return panel;
44561     },
44562
44563     /**
44564      * Hides the tab for the specified panel.
44565      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44566      */
44567     hidePanel : function(panel){
44568         if(this.tabs && (panel = this.getPanel(panel))){
44569             this.tabs.hideTab(panel.getEl().id);
44570         }
44571     },
44572
44573     /**
44574      * Unhides the tab for a previously hidden panel.
44575      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44576      */
44577     unhidePanel : function(panel){
44578         if(this.tabs && (panel = this.getPanel(panel))){
44579             this.tabs.unhideTab(panel.getEl().id);
44580         }
44581     },
44582
44583     clearPanels : function(){
44584         while(this.panels.getCount() > 0){
44585              this.remove(this.panels.first());
44586         }
44587     },
44588
44589     /**
44590      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44591      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44592      * @param {Boolean} preservePanel Overrides the config preservePanel option
44593      * @return {Roo.ContentPanel} The panel that was removed
44594      */
44595     remove : function(panel, preservePanel){
44596         panel = this.getPanel(panel);
44597         if(!panel){
44598             return null;
44599         }
44600         var e = {};
44601         this.fireEvent("beforeremove", this, panel, e);
44602         if(e.cancel === true){
44603             return null;
44604         }
44605         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44606         var panelId = panel.getId();
44607         this.panels.removeKey(panelId);
44608         if(preservePanel){
44609             document.body.appendChild(panel.getEl().dom);
44610         }
44611         if(this.tabs){
44612             this.tabs.removeTab(panel.getEl().id);
44613         }else if (!preservePanel){
44614             this.bodyEl.dom.removeChild(panel.getEl().dom);
44615         }
44616         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44617             var p = this.panels.first();
44618             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44619             tempEl.appendChild(p.getEl().dom);
44620             this.bodyEl.update("");
44621             this.bodyEl.dom.appendChild(p.getEl().dom);
44622             tempEl = null;
44623             this.updateTitle(p.getTitle());
44624             this.tabs = null;
44625             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44626             this.setActivePanel(p);
44627         }
44628         panel.setRegion(null);
44629         if(this.activePanel == panel){
44630             this.activePanel = null;
44631         }
44632         if(this.config.autoDestroy !== false && preservePanel !== true){
44633             try{panel.destroy();}catch(e){}
44634         }
44635         this.fireEvent("panelremoved", this, panel);
44636         return panel;
44637     },
44638
44639     /**
44640      * Returns the TabPanel component used by this region
44641      * @return {Roo.TabPanel}
44642      */
44643     getTabs : function(){
44644         return this.tabs;
44645     },
44646
44647     createTool : function(parentEl, className){
44648         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44649             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44650         btn.addClassOnOver("x-layout-tools-button-over");
44651         return btn;
44652     }
44653 });/*
44654  * Based on:
44655  * Ext JS Library 1.1.1
44656  * Copyright(c) 2006-2007, Ext JS, LLC.
44657  *
44658  * Originally Released Under LGPL - original licence link has changed is not relivant.
44659  *
44660  * Fork - LGPL
44661  * <script type="text/javascript">
44662  */
44663  
44664
44665
44666 /**
44667  * @class Roo.SplitLayoutRegion
44668  * @extends Roo.LayoutRegion
44669  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44670  */
44671 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44672     this.cursor = cursor;
44673     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44674 };
44675
44676 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44677     splitTip : "Drag to resize.",
44678     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44679     useSplitTips : false,
44680
44681     applyConfig : function(config){
44682         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44683         if(config.split){
44684             if(!this.split){
44685                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44686                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44687                 /** The SplitBar for this region 
44688                 * @type Roo.SplitBar */
44689                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44690                 this.split.on("moved", this.onSplitMove, this);
44691                 this.split.useShim = config.useShim === true;
44692                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44693                 if(this.useSplitTips){
44694                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44695                 }
44696                 if(config.collapsible){
44697                     this.split.el.on("dblclick", this.collapse,  this);
44698                 }
44699             }
44700             if(typeof config.minSize != "undefined"){
44701                 this.split.minSize = config.minSize;
44702             }
44703             if(typeof config.maxSize != "undefined"){
44704                 this.split.maxSize = config.maxSize;
44705             }
44706             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44707                 this.hideSplitter();
44708             }
44709         }
44710     },
44711
44712     getHMaxSize : function(){
44713          var cmax = this.config.maxSize || 10000;
44714          var center = this.mgr.getRegion("center");
44715          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44716     },
44717
44718     getVMaxSize : function(){
44719          var cmax = this.config.maxSize || 10000;
44720          var center = this.mgr.getRegion("center");
44721          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44722     },
44723
44724     onSplitMove : function(split, newSize){
44725         this.fireEvent("resized", this, newSize);
44726     },
44727     
44728     /** 
44729      * Returns the {@link Roo.SplitBar} for this region.
44730      * @return {Roo.SplitBar}
44731      */
44732     getSplitBar : function(){
44733         return this.split;
44734     },
44735     
44736     hide : function(){
44737         this.hideSplitter();
44738         Roo.SplitLayoutRegion.superclass.hide.call(this);
44739     },
44740
44741     hideSplitter : function(){
44742         if(this.split){
44743             this.split.el.setLocation(-2000,-2000);
44744             this.split.el.hide();
44745         }
44746     },
44747
44748     show : function(){
44749         if(this.split){
44750             this.split.el.show();
44751         }
44752         Roo.SplitLayoutRegion.superclass.show.call(this);
44753     },
44754     
44755     beforeSlide: function(){
44756         if(Roo.isGecko){// firefox overflow auto bug workaround
44757             this.bodyEl.clip();
44758             if(this.tabs) this.tabs.bodyEl.clip();
44759             if(this.activePanel){
44760                 this.activePanel.getEl().clip();
44761                 
44762                 if(this.activePanel.beforeSlide){
44763                     this.activePanel.beforeSlide();
44764                 }
44765             }
44766         }
44767     },
44768     
44769     afterSlide : function(){
44770         if(Roo.isGecko){// firefox overflow auto bug workaround
44771             this.bodyEl.unclip();
44772             if(this.tabs) this.tabs.bodyEl.unclip();
44773             if(this.activePanel){
44774                 this.activePanel.getEl().unclip();
44775                 if(this.activePanel.afterSlide){
44776                     this.activePanel.afterSlide();
44777                 }
44778             }
44779         }
44780     },
44781
44782     initAutoHide : function(){
44783         if(this.autoHide !== false){
44784             if(!this.autoHideHd){
44785                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44786                 this.autoHideHd = {
44787                     "mouseout": function(e){
44788                         if(!e.within(this.el, true)){
44789                             st.delay(500);
44790                         }
44791                     },
44792                     "mouseover" : function(e){
44793                         st.cancel();
44794                     },
44795                     scope : this
44796                 };
44797             }
44798             this.el.on(this.autoHideHd);
44799         }
44800     },
44801
44802     clearAutoHide : function(){
44803         if(this.autoHide !== false){
44804             this.el.un("mouseout", this.autoHideHd.mouseout);
44805             this.el.un("mouseover", this.autoHideHd.mouseover);
44806         }
44807     },
44808
44809     clearMonitor : function(){
44810         Roo.get(document).un("click", this.slideInIf, this);
44811     },
44812
44813     // these names are backwards but not changed for compat
44814     slideOut : function(){
44815         if(this.isSlid || this.el.hasActiveFx()){
44816             return;
44817         }
44818         this.isSlid = true;
44819         if(this.collapseBtn){
44820             this.collapseBtn.hide();
44821         }
44822         this.closeBtnState = this.closeBtn.getStyle('display');
44823         this.closeBtn.hide();
44824         if(this.stickBtn){
44825             this.stickBtn.show();
44826         }
44827         this.el.show();
44828         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44829         this.beforeSlide();
44830         this.el.setStyle("z-index", 10001);
44831         this.el.slideIn(this.getSlideAnchor(), {
44832             callback: function(){
44833                 this.afterSlide();
44834                 this.initAutoHide();
44835                 Roo.get(document).on("click", this.slideInIf, this);
44836                 this.fireEvent("slideshow", this);
44837             },
44838             scope: this,
44839             block: true
44840         });
44841     },
44842
44843     afterSlideIn : function(){
44844         this.clearAutoHide();
44845         this.isSlid = false;
44846         this.clearMonitor();
44847         this.el.setStyle("z-index", "");
44848         if(this.collapseBtn){
44849             this.collapseBtn.show();
44850         }
44851         this.closeBtn.setStyle('display', this.closeBtnState);
44852         if(this.stickBtn){
44853             this.stickBtn.hide();
44854         }
44855         this.fireEvent("slidehide", this);
44856     },
44857
44858     slideIn : function(cb){
44859         if(!this.isSlid || this.el.hasActiveFx()){
44860             Roo.callback(cb);
44861             return;
44862         }
44863         this.isSlid = false;
44864         this.beforeSlide();
44865         this.el.slideOut(this.getSlideAnchor(), {
44866             callback: function(){
44867                 this.el.setLeftTop(-10000, -10000);
44868                 this.afterSlide();
44869                 this.afterSlideIn();
44870                 Roo.callback(cb);
44871             },
44872             scope: this,
44873             block: true
44874         });
44875     },
44876     
44877     slideInIf : function(e){
44878         if(!e.within(this.el)){
44879             this.slideIn();
44880         }
44881     },
44882
44883     animateCollapse : function(){
44884         this.beforeSlide();
44885         this.el.setStyle("z-index", 20000);
44886         var anchor = this.getSlideAnchor();
44887         this.el.slideOut(anchor, {
44888             callback : function(){
44889                 this.el.setStyle("z-index", "");
44890                 this.collapsedEl.slideIn(anchor, {duration:.3});
44891                 this.afterSlide();
44892                 this.el.setLocation(-10000,-10000);
44893                 this.el.hide();
44894                 this.fireEvent("collapsed", this);
44895             },
44896             scope: this,
44897             block: true
44898         });
44899     },
44900
44901     animateExpand : function(){
44902         this.beforeSlide();
44903         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44904         this.el.setStyle("z-index", 20000);
44905         this.collapsedEl.hide({
44906             duration:.1
44907         });
44908         this.el.slideIn(this.getSlideAnchor(), {
44909             callback : function(){
44910                 this.el.setStyle("z-index", "");
44911                 this.afterSlide();
44912                 if(this.split){
44913                     this.split.el.show();
44914                 }
44915                 this.fireEvent("invalidated", this);
44916                 this.fireEvent("expanded", this);
44917             },
44918             scope: this,
44919             block: true
44920         });
44921     },
44922
44923     anchors : {
44924         "west" : "left",
44925         "east" : "right",
44926         "north" : "top",
44927         "south" : "bottom"
44928     },
44929
44930     sanchors : {
44931         "west" : "l",
44932         "east" : "r",
44933         "north" : "t",
44934         "south" : "b"
44935     },
44936
44937     canchors : {
44938         "west" : "tl-tr",
44939         "east" : "tr-tl",
44940         "north" : "tl-bl",
44941         "south" : "bl-tl"
44942     },
44943
44944     getAnchor : function(){
44945         return this.anchors[this.position];
44946     },
44947
44948     getCollapseAnchor : function(){
44949         return this.canchors[this.position];
44950     },
44951
44952     getSlideAnchor : function(){
44953         return this.sanchors[this.position];
44954     },
44955
44956     getAlignAdj : function(){
44957         var cm = this.cmargins;
44958         switch(this.position){
44959             case "west":
44960                 return [0, 0];
44961             break;
44962             case "east":
44963                 return [0, 0];
44964             break;
44965             case "north":
44966                 return [0, 0];
44967             break;
44968             case "south":
44969                 return [0, 0];
44970             break;
44971         }
44972     },
44973
44974     getExpandAdj : function(){
44975         var c = this.collapsedEl, cm = this.cmargins;
44976         switch(this.position){
44977             case "west":
44978                 return [-(cm.right+c.getWidth()+cm.left), 0];
44979             break;
44980             case "east":
44981                 return [cm.right+c.getWidth()+cm.left, 0];
44982             break;
44983             case "north":
44984                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44985             break;
44986             case "south":
44987                 return [0, cm.top+cm.bottom+c.getHeight()];
44988             break;
44989         }
44990     }
44991 });/*
44992  * Based on:
44993  * Ext JS Library 1.1.1
44994  * Copyright(c) 2006-2007, Ext JS, LLC.
44995  *
44996  * Originally Released Under LGPL - original licence link has changed is not relivant.
44997  *
44998  * Fork - LGPL
44999  * <script type="text/javascript">
45000  */
45001 /*
45002  * These classes are private internal classes
45003  */
45004 Roo.CenterLayoutRegion = function(mgr, config){
45005     Roo.LayoutRegion.call(this, mgr, config, "center");
45006     this.visible = true;
45007     this.minWidth = config.minWidth || 20;
45008     this.minHeight = config.minHeight || 20;
45009 };
45010
45011 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45012     hide : function(){
45013         // center panel can't be hidden
45014     },
45015     
45016     show : function(){
45017         // center panel can't be hidden
45018     },
45019     
45020     getMinWidth: function(){
45021         return this.minWidth;
45022     },
45023     
45024     getMinHeight: function(){
45025         return this.minHeight;
45026     }
45027 });
45028
45029
45030 Roo.NorthLayoutRegion = function(mgr, config){
45031     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45032     if(this.split){
45033         this.split.placement = Roo.SplitBar.TOP;
45034         this.split.orientation = Roo.SplitBar.VERTICAL;
45035         this.split.el.addClass("x-layout-split-v");
45036     }
45037     var size = config.initialSize || config.height;
45038     if(typeof size != "undefined"){
45039         this.el.setHeight(size);
45040     }
45041 };
45042 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45043     orientation: Roo.SplitBar.VERTICAL,
45044     getBox : function(){
45045         if(this.collapsed){
45046             return this.collapsedEl.getBox();
45047         }
45048         var box = this.el.getBox();
45049         if(this.split){
45050             box.height += this.split.el.getHeight();
45051         }
45052         return box;
45053     },
45054     
45055     updateBox : function(box){
45056         if(this.split && !this.collapsed){
45057             box.height -= this.split.el.getHeight();
45058             this.split.el.setLeft(box.x);
45059             this.split.el.setTop(box.y+box.height);
45060             this.split.el.setWidth(box.width);
45061         }
45062         if(this.collapsed){
45063             this.updateBody(box.width, null);
45064         }
45065         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45066     }
45067 });
45068
45069 Roo.SouthLayoutRegion = function(mgr, config){
45070     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45071     if(this.split){
45072         this.split.placement = Roo.SplitBar.BOTTOM;
45073         this.split.orientation = Roo.SplitBar.VERTICAL;
45074         this.split.el.addClass("x-layout-split-v");
45075     }
45076     var size = config.initialSize || config.height;
45077     if(typeof size != "undefined"){
45078         this.el.setHeight(size);
45079     }
45080 };
45081 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45082     orientation: Roo.SplitBar.VERTICAL,
45083     getBox : function(){
45084         if(this.collapsed){
45085             return this.collapsedEl.getBox();
45086         }
45087         var box = this.el.getBox();
45088         if(this.split){
45089             var sh = this.split.el.getHeight();
45090             box.height += sh;
45091             box.y -= sh;
45092         }
45093         return box;
45094     },
45095     
45096     updateBox : function(box){
45097         if(this.split && !this.collapsed){
45098             var sh = this.split.el.getHeight();
45099             box.height -= sh;
45100             box.y += sh;
45101             this.split.el.setLeft(box.x);
45102             this.split.el.setTop(box.y-sh);
45103             this.split.el.setWidth(box.width);
45104         }
45105         if(this.collapsed){
45106             this.updateBody(box.width, null);
45107         }
45108         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45109     }
45110 });
45111
45112 Roo.EastLayoutRegion = function(mgr, config){
45113     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45114     if(this.split){
45115         this.split.placement = Roo.SplitBar.RIGHT;
45116         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45117         this.split.el.addClass("x-layout-split-h");
45118     }
45119     var size = config.initialSize || config.width;
45120     if(typeof size != "undefined"){
45121         this.el.setWidth(size);
45122     }
45123 };
45124 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45125     orientation: Roo.SplitBar.HORIZONTAL,
45126     getBox : function(){
45127         if(this.collapsed){
45128             return this.collapsedEl.getBox();
45129         }
45130         var box = this.el.getBox();
45131         if(this.split){
45132             var sw = this.split.el.getWidth();
45133             box.width += sw;
45134             box.x -= sw;
45135         }
45136         return box;
45137     },
45138
45139     updateBox : function(box){
45140         if(this.split && !this.collapsed){
45141             var sw = this.split.el.getWidth();
45142             box.width -= sw;
45143             this.split.el.setLeft(box.x);
45144             this.split.el.setTop(box.y);
45145             this.split.el.setHeight(box.height);
45146             box.x += sw;
45147         }
45148         if(this.collapsed){
45149             this.updateBody(null, box.height);
45150         }
45151         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45152     }
45153 });
45154
45155 Roo.WestLayoutRegion = function(mgr, config){
45156     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45157     if(this.split){
45158         this.split.placement = Roo.SplitBar.LEFT;
45159         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45160         this.split.el.addClass("x-layout-split-h");
45161     }
45162     var size = config.initialSize || config.width;
45163     if(typeof size != "undefined"){
45164         this.el.setWidth(size);
45165     }
45166 };
45167 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45168     orientation: Roo.SplitBar.HORIZONTAL,
45169     getBox : function(){
45170         if(this.collapsed){
45171             return this.collapsedEl.getBox();
45172         }
45173         var box = this.el.getBox();
45174         if(this.split){
45175             box.width += this.split.el.getWidth();
45176         }
45177         return box;
45178     },
45179     
45180     updateBox : function(box){
45181         if(this.split && !this.collapsed){
45182             var sw = this.split.el.getWidth();
45183             box.width -= sw;
45184             this.split.el.setLeft(box.x+box.width);
45185             this.split.el.setTop(box.y);
45186             this.split.el.setHeight(box.height);
45187         }
45188         if(this.collapsed){
45189             this.updateBody(null, box.height);
45190         }
45191         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45192     }
45193 });
45194 /*
45195  * Based on:
45196  * Ext JS Library 1.1.1
45197  * Copyright(c) 2006-2007, Ext JS, LLC.
45198  *
45199  * Originally Released Under LGPL - original licence link has changed is not relivant.
45200  *
45201  * Fork - LGPL
45202  * <script type="text/javascript">
45203  */
45204  
45205  
45206 /*
45207  * Private internal class for reading and applying state
45208  */
45209 Roo.LayoutStateManager = function(layout){
45210      // default empty state
45211      this.state = {
45212         north: {},
45213         south: {},
45214         east: {},
45215         west: {}       
45216     };
45217 };
45218
45219 Roo.LayoutStateManager.prototype = {
45220     init : function(layout, provider){
45221         this.provider = provider;
45222         var state = provider.get(layout.id+"-layout-state");
45223         if(state){
45224             var wasUpdating = layout.isUpdating();
45225             if(!wasUpdating){
45226                 layout.beginUpdate();
45227             }
45228             for(var key in state){
45229                 if(typeof state[key] != "function"){
45230                     var rstate = state[key];
45231                     var r = layout.getRegion(key);
45232                     if(r && rstate){
45233                         if(rstate.size){
45234                             r.resizeTo(rstate.size);
45235                         }
45236                         if(rstate.collapsed == true){
45237                             r.collapse(true);
45238                         }else{
45239                             r.expand(null, true);
45240                         }
45241                     }
45242                 }
45243             }
45244             if(!wasUpdating){
45245                 layout.endUpdate();
45246             }
45247             this.state = state; 
45248         }
45249         this.layout = layout;
45250         layout.on("regionresized", this.onRegionResized, this);
45251         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45252         layout.on("regionexpanded", this.onRegionExpanded, this);
45253     },
45254     
45255     storeState : function(){
45256         this.provider.set(this.layout.id+"-layout-state", this.state);
45257     },
45258     
45259     onRegionResized : function(region, newSize){
45260         this.state[region.getPosition()].size = newSize;
45261         this.storeState();
45262     },
45263     
45264     onRegionCollapsed : function(region){
45265         this.state[region.getPosition()].collapsed = true;
45266         this.storeState();
45267     },
45268     
45269     onRegionExpanded : function(region){
45270         this.state[region.getPosition()].collapsed = false;
45271         this.storeState();
45272     }
45273 };/*
45274  * Based on:
45275  * Ext JS Library 1.1.1
45276  * Copyright(c) 2006-2007, Ext JS, LLC.
45277  *
45278  * Originally Released Under LGPL - original licence link has changed is not relivant.
45279  *
45280  * Fork - LGPL
45281  * <script type="text/javascript">
45282  */
45283 /**
45284  * @class Roo.ContentPanel
45285  * @extends Roo.util.Observable
45286  * A basic ContentPanel element.
45287  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45288  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45289  * @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
45290  * @cfg {Boolean} closable True if the panel can be closed/removed
45291  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45292  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45293  * @cfg {Toolbar} toolbar A toolbar for this panel
45294  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45295  * @cfg {String} title The title for this panel
45296  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45297  * @cfg {String} url Calls {@link #setUrl} with this value
45298  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45299  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45300  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45301  * @constructor
45302  * Create a new ContentPanel.
45303  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45304  * @param {String/Object} config A string to set only the title or a config object
45305  * @param {String} content (optional) Set the HTML content for this panel
45306  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45307  */
45308 Roo.ContentPanel = function(el, config, content){
45309     
45310      
45311     /*
45312     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45313         config = el;
45314         el = Roo.id();
45315     }
45316     if (config && config.parentLayout) { 
45317         el = config.parentLayout.el.createChild(); 
45318     }
45319     */
45320     if(el.autoCreate){ // xtype is available if this is called from factory
45321         config = el;
45322         el = Roo.id();
45323     }
45324     this.el = Roo.get(el);
45325     if(!this.el && config && config.autoCreate){
45326         if(typeof config.autoCreate == "object"){
45327             if(!config.autoCreate.id){
45328                 config.autoCreate.id = config.id||el;
45329             }
45330             this.el = Roo.DomHelper.append(document.body,
45331                         config.autoCreate, true);
45332         }else{
45333             this.el = Roo.DomHelper.append(document.body,
45334                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45335         }
45336     }
45337     this.closable = false;
45338     this.loaded = false;
45339     this.active = false;
45340     if(typeof config == "string"){
45341         this.title = config;
45342     }else{
45343         Roo.apply(this, config);
45344     }
45345     
45346     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45347         this.wrapEl = this.el.wrap();    
45348         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45349         
45350     }
45351     
45352     
45353     
45354     if(this.resizeEl){
45355         this.resizeEl = Roo.get(this.resizeEl, true);
45356     }else{
45357         this.resizeEl = this.el;
45358     }
45359     this.addEvents({
45360         /**
45361          * @event activate
45362          * Fires when this panel is activated. 
45363          * @param {Roo.ContentPanel} this
45364          */
45365         "activate" : true,
45366         /**
45367          * @event deactivate
45368          * Fires when this panel is activated. 
45369          * @param {Roo.ContentPanel} this
45370          */
45371         "deactivate" : true,
45372
45373         /**
45374          * @event resize
45375          * Fires when this panel is resized if fitToFrame is true.
45376          * @param {Roo.ContentPanel} this
45377          * @param {Number} width The width after any component adjustments
45378          * @param {Number} height The height after any component adjustments
45379          */
45380         "resize" : true
45381     });
45382     if(this.autoScroll){
45383         this.resizeEl.setStyle("overflow", "auto");
45384     } else {
45385         // fix randome scrolling
45386         this.el.on('scroll', function() {
45387             this.scrollTo('top',0); 
45388         });
45389     }
45390     content = content || this.content;
45391     if(content){
45392         this.setContent(content);
45393     }
45394     if(config && config.url){
45395         this.setUrl(this.url, this.params, this.loadOnce);
45396     }
45397     
45398     
45399     
45400     Roo.ContentPanel.superclass.constructor.call(this);
45401 };
45402
45403 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45404     tabTip:'',
45405     setRegion : function(region){
45406         this.region = region;
45407         if(region){
45408            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45409         }else{
45410            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45411         } 
45412     },
45413     
45414     /**
45415      * Returns the toolbar for this Panel if one was configured. 
45416      * @return {Roo.Toolbar} 
45417      */
45418     getToolbar : function(){
45419         return this.toolbar;
45420     },
45421     
45422     setActiveState : function(active){
45423         this.active = active;
45424         if(!active){
45425             this.fireEvent("deactivate", this);
45426         }else{
45427             this.fireEvent("activate", this);
45428         }
45429     },
45430     /**
45431      * Updates this panel's element
45432      * @param {String} content The new content
45433      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45434     */
45435     setContent : function(content, loadScripts){
45436         this.el.update(content, loadScripts);
45437     },
45438
45439     ignoreResize : function(w, h){
45440         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45441             return true;
45442         }else{
45443             this.lastSize = {width: w, height: h};
45444             return false;
45445         }
45446     },
45447     /**
45448      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45449      * @return {Roo.UpdateManager} The UpdateManager
45450      */
45451     getUpdateManager : function(){
45452         return this.el.getUpdateManager();
45453     },
45454      /**
45455      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45456      * @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:
45457 <pre><code>
45458 panel.load({
45459     url: "your-url.php",
45460     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45461     callback: yourFunction,
45462     scope: yourObject, //(optional scope)
45463     discardUrl: false,
45464     nocache: false,
45465     text: "Loading...",
45466     timeout: 30,
45467     scripts: false
45468 });
45469 </code></pre>
45470      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45471      * 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.
45472      * @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}
45473      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45474      * @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.
45475      * @return {Roo.ContentPanel} this
45476      */
45477     load : function(){
45478         var um = this.el.getUpdateManager();
45479         um.update.apply(um, arguments);
45480         return this;
45481     },
45482
45483
45484     /**
45485      * 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.
45486      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45487      * @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)
45488      * @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)
45489      * @return {Roo.UpdateManager} The UpdateManager
45490      */
45491     setUrl : function(url, params, loadOnce){
45492         if(this.refreshDelegate){
45493             this.removeListener("activate", this.refreshDelegate);
45494         }
45495         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45496         this.on("activate", this.refreshDelegate);
45497         return this.el.getUpdateManager();
45498     },
45499     
45500     _handleRefresh : function(url, params, loadOnce){
45501         if(!loadOnce || !this.loaded){
45502             var updater = this.el.getUpdateManager();
45503             updater.update(url, params, this._setLoaded.createDelegate(this));
45504         }
45505     },
45506     
45507     _setLoaded : function(){
45508         this.loaded = true;
45509     }, 
45510     
45511     /**
45512      * Returns this panel's id
45513      * @return {String} 
45514      */
45515     getId : function(){
45516         return this.el.id;
45517     },
45518     
45519     /** 
45520      * Returns this panel's element - used by regiosn to add.
45521      * @return {Roo.Element} 
45522      */
45523     getEl : function(){
45524         return this.wrapEl || this.el;
45525     },
45526     
45527     adjustForComponents : function(width, height){
45528         if(this.resizeEl != this.el){
45529             width -= this.el.getFrameWidth('lr');
45530             height -= this.el.getFrameWidth('tb');
45531         }
45532         if(this.toolbar){
45533             var te = this.toolbar.getEl();
45534             height -= te.getHeight();
45535             te.setWidth(width);
45536         }
45537         if(this.adjustments){
45538             width += this.adjustments[0];
45539             height += this.adjustments[1];
45540         }
45541         return {"width": width, "height": height};
45542     },
45543     
45544     setSize : function(width, height){
45545         if(this.fitToFrame && !this.ignoreResize(width, height)){
45546             if(this.fitContainer && this.resizeEl != this.el){
45547                 this.el.setSize(width, height);
45548             }
45549             var size = this.adjustForComponents(width, height);
45550             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45551             this.fireEvent('resize', this, size.width, size.height);
45552         }
45553     },
45554     
45555     /**
45556      * Returns this panel's title
45557      * @return {String} 
45558      */
45559     getTitle : function(){
45560         return this.title;
45561     },
45562     
45563     /**
45564      * Set this panel's title
45565      * @param {String} title
45566      */
45567     setTitle : function(title){
45568         this.title = title;
45569         if(this.region){
45570             this.region.updatePanelTitle(this, title);
45571         }
45572     },
45573     
45574     /**
45575      * Returns true is this panel was configured to be closable
45576      * @return {Boolean} 
45577      */
45578     isClosable : function(){
45579         return this.closable;
45580     },
45581     
45582     beforeSlide : function(){
45583         this.el.clip();
45584         this.resizeEl.clip();
45585     },
45586     
45587     afterSlide : function(){
45588         this.el.unclip();
45589         this.resizeEl.unclip();
45590     },
45591     
45592     /**
45593      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45594      *   Will fail silently if the {@link #setUrl} method has not been called.
45595      *   This does not activate the panel, just updates its content.
45596      */
45597     refresh : function(){
45598         if(this.refreshDelegate){
45599            this.loaded = false;
45600            this.refreshDelegate();
45601         }
45602     },
45603     
45604     /**
45605      * Destroys this panel
45606      */
45607     destroy : function(){
45608         this.el.removeAllListeners();
45609         var tempEl = document.createElement("span");
45610         tempEl.appendChild(this.el.dom);
45611         tempEl.innerHTML = "";
45612         this.el.remove();
45613         this.el = null;
45614     },
45615     
45616       /**
45617      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45618      * <pre><code>
45619
45620 layout.addxtype({
45621        xtype : 'Form',
45622        items: [ .... ]
45623    }
45624 );
45625
45626 </code></pre>
45627      * @param {Object} cfg Xtype definition of item to add.
45628      */
45629     
45630     addxtype : function(cfg) {
45631         // add form..
45632         if (cfg.xtype.match(/^Form$/)) {
45633             var el = this.el.createChild();
45634
45635             this.form = new  Roo.form.Form(cfg);
45636             
45637             
45638             if ( this.form.allItems.length) this.form.render(el.dom);
45639             return this.form;
45640         }
45641         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45642             // views..
45643             cfg.el = this.el.appendChild(document.createElement("div"));
45644             // factory?
45645             var ret = new Roo[cfg.xtype](cfg);
45646             ret.render(false, ''); // render blank..
45647             return ret;
45648             
45649         }
45650         return false;
45651         
45652     }
45653 });
45654
45655 /**
45656  * @class Roo.GridPanel
45657  * @extends Roo.ContentPanel
45658  * @constructor
45659  * Create a new GridPanel.
45660  * @param {Roo.grid.Grid} grid The grid for this panel
45661  * @param {String/Object} config A string to set only the panel's title, or a config object
45662  */
45663 Roo.GridPanel = function(grid, config){
45664     
45665   
45666     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45667         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45668         
45669     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45670     
45671     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45672     
45673     if(this.toolbar){
45674         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45675     }
45676     // xtype created footer. - not sure if will work as we normally have to render first..
45677     if (this.footer && !this.footer.el && this.footer.xtype) {
45678         
45679         this.footer.container = this.grid.getView().getFooterPanel(true);
45680         this.footer.dataSource = this.grid.dataSource;
45681         this.footer = Roo.factory(this.footer, Roo);
45682         
45683     }
45684     
45685     grid.monitorWindowResize = false; // turn off autosizing
45686     grid.autoHeight = false;
45687     grid.autoWidth = false;
45688     this.grid = grid;
45689     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45690 };
45691
45692 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45693     getId : function(){
45694         return this.grid.id;
45695     },
45696     
45697     /**
45698      * Returns the grid for this panel
45699      * @return {Roo.grid.Grid} 
45700      */
45701     getGrid : function(){
45702         return this.grid;    
45703     },
45704     
45705     setSize : function(width, height){
45706         if(!this.ignoreResize(width, height)){
45707             var grid = this.grid;
45708             var size = this.adjustForComponents(width, height);
45709             grid.getGridEl().setSize(size.width, size.height);
45710             grid.autoSize();
45711         }
45712     },
45713     
45714     beforeSlide : function(){
45715         this.grid.getView().scroller.clip();
45716     },
45717     
45718     afterSlide : function(){
45719         this.grid.getView().scroller.unclip();
45720     },
45721     
45722     destroy : function(){
45723         this.grid.destroy();
45724         delete this.grid;
45725         Roo.GridPanel.superclass.destroy.call(this); 
45726     }
45727 });
45728
45729
45730 /**
45731  * @class Roo.NestedLayoutPanel
45732  * @extends Roo.ContentPanel
45733  * @constructor
45734  * Create a new NestedLayoutPanel.
45735  * 
45736  * 
45737  * @param {Roo.BorderLayout} layout The layout for this panel
45738  * @param {String/Object} config A string to set only the title or a config object
45739  */
45740 Roo.NestedLayoutPanel = function(layout, config)
45741 {
45742     // construct with only one argument..
45743     /* FIXME - implement nicer consturctors
45744     if (layout.layout) {
45745         config = layout;
45746         layout = config.layout;
45747         delete config.layout;
45748     }
45749     if (layout.xtype && !layout.getEl) {
45750         // then layout needs constructing..
45751         layout = Roo.factory(layout, Roo);
45752     }
45753     */
45754     
45755     
45756     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45757     
45758     layout.monitorWindowResize = false; // turn off autosizing
45759     this.layout = layout;
45760     this.layout.getEl().addClass("x-layout-nested-layout");
45761     
45762     
45763     
45764     
45765 };
45766
45767 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45768
45769     setSize : function(width, height){
45770         if(!this.ignoreResize(width, height)){
45771             var size = this.adjustForComponents(width, height);
45772             var el = this.layout.getEl();
45773             el.setSize(size.width, size.height);
45774             var touch = el.dom.offsetWidth;
45775             this.layout.layout();
45776             // ie requires a double layout on the first pass
45777             if(Roo.isIE && !this.initialized){
45778                 this.initialized = true;
45779                 this.layout.layout();
45780             }
45781         }
45782     },
45783     
45784     // activate all subpanels if not currently active..
45785     
45786     setActiveState : function(active){
45787         this.active = active;
45788         if(!active){
45789             this.fireEvent("deactivate", this);
45790             return;
45791         }
45792         
45793         this.fireEvent("activate", this);
45794         // not sure if this should happen before or after..
45795         if (!this.layout) {
45796             return; // should not happen..
45797         }
45798         var reg = false;
45799         for (var r in this.layout.regions) {
45800             reg = this.layout.getRegion(r);
45801             if (reg.getActivePanel()) {
45802                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45803                 reg.setActivePanel(reg.getActivePanel());
45804                 continue;
45805             }
45806             if (!reg.panels.length) {
45807                 continue;
45808             }
45809             reg.showPanel(reg.getPanel(0));
45810         }
45811         
45812         
45813         
45814         
45815     },
45816     
45817     /**
45818      * Returns the nested BorderLayout for this panel
45819      * @return {Roo.BorderLayout} 
45820      */
45821     getLayout : function(){
45822         return this.layout;
45823     },
45824     
45825      /**
45826      * Adds a xtype elements to the layout of the nested panel
45827      * <pre><code>
45828
45829 panel.addxtype({
45830        xtype : 'ContentPanel',
45831        region: 'west',
45832        items: [ .... ]
45833    }
45834 );
45835
45836 panel.addxtype({
45837         xtype : 'NestedLayoutPanel',
45838         region: 'west',
45839         layout: {
45840            center: { },
45841            west: { }   
45842         },
45843         items : [ ... list of content panels or nested layout panels.. ]
45844    }
45845 );
45846 </code></pre>
45847      * @param {Object} cfg Xtype definition of item to add.
45848      */
45849     addxtype : function(cfg) {
45850         return this.layout.addxtype(cfg);
45851     
45852     }
45853 });
45854
45855 Roo.ScrollPanel = function(el, config, content){
45856     config = config || {};
45857     config.fitToFrame = true;
45858     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45859     
45860     this.el.dom.style.overflow = "hidden";
45861     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45862     this.el.removeClass("x-layout-inactive-content");
45863     this.el.on("mousewheel", this.onWheel, this);
45864
45865     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45866     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45867     up.unselectable(); down.unselectable();
45868     up.on("click", this.scrollUp, this);
45869     down.on("click", this.scrollDown, this);
45870     up.addClassOnOver("x-scroller-btn-over");
45871     down.addClassOnOver("x-scroller-btn-over");
45872     up.addClassOnClick("x-scroller-btn-click");
45873     down.addClassOnClick("x-scroller-btn-click");
45874     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45875
45876     this.resizeEl = this.el;
45877     this.el = wrap; this.up = up; this.down = down;
45878 };
45879
45880 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45881     increment : 100,
45882     wheelIncrement : 5,
45883     scrollUp : function(){
45884         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45885     },
45886
45887     scrollDown : function(){
45888         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45889     },
45890
45891     afterScroll : function(){
45892         var el = this.resizeEl;
45893         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45894         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45895         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45896     },
45897
45898     setSize : function(){
45899         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45900         this.afterScroll();
45901     },
45902
45903     onWheel : function(e){
45904         var d = e.getWheelDelta();
45905         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45906         this.afterScroll();
45907         e.stopEvent();
45908     },
45909
45910     setContent : function(content, loadScripts){
45911         this.resizeEl.update(content, loadScripts);
45912     }
45913
45914 });
45915
45916
45917
45918
45919
45920
45921
45922
45923
45924 /**
45925  * @class Roo.TreePanel
45926  * @extends Roo.ContentPanel
45927  * @constructor
45928  * Create a new TreePanel. - defaults to fit/scoll contents.
45929  * @param {String/Object} config A string to set only the panel's title, or a config object
45930  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45931  */
45932 Roo.TreePanel = function(config){
45933     var el = config.el;
45934     var tree = config.tree;
45935     delete config.tree; 
45936     delete config.el; // hopefull!
45937     
45938     // wrapper for IE7 strict & safari scroll issue
45939     
45940     var treeEl = el.createChild();
45941     config.resizeEl = treeEl;
45942     
45943     
45944     
45945     Roo.TreePanel.superclass.constructor.call(this, el, config);
45946  
45947  
45948     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45949     //console.log(tree);
45950     this.on('activate', function()
45951     {
45952         if (this.tree.rendered) {
45953             return;
45954         }
45955         //console.log('render tree');
45956         this.tree.render();
45957     });
45958     
45959     this.on('resize',  function (cp, w, h) {
45960             this.tree.innerCt.setWidth(w);
45961             this.tree.innerCt.setHeight(h);
45962             this.tree.innerCt.setStyle('overflow-y', 'auto');
45963     });
45964
45965         
45966     
45967 };
45968
45969 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
45970     fitToFrame : true,
45971     autoScroll : true
45972 });
45973
45974
45975
45976
45977
45978
45979
45980
45981
45982
45983
45984 /*
45985  * Based on:
45986  * Ext JS Library 1.1.1
45987  * Copyright(c) 2006-2007, Ext JS, LLC.
45988  *
45989  * Originally Released Under LGPL - original licence link has changed is not relivant.
45990  *
45991  * Fork - LGPL
45992  * <script type="text/javascript">
45993  */
45994  
45995
45996 /**
45997  * @class Roo.ReaderLayout
45998  * @extends Roo.BorderLayout
45999  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46000  * center region containing two nested regions (a top one for a list view and one for item preview below),
46001  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46002  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46003  * expedites the setup of the overall layout and regions for this common application style.
46004  * Example:
46005  <pre><code>
46006 var reader = new Roo.ReaderLayout();
46007 var CP = Roo.ContentPanel;  // shortcut for adding
46008
46009 reader.beginUpdate();
46010 reader.add("north", new CP("north", "North"));
46011 reader.add("west", new CP("west", {title: "West"}));
46012 reader.add("east", new CP("east", {title: "East"}));
46013
46014 reader.regions.listView.add(new CP("listView", "List"));
46015 reader.regions.preview.add(new CP("preview", "Preview"));
46016 reader.endUpdate();
46017 </code></pre>
46018 * @constructor
46019 * Create a new ReaderLayout
46020 * @param {Object} config Configuration options
46021 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46022 * document.body if omitted)
46023 */
46024 Roo.ReaderLayout = function(config, renderTo){
46025     var c = config || {size:{}};
46026     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46027         north: c.north !== false ? Roo.apply({
46028             split:false,
46029             initialSize: 32,
46030             titlebar: false
46031         }, c.north) : false,
46032         west: c.west !== false ? Roo.apply({
46033             split:true,
46034             initialSize: 200,
46035             minSize: 175,
46036             maxSize: 400,
46037             titlebar: true,
46038             collapsible: true,
46039             animate: true,
46040             margins:{left:5,right:0,bottom:5,top:5},
46041             cmargins:{left:5,right:5,bottom:5,top:5}
46042         }, c.west) : false,
46043         east: c.east !== false ? Roo.apply({
46044             split:true,
46045             initialSize: 200,
46046             minSize: 175,
46047             maxSize: 400,
46048             titlebar: true,
46049             collapsible: true,
46050             animate: true,
46051             margins:{left:0,right:5,bottom:5,top:5},
46052             cmargins:{left:5,right:5,bottom:5,top:5}
46053         }, c.east) : false,
46054         center: Roo.apply({
46055             tabPosition: 'top',
46056             autoScroll:false,
46057             closeOnTab: true,
46058             titlebar:false,
46059             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46060         }, c.center)
46061     });
46062
46063     this.el.addClass('x-reader');
46064
46065     this.beginUpdate();
46066
46067     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46068         south: c.preview !== false ? Roo.apply({
46069             split:true,
46070             initialSize: 200,
46071             minSize: 100,
46072             autoScroll:true,
46073             collapsible:true,
46074             titlebar: true,
46075             cmargins:{top:5,left:0, right:0, bottom:0}
46076         }, c.preview) : false,
46077         center: Roo.apply({
46078             autoScroll:false,
46079             titlebar:false,
46080             minHeight:200
46081         }, c.listView)
46082     });
46083     this.add('center', new Roo.NestedLayoutPanel(inner,
46084             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46085
46086     this.endUpdate();
46087
46088     this.regions.preview = inner.getRegion('south');
46089     this.regions.listView = inner.getRegion('center');
46090 };
46091
46092 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46093  * Based on:
46094  * Ext JS Library 1.1.1
46095  * Copyright(c) 2006-2007, Ext JS, LLC.
46096  *
46097  * Originally Released Under LGPL - original licence link has changed is not relivant.
46098  *
46099  * Fork - LGPL
46100  * <script type="text/javascript">
46101  */
46102  
46103 /**
46104  * @class Roo.grid.Grid
46105  * @extends Roo.util.Observable
46106  * This class represents the primary interface of a component based grid control.
46107  * <br><br>Usage:<pre><code>
46108  var grid = new Roo.grid.Grid("my-container-id", {
46109      ds: myDataStore,
46110      cm: myColModel,
46111      selModel: mySelectionModel,
46112      autoSizeColumns: true,
46113      monitorWindowResize: false,
46114      trackMouseOver: true
46115  });
46116  // set any options
46117  grid.render();
46118  * </code></pre>
46119  * <b>Common Problems:</b><br/>
46120  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46121  * element will correct this<br/>
46122  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46123  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46124  * are unpredictable.<br/>
46125  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46126  * grid to calculate dimensions/offsets.<br/>
46127   * @constructor
46128  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46129  * The container MUST have some type of size defined for the grid to fill. The container will be
46130  * automatically set to position relative if it isn't already.
46131  * @param {Object} config A config object that sets properties on this grid.
46132  */
46133 Roo.grid.Grid = function(container, config){
46134         // initialize the container
46135         this.container = Roo.get(container);
46136         this.container.update("");
46137         this.container.setStyle("overflow", "hidden");
46138     this.container.addClass('x-grid-container');
46139
46140     this.id = this.container.id;
46141
46142     Roo.apply(this, config);
46143     // check and correct shorthanded configs
46144     if(this.ds){
46145         this.dataSource = this.ds;
46146         delete this.ds;
46147     }
46148     if(this.cm){
46149         this.colModel = this.cm;
46150         delete this.cm;
46151     }
46152     if(this.sm){
46153         this.selModel = this.sm;
46154         delete this.sm;
46155     }
46156
46157     if (this.selModel) {
46158         this.selModel = Roo.factory(this.selModel, Roo.grid);
46159         this.sm = this.selModel;
46160         this.sm.xmodule = this.xmodule || false;
46161     }
46162     if (typeof(this.colModel.config) == 'undefined') {
46163         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46164         this.cm = this.colModel;
46165         this.cm.xmodule = this.xmodule || false;
46166     }
46167     if (this.dataSource) {
46168         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46169         this.ds = this.dataSource;
46170         this.ds.xmodule = this.xmodule || false;
46171         
46172     }
46173     
46174     
46175     
46176     if(this.width){
46177         this.container.setWidth(this.width);
46178     }
46179
46180     if(this.height){
46181         this.container.setHeight(this.height);
46182     }
46183     /** @private */
46184         this.addEvents({
46185             // raw events
46186             /**
46187              * @event click
46188              * The raw click event for the entire grid.
46189              * @param {Roo.EventObject} e
46190              */
46191             "click" : true,
46192             /**
46193              * @event dblclick
46194              * The raw dblclick event for the entire grid.
46195              * @param {Roo.EventObject} e
46196              */
46197             "dblclick" : true,
46198             /**
46199              * @event contextmenu
46200              * The raw contextmenu event for the entire grid.
46201              * @param {Roo.EventObject} e
46202              */
46203             "contextmenu" : true,
46204             /**
46205              * @event mousedown
46206              * The raw mousedown event for the entire grid.
46207              * @param {Roo.EventObject} e
46208              */
46209             "mousedown" : true,
46210             /**
46211              * @event mouseup
46212              * The raw mouseup event for the entire grid.
46213              * @param {Roo.EventObject} e
46214              */
46215             "mouseup" : true,
46216             /**
46217              * @event mouseover
46218              * The raw mouseover event for the entire grid.
46219              * @param {Roo.EventObject} e
46220              */
46221             "mouseover" : true,
46222             /**
46223              * @event mouseout
46224              * The raw mouseout event for the entire grid.
46225              * @param {Roo.EventObject} e
46226              */
46227             "mouseout" : true,
46228             /**
46229              * @event keypress
46230              * The raw keypress event for the entire grid.
46231              * @param {Roo.EventObject} e
46232              */
46233             "keypress" : true,
46234             /**
46235              * @event keydown
46236              * The raw keydown event for the entire grid.
46237              * @param {Roo.EventObject} e
46238              */
46239             "keydown" : true,
46240
46241             // custom events
46242
46243             /**
46244              * @event cellclick
46245              * Fires when a cell is clicked
46246              * @param {Grid} this
46247              * @param {Number} rowIndex
46248              * @param {Number} columnIndex
46249              * @param {Roo.EventObject} e
46250              */
46251             "cellclick" : true,
46252             /**
46253              * @event celldblclick
46254              * Fires when a cell is double clicked
46255              * @param {Grid} this
46256              * @param {Number} rowIndex
46257              * @param {Number} columnIndex
46258              * @param {Roo.EventObject} e
46259              */
46260             "celldblclick" : true,
46261             /**
46262              * @event rowclick
46263              * Fires when a row is clicked
46264              * @param {Grid} this
46265              * @param {Number} rowIndex
46266              * @param {Roo.EventObject} e
46267              */
46268             "rowclick" : true,
46269             /**
46270              * @event rowdblclick
46271              * Fires when a row is double clicked
46272              * @param {Grid} this
46273              * @param {Number} rowIndex
46274              * @param {Roo.EventObject} e
46275              */
46276             "rowdblclick" : true,
46277             /**
46278              * @event headerclick
46279              * Fires when a header is clicked
46280              * @param {Grid} this
46281              * @param {Number} columnIndex
46282              * @param {Roo.EventObject} e
46283              */
46284             "headerclick" : true,
46285             /**
46286              * @event headerdblclick
46287              * Fires when a header cell is double clicked
46288              * @param {Grid} this
46289              * @param {Number} columnIndex
46290              * @param {Roo.EventObject} e
46291              */
46292             "headerdblclick" : true,
46293             /**
46294              * @event rowcontextmenu
46295              * Fires when a row is right clicked
46296              * @param {Grid} this
46297              * @param {Number} rowIndex
46298              * @param {Roo.EventObject} e
46299              */
46300             "rowcontextmenu" : true,
46301             /**
46302          * @event cellcontextmenu
46303          * Fires when a cell is right clicked
46304          * @param {Grid} this
46305          * @param {Number} rowIndex
46306          * @param {Number} cellIndex
46307          * @param {Roo.EventObject} e
46308          */
46309          "cellcontextmenu" : true,
46310             /**
46311              * @event headercontextmenu
46312              * Fires when a header is right clicked
46313              * @param {Grid} this
46314              * @param {Number} columnIndex
46315              * @param {Roo.EventObject} e
46316              */
46317             "headercontextmenu" : true,
46318             /**
46319              * @event bodyscroll
46320              * Fires when the body element is scrolled
46321              * @param {Number} scrollLeft
46322              * @param {Number} scrollTop
46323              */
46324             "bodyscroll" : true,
46325             /**
46326              * @event columnresize
46327              * Fires when the user resizes a column
46328              * @param {Number} columnIndex
46329              * @param {Number} newSize
46330              */
46331             "columnresize" : true,
46332             /**
46333              * @event columnmove
46334              * Fires when the user moves a column
46335              * @param {Number} oldIndex
46336              * @param {Number} newIndex
46337              */
46338             "columnmove" : true,
46339             /**
46340              * @event startdrag
46341              * Fires when row(s) start being dragged
46342              * @param {Grid} this
46343              * @param {Roo.GridDD} dd The drag drop object
46344              * @param {event} e The raw browser event
46345              */
46346             "startdrag" : true,
46347             /**
46348              * @event enddrag
46349              * Fires when a drag operation is complete
46350              * @param {Grid} this
46351              * @param {Roo.GridDD} dd The drag drop object
46352              * @param {event} e The raw browser event
46353              */
46354             "enddrag" : true,
46355             /**
46356              * @event dragdrop
46357              * Fires when dragged row(s) are dropped on a valid DD target
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             "dragdrop" : true,
46364             /**
46365              * @event dragover
46366              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
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             "dragover" : true,
46373             /**
46374              * @event dragenter
46375              *  Fires when the dragged row(s) first cross 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             "dragenter" : true,
46382             /**
46383              * @event dragout
46384              * Fires when the dragged row(s) leave another DD target while being dragged
46385              * @param {Grid} this
46386              * @param {Roo.GridDD} dd The drag drop object
46387              * @param {String} targetId The target drag drop object
46388              * @param {event} e The raw browser event
46389              */
46390             "dragout" : true,
46391         /**
46392          * @event render
46393          * Fires when the grid is rendered
46394          * @param {Grid} grid
46395          */
46396         render : true
46397     });
46398
46399     Roo.grid.Grid.superclass.constructor.call(this);
46400 };
46401 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46402     
46403     /**
46404      * @cfg {String} ddGroup - drag drop group.
46405          */
46406     
46407     /**
46408      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46409          */
46410         minColumnWidth : 25,
46411
46412     /**
46413          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46414          * <b>on initial render.</b> It is more efficient to explicitly size the columns
46415          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46416          */
46417         autoSizeColumns : false,
46418
46419         /**
46420          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46421          */
46422         autoSizeHeaders : true,
46423
46424         /**
46425          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46426          */
46427         monitorWindowResize : true,
46428
46429         /**
46430          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46431          * rows measured to get a columns size. Default is 0 (all rows).
46432          */
46433         maxRowsToMeasure : 0,
46434
46435         /**
46436          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46437          */
46438         trackMouseOver : true,
46439
46440     /**
46441          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46442          */
46443     
46444         /**
46445          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46446          */
46447         enableDragDrop : false,
46448
46449         /**
46450          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46451          */
46452         enableColumnMove : true,
46453
46454         /**
46455          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46456          */
46457         enableColumnHide : true,
46458
46459         /**
46460          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46461          */
46462         enableRowHeightSync : false,
46463
46464         /**
46465          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46466          */
46467         stripeRows : true,
46468
46469         /**
46470          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46471          */
46472         autoHeight : false,
46473
46474     /**
46475      * @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.
46476      */
46477     autoExpandColumn : false,
46478
46479     /**
46480     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46481     * Default is 50.
46482     */
46483     autoExpandMin : 50,
46484
46485     /**
46486     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46487     */
46488     autoExpandMax : 1000,
46489
46490     /**
46491          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46492          */
46493         view : null,
46494
46495         /**
46496      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46497          */
46498         loadMask : false,
46499     /**
46500      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46501          */
46502         dropTarget: false,
46503     // private
46504     rendered : false,
46505
46506     /**
46507     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46508     * of a fixed width. Default is false.
46509     */
46510     /**
46511     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46512     */
46513     /**
46514      * Called once after all setup has been completed and the grid is ready to be rendered.
46515      * @return {Roo.grid.Grid} this
46516      */
46517     render : function(){
46518         var c = this.container;
46519         // try to detect autoHeight/width mode
46520         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46521             this.autoHeight = true;
46522         }
46523         var view = this.getView();
46524         view.init(this);
46525
46526         c.on("click", this.onClick, this);
46527         c.on("dblclick", this.onDblClick, this);
46528         c.on("contextmenu", this.onContextMenu, this);
46529         c.on("keydown", this.onKeyDown, this);
46530
46531         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46532
46533         this.getSelectionModel().init(this);
46534
46535         view.render();
46536
46537         if(this.loadMask){
46538             this.loadMask = new Roo.LoadMask(this.container,
46539                     Roo.apply({store:this.dataSource}, this.loadMask));
46540         }
46541         
46542         
46543         if (this.toolbar && this.toolbar.xtype) {
46544             this.toolbar.container = this.getView().getHeaderPanel(true);
46545             this.toolbar = new Ext.Toolbar(this.toolbar);
46546         }
46547         if (this.footer && this.footer.xtype) {
46548             this.footer.dataSource = this.getDataSource();
46549             this.footer.container = this.getView().getFooterPanel(true);
46550             this.footer = Roo.factory(this.footer, Roo);
46551         }
46552         if (this.dropTarget && this.dropTarget.xtype) {
46553             delete this.dropTarget.xtype;
46554             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46555         }
46556         
46557         
46558         this.rendered = true;
46559         this.fireEvent('render', this);
46560         return this;
46561     },
46562
46563         /**
46564          * Reconfigures the grid to use a different Store and Column Model.
46565          * The View will be bound to the new objects and refreshed.
46566          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46567          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46568          */
46569     reconfigure : function(dataSource, colModel){
46570         if(this.loadMask){
46571             this.loadMask.destroy();
46572             this.loadMask = new Roo.LoadMask(this.container,
46573                     Roo.apply({store:dataSource}, this.loadMask));
46574         }
46575         this.view.bind(dataSource, colModel);
46576         this.dataSource = dataSource;
46577         this.colModel = colModel;
46578         this.view.refresh(true);
46579     },
46580
46581     // private
46582     onKeyDown : function(e){
46583         this.fireEvent("keydown", e);
46584     },
46585
46586     /**
46587      * Destroy this grid.
46588      * @param {Boolean} removeEl True to remove the element
46589      */
46590     destroy : function(removeEl, keepListeners){
46591         if(this.loadMask){
46592             this.loadMask.destroy();
46593         }
46594         var c = this.container;
46595         c.removeAllListeners();
46596         this.view.destroy();
46597         this.colModel.purgeListeners();
46598         if(!keepListeners){
46599             this.purgeListeners();
46600         }
46601         c.update("");
46602         if(removeEl === true){
46603             c.remove();
46604         }
46605     },
46606
46607     // private
46608     processEvent : function(name, e){
46609         this.fireEvent(name, e);
46610         var t = e.getTarget();
46611         var v = this.view;
46612         var header = v.findHeaderIndex(t);
46613         if(header !== false){
46614             this.fireEvent("header" + name, this, header, e);
46615         }else{
46616             var row = v.findRowIndex(t);
46617             var cell = v.findCellIndex(t);
46618             if(row !== false){
46619                 this.fireEvent("row" + name, this, row, e);
46620                 if(cell !== false){
46621                     this.fireEvent("cell" + name, this, row, cell, e);
46622                 }
46623             }
46624         }
46625     },
46626
46627     // private
46628     onClick : function(e){
46629         this.processEvent("click", e);
46630     },
46631
46632     // private
46633     onContextMenu : function(e, t){
46634         this.processEvent("contextmenu", e);
46635     },
46636
46637     // private
46638     onDblClick : function(e){
46639         this.processEvent("dblclick", e);
46640     },
46641
46642     // private
46643     walkCells : function(row, col, step, fn, scope){
46644         var cm = this.colModel, clen = cm.getColumnCount();
46645         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46646         if(step < 0){
46647             if(col < 0){
46648                 row--;
46649                 first = false;
46650             }
46651             while(row >= 0){
46652                 if(!first){
46653                     col = clen-1;
46654                 }
46655                 first = false;
46656                 while(col >= 0){
46657                     if(fn.call(scope || this, row, col, cm) === true){
46658                         return [row, col];
46659                     }
46660                     col--;
46661                 }
46662                 row--;
46663             }
46664         } else {
46665             if(col >= clen){
46666                 row++;
46667                 first = false;
46668             }
46669             while(row < rlen){
46670                 if(!first){
46671                     col = 0;
46672                 }
46673                 first = false;
46674                 while(col < clen){
46675                     if(fn.call(scope || this, row, col, cm) === true){
46676                         return [row, col];
46677                     }
46678                     col++;
46679                 }
46680                 row++;
46681             }
46682         }
46683         return null;
46684     },
46685
46686     // private
46687     getSelections : function(){
46688         return this.selModel.getSelections();
46689     },
46690
46691     /**
46692      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46693      * but if manual update is required this method will initiate it.
46694      */
46695     autoSize : function(){
46696         if(this.rendered){
46697             this.view.layout();
46698             if(this.view.adjustForScroll){
46699                 this.view.adjustForScroll();
46700             }
46701         }
46702     },
46703
46704     /**
46705      * Returns the grid's underlying element.
46706      * @return {Element} The element
46707      */
46708     getGridEl : function(){
46709         return this.container;
46710     },
46711
46712     // private for compatibility, overridden by editor grid
46713     stopEditing : function(){},
46714
46715     /**
46716      * Returns the grid's SelectionModel.
46717      * @return {SelectionModel}
46718      */
46719     getSelectionModel : function(){
46720         if(!this.selModel){
46721             this.selModel = new Roo.grid.RowSelectionModel();
46722         }
46723         return this.selModel;
46724     },
46725
46726     /**
46727      * Returns the grid's DataSource.
46728      * @return {DataSource}
46729      */
46730     getDataSource : function(){
46731         return this.dataSource;
46732     },
46733
46734     /**
46735      * Returns the grid's ColumnModel.
46736      * @return {ColumnModel}
46737      */
46738     getColumnModel : function(){
46739         return this.colModel;
46740     },
46741
46742     /**
46743      * Returns the grid's GridView object.
46744      * @return {GridView}
46745      */
46746     getView : function(){
46747         if(!this.view){
46748             this.view = new Roo.grid.GridView(this.viewConfig);
46749         }
46750         return this.view;
46751     },
46752     /**
46753      * Called to get grid's drag proxy text, by default returns this.ddText.
46754      * @return {String}
46755      */
46756     getDragDropText : function(){
46757         var count = this.selModel.getCount();
46758         return String.format(this.ddText, count, count == 1 ? '' : 's');
46759     }
46760 });
46761 /**
46762  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46763  * %0 is replaced with the number of selected rows.
46764  * @type String
46765  */
46766 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46767  * Based on:
46768  * Ext JS Library 1.1.1
46769  * Copyright(c) 2006-2007, Ext JS, LLC.
46770  *
46771  * Originally Released Under LGPL - original licence link has changed is not relivant.
46772  *
46773  * Fork - LGPL
46774  * <script type="text/javascript">
46775  */
46776  
46777 Roo.grid.AbstractGridView = function(){
46778         this.grid = null;
46779         
46780         this.events = {
46781             "beforerowremoved" : true,
46782             "beforerowsinserted" : true,
46783             "beforerefresh" : true,
46784             "rowremoved" : true,
46785             "rowsinserted" : true,
46786             "rowupdated" : true,
46787             "refresh" : true
46788         };
46789     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46790 };
46791
46792 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46793     rowClass : "x-grid-row",
46794     cellClass : "x-grid-cell",
46795     tdClass : "x-grid-td",
46796     hdClass : "x-grid-hd",
46797     splitClass : "x-grid-hd-split",
46798     
46799         init: function(grid){
46800         this.grid = grid;
46801                 var cid = this.grid.getGridEl().id;
46802         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46803         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46804         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46805         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46806         },
46807         
46808         getColumnRenderers : function(){
46809         var renderers = [];
46810         var cm = this.grid.colModel;
46811         var colCount = cm.getColumnCount();
46812         for(var i = 0; i < colCount; i++){
46813             renderers[i] = cm.getRenderer(i);
46814         }
46815         return renderers;
46816     },
46817     
46818     getColumnIds : function(){
46819         var ids = [];
46820         var cm = this.grid.colModel;
46821         var colCount = cm.getColumnCount();
46822         for(var i = 0; i < colCount; i++){
46823             ids[i] = cm.getColumnId(i);
46824         }
46825         return ids;
46826     },
46827     
46828     getDataIndexes : function(){
46829         if(!this.indexMap){
46830             this.indexMap = this.buildIndexMap();
46831         }
46832         return this.indexMap.colToData;
46833     },
46834     
46835     getColumnIndexByDataIndex : function(dataIndex){
46836         if(!this.indexMap){
46837             this.indexMap = this.buildIndexMap();
46838         }
46839         return this.indexMap.dataToCol[dataIndex];
46840     },
46841     
46842     /**
46843      * Set a css style for a column dynamically. 
46844      * @param {Number} colIndex The index of the column
46845      * @param {String} name The css property name
46846      * @param {String} value The css value
46847      */
46848     setCSSStyle : function(colIndex, name, value){
46849         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46850         Roo.util.CSS.updateRule(selector, name, value);
46851     },
46852     
46853     generateRules : function(cm){
46854         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46855         Roo.util.CSS.removeStyleSheet(rulesId);
46856         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46857             var cid = cm.getColumnId(i);
46858             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46859                          this.tdSelector, cid, " {\n}\n",
46860                          this.hdSelector, cid, " {\n}\n",
46861                          this.splitSelector, cid, " {\n}\n");
46862         }
46863         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46864     }
46865 });/*
46866  * Based on:
46867  * Ext JS Library 1.1.1
46868  * Copyright(c) 2006-2007, Ext JS, LLC.
46869  *
46870  * Originally Released Under LGPL - original licence link has changed is not relivant.
46871  *
46872  * Fork - LGPL
46873  * <script type="text/javascript">
46874  */
46875
46876 // private
46877 // This is a support class used internally by the Grid components
46878 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46879     this.grid = grid;
46880     this.view = grid.getView();
46881     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46882     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46883     if(hd2){
46884         this.setHandleElId(Roo.id(hd));
46885         this.setOuterHandleElId(Roo.id(hd2));
46886     }
46887     this.scroll = false;
46888 };
46889 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46890     maxDragWidth: 120,
46891     getDragData : function(e){
46892         var t = Roo.lib.Event.getTarget(e);
46893         var h = this.view.findHeaderCell(t);
46894         if(h){
46895             return {ddel: h.firstChild, header:h};
46896         }
46897         return false;
46898     },
46899
46900     onInitDrag : function(e){
46901         this.view.headersDisabled = true;
46902         var clone = this.dragData.ddel.cloneNode(true);
46903         clone.id = Roo.id();
46904         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46905         this.proxy.update(clone);
46906         return true;
46907     },
46908
46909     afterValidDrop : function(){
46910         var v = this.view;
46911         setTimeout(function(){
46912             v.headersDisabled = false;
46913         }, 50);
46914     },
46915
46916     afterInvalidDrop : function(){
46917         var v = this.view;
46918         setTimeout(function(){
46919             v.headersDisabled = false;
46920         }, 50);
46921     }
46922 });
46923 /*
46924  * Based on:
46925  * Ext JS Library 1.1.1
46926  * Copyright(c) 2006-2007, Ext JS, LLC.
46927  *
46928  * Originally Released Under LGPL - original licence link has changed is not relivant.
46929  *
46930  * Fork - LGPL
46931  * <script type="text/javascript">
46932  */
46933 // private
46934 // This is a support class used internally by the Grid components
46935 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46936     this.grid = grid;
46937     this.view = grid.getView();
46938     // split the proxies so they don't interfere with mouse events
46939     this.proxyTop = Roo.DomHelper.append(document.body, {
46940         cls:"col-move-top", html:"&#160;"
46941     }, true);
46942     this.proxyBottom = Roo.DomHelper.append(document.body, {
46943         cls:"col-move-bottom", html:"&#160;"
46944     }, true);
46945     this.proxyTop.hide = this.proxyBottom.hide = function(){
46946         this.setLeftTop(-100,-100);
46947         this.setStyle("visibility", "hidden");
46948     };
46949     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46950     // temporarily disabled
46951     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46952     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46953 };
46954 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46955     proxyOffsets : [-4, -9],
46956     fly: Roo.Element.fly,
46957
46958     getTargetFromEvent : function(e){
46959         var t = Roo.lib.Event.getTarget(e);
46960         var cindex = this.view.findCellIndex(t);
46961         if(cindex !== false){
46962             return this.view.getHeaderCell(cindex);
46963         }
46964     },
46965
46966     nextVisible : function(h){
46967         var v = this.view, cm = this.grid.colModel;
46968         h = h.nextSibling;
46969         while(h){
46970             if(!cm.isHidden(v.getCellIndex(h))){
46971                 return h;
46972             }
46973             h = h.nextSibling;
46974         }
46975         return null;
46976     },
46977
46978     prevVisible : function(h){
46979         var v = this.view, cm = this.grid.colModel;
46980         h = h.prevSibling;
46981         while(h){
46982             if(!cm.isHidden(v.getCellIndex(h))){
46983                 return h;
46984             }
46985             h = h.prevSibling;
46986         }
46987         return null;
46988     },
46989
46990     positionIndicator : function(h, n, e){
46991         var x = Roo.lib.Event.getPageX(e);
46992         var r = Roo.lib.Dom.getRegion(n.firstChild);
46993         var px, pt, py = r.top + this.proxyOffsets[1];
46994         if((r.right - x) <= (r.right-r.left)/2){
46995             px = r.right+this.view.borderWidth;
46996             pt = "after";
46997         }else{
46998             px = r.left;
46999             pt = "before";
47000         }
47001         var oldIndex = this.view.getCellIndex(h);
47002         var newIndex = this.view.getCellIndex(n);
47003
47004         if(this.grid.colModel.isFixed(newIndex)){
47005             return false;
47006         }
47007
47008         var locked = this.grid.colModel.isLocked(newIndex);
47009
47010         if(pt == "after"){
47011             newIndex++;
47012         }
47013         if(oldIndex < newIndex){
47014             newIndex--;
47015         }
47016         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47017             return false;
47018         }
47019         px +=  this.proxyOffsets[0];
47020         this.proxyTop.setLeftTop(px, py);
47021         this.proxyTop.show();
47022         if(!this.bottomOffset){
47023             this.bottomOffset = this.view.mainHd.getHeight();
47024         }
47025         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47026         this.proxyBottom.show();
47027         return pt;
47028     },
47029
47030     onNodeEnter : function(n, dd, e, data){
47031         if(data.header != n){
47032             this.positionIndicator(data.header, n, e);
47033         }
47034     },
47035
47036     onNodeOver : function(n, dd, e, data){
47037         var result = false;
47038         if(data.header != n){
47039             result = this.positionIndicator(data.header, n, e);
47040         }
47041         if(!result){
47042             this.proxyTop.hide();
47043             this.proxyBottom.hide();
47044         }
47045         return result ? this.dropAllowed : this.dropNotAllowed;
47046     },
47047
47048     onNodeOut : function(n, dd, e, data){
47049         this.proxyTop.hide();
47050         this.proxyBottom.hide();
47051     },
47052
47053     onNodeDrop : function(n, dd, e, data){
47054         var h = data.header;
47055         if(h != n){
47056             var cm = this.grid.colModel;
47057             var x = Roo.lib.Event.getPageX(e);
47058             var r = Roo.lib.Dom.getRegion(n.firstChild);
47059             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47060             var oldIndex = this.view.getCellIndex(h);
47061             var newIndex = this.view.getCellIndex(n);
47062             var locked = cm.isLocked(newIndex);
47063             if(pt == "after"){
47064                 newIndex++;
47065             }
47066             if(oldIndex < newIndex){
47067                 newIndex--;
47068             }
47069             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47070                 return false;
47071             }
47072             cm.setLocked(oldIndex, locked, true);
47073             cm.moveColumn(oldIndex, newIndex);
47074             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47075             return true;
47076         }
47077         return false;
47078     }
47079 });
47080 /*
47081  * Based on:
47082  * Ext JS Library 1.1.1
47083  * Copyright(c) 2006-2007, Ext JS, LLC.
47084  *
47085  * Originally Released Under LGPL - original licence link has changed is not relivant.
47086  *
47087  * Fork - LGPL
47088  * <script type="text/javascript">
47089  */
47090   
47091 /**
47092  * @class Roo.grid.GridView
47093  * @extends Roo.util.Observable
47094  *
47095  * @constructor
47096  * @param {Object} config
47097  */
47098 Roo.grid.GridView = function(config){
47099     Roo.grid.GridView.superclass.constructor.call(this);
47100     this.el = null;
47101
47102     Roo.apply(this, config);
47103 };
47104
47105 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47106
47107     /**
47108      * Override this function to apply custom css classes to rows during rendering
47109      * @param {Record} record The record
47110      * @param {Number} index
47111      * @method getRowClass
47112      */
47113     rowClass : "x-grid-row",
47114
47115     cellClass : "x-grid-col",
47116
47117     tdClass : "x-grid-td",
47118
47119     hdClass : "x-grid-hd",
47120
47121     splitClass : "x-grid-split",
47122
47123     sortClasses : ["sort-asc", "sort-desc"],
47124
47125     enableMoveAnim : false,
47126
47127     hlColor: "C3DAF9",
47128
47129     dh : Roo.DomHelper,
47130
47131     fly : Roo.Element.fly,
47132
47133     css : Roo.util.CSS,
47134
47135     borderWidth: 1,
47136
47137     splitOffset: 3,
47138
47139     scrollIncrement : 22,
47140
47141     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47142
47143     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47144
47145     bind : function(ds, cm){
47146         if(this.ds){
47147             this.ds.un("load", this.onLoad, this);
47148             this.ds.un("datachanged", this.onDataChange, this);
47149             this.ds.un("add", this.onAdd, this);
47150             this.ds.un("remove", this.onRemove, this);
47151             this.ds.un("update", this.onUpdate, this);
47152             this.ds.un("clear", this.onClear, this);
47153         }
47154         if(ds){
47155             ds.on("load", this.onLoad, this);
47156             ds.on("datachanged", this.onDataChange, this);
47157             ds.on("add", this.onAdd, this);
47158             ds.on("remove", this.onRemove, this);
47159             ds.on("update", this.onUpdate, this);
47160             ds.on("clear", this.onClear, this);
47161         }
47162         this.ds = ds;
47163
47164         if(this.cm){
47165             this.cm.un("widthchange", this.onColWidthChange, this);
47166             this.cm.un("headerchange", this.onHeaderChange, this);
47167             this.cm.un("hiddenchange", this.onHiddenChange, this);
47168             this.cm.un("columnmoved", this.onColumnMove, this);
47169             this.cm.un("columnlockchange", this.onColumnLock, this);
47170         }
47171         if(cm){
47172             this.generateRules(cm);
47173             cm.on("widthchange", this.onColWidthChange, this);
47174             cm.on("headerchange", this.onHeaderChange, this);
47175             cm.on("hiddenchange", this.onHiddenChange, this);
47176             cm.on("columnmoved", this.onColumnMove, this);
47177             cm.on("columnlockchange", this.onColumnLock, this);
47178         }
47179         this.cm = cm;
47180     },
47181
47182     init: function(grid){
47183                 Roo.grid.GridView.superclass.init.call(this, grid);
47184
47185                 this.bind(grid.dataSource, grid.colModel);
47186
47187             grid.on("headerclick", this.handleHeaderClick, this);
47188
47189         if(grid.trackMouseOver){
47190             grid.on("mouseover", this.onRowOver, this);
47191                 grid.on("mouseout", this.onRowOut, this);
47192             }
47193             grid.cancelTextSelection = function(){};
47194                 this.gridId = grid.id;
47195
47196                 var tpls = this.templates || {};
47197
47198                 if(!tpls.master){
47199                     tpls.master = new Roo.Template(
47200                        '<div class="x-grid" hidefocus="true">',
47201                           '<div class="x-grid-topbar"></div>',
47202                           '<div class="x-grid-scroller"><div></div></div>',
47203                           '<div class="x-grid-locked">',
47204                               '<div class="x-grid-header">{lockedHeader}</div>',
47205                               '<div class="x-grid-body">{lockedBody}</div>',
47206                           "</div>",
47207                           '<div class="x-grid-viewport">',
47208                               '<div class="x-grid-header">{header}</div>',
47209                               '<div class="x-grid-body">{body}</div>',
47210                           "</div>",
47211                           '<div class="x-grid-bottombar"></div>',
47212                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47213                           '<div class="x-grid-resize-proxy">&#160;</div>',
47214                        "</div>"
47215                     );
47216                     tpls.master.disableformats = true;
47217                 }
47218
47219                 if(!tpls.header){
47220                     tpls.header = new Roo.Template(
47221                        '<table border="0" cellspacing="0" cellpadding="0">',
47222                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47223                        "</table>{splits}"
47224                     );
47225                     tpls.header.disableformats = true;
47226                 }
47227                 tpls.header.compile();
47228
47229                 if(!tpls.hcell){
47230                     tpls.hcell = new Roo.Template(
47231                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47232                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47233                         "</div></td>"
47234                      );
47235                      tpls.hcell.disableFormats = true;
47236                 }
47237                 tpls.hcell.compile();
47238
47239                 if(!tpls.hsplit){
47240                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47241                     tpls.hsplit.disableFormats = true;
47242                 }
47243                 tpls.hsplit.compile();
47244
47245                 if(!tpls.body){
47246                     tpls.body = new Roo.Template(
47247                        '<table border="0" cellspacing="0" cellpadding="0">',
47248                        "<tbody>{rows}</tbody>",
47249                        "</table>"
47250                     );
47251                     tpls.body.disableFormats = true;
47252                 }
47253                 tpls.body.compile();
47254
47255                 if(!tpls.row){
47256                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47257                     tpls.row.disableFormats = true;
47258                 }
47259                 tpls.row.compile();
47260
47261                 if(!tpls.cell){
47262                     tpls.cell = new Roo.Template(
47263                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47264                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47265                         "</td>"
47266                     );
47267             tpls.cell.disableFormats = true;
47268         }
47269                 tpls.cell.compile();
47270
47271                 this.templates = tpls;
47272         },
47273
47274         // remap these for backwards compat
47275     onColWidthChange : function(){
47276         this.updateColumns.apply(this, arguments);
47277     },
47278     onHeaderChange : function(){
47279         this.updateHeaders.apply(this, arguments);
47280     }, 
47281     onHiddenChange : function(){
47282         this.handleHiddenChange.apply(this, arguments);
47283     },
47284     onColumnMove : function(){
47285         this.handleColumnMove.apply(this, arguments);
47286     },
47287     onColumnLock : function(){
47288         this.handleLockChange.apply(this, arguments);
47289     },
47290
47291     onDataChange : function(){
47292         this.refresh();
47293         this.updateHeaderSortState();
47294     },
47295
47296         onClear : function(){
47297         this.refresh();
47298     },
47299
47300         onUpdate : function(ds, record){
47301         this.refreshRow(record);
47302     },
47303
47304     refreshRow : function(record){
47305         var ds = this.ds, index;
47306         if(typeof record == 'number'){
47307             index = record;
47308             record = ds.getAt(index);
47309         }else{
47310             index = ds.indexOf(record);
47311         }
47312         this.insertRows(ds, index, index, true);
47313         this.onRemove(ds, record, index+1, true);
47314         this.syncRowHeights(index, index);
47315         this.layout();
47316         this.fireEvent("rowupdated", this, index, record);
47317     },
47318
47319     onAdd : function(ds, records, index){
47320         this.insertRows(ds, index, index + (records.length-1));
47321     },
47322
47323     onRemove : function(ds, record, index, isUpdate){
47324         if(isUpdate !== true){
47325             this.fireEvent("beforerowremoved", this, index, record);
47326         }
47327         var bt = this.getBodyTable(), lt = this.getLockedTable();
47328         if(bt.rows[index]){
47329             bt.firstChild.removeChild(bt.rows[index]);
47330         }
47331         if(lt.rows[index]){
47332             lt.firstChild.removeChild(lt.rows[index]);
47333         }
47334         if(isUpdate !== true){
47335             this.stripeRows(index);
47336             this.syncRowHeights(index, index);
47337             this.layout();
47338             this.fireEvent("rowremoved", this, index, record);
47339         }
47340     },
47341
47342     onLoad : function(){
47343         this.scrollToTop();
47344     },
47345
47346     /**
47347      * Scrolls the grid to the top
47348      */
47349     scrollToTop : function(){
47350         if(this.scroller){
47351             this.scroller.dom.scrollTop = 0;
47352             this.syncScroll();
47353         }
47354     },
47355
47356     /**
47357      * Gets a panel in the header of the grid that can be used for toolbars etc.
47358      * After modifying the contents of this panel a call to grid.autoSize() may be
47359      * required to register any changes in size.
47360      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47361      * @return Roo.Element
47362      */
47363     getHeaderPanel : function(doShow){
47364         if(doShow){
47365             this.headerPanel.show();
47366         }
47367         return this.headerPanel;
47368         },
47369
47370         /**
47371      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47372      * After modifying the contents of this panel a call to grid.autoSize() may be
47373      * required to register any changes in size.
47374      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47375      * @return Roo.Element
47376      */
47377     getFooterPanel : function(doShow){
47378         if(doShow){
47379             this.footerPanel.show();
47380         }
47381         return this.footerPanel;
47382         },
47383
47384         initElements : function(){
47385             var E = Roo.Element;
47386             var el = this.grid.getGridEl().dom.firstChild;
47387             var cs = el.childNodes;
47388
47389             this.el = new E(el);
47390             this.headerPanel = new E(el.firstChild);
47391             this.headerPanel.enableDisplayMode("block");
47392
47393         this.scroller = new E(cs[1]);
47394             this.scrollSizer = new E(this.scroller.dom.firstChild);
47395
47396             this.lockedWrap = new E(cs[2]);
47397             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47398             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47399
47400             this.mainWrap = new E(cs[3]);
47401             this.mainHd = new E(this.mainWrap.dom.firstChild);
47402             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47403
47404             this.footerPanel = new E(cs[4]);
47405             this.footerPanel.enableDisplayMode("block");
47406
47407         this.focusEl = new E(cs[5]);
47408         this.focusEl.swallowEvent("click", true);
47409         this.resizeProxy = new E(cs[6]);
47410
47411             this.headerSelector = String.format(
47412                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47413                this.lockedHd.id, this.mainHd.id
47414             );
47415
47416             this.splitterSelector = String.format(
47417                '#{0} div.x-grid-split, #{1} div.x-grid-split',
47418                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47419             );
47420     },
47421     idToCssName : function(s)
47422     {
47423         return s.replace(/[^a-z0-9]+/ig, '-');
47424     },
47425
47426         getHeaderCell : function(index){
47427             return Roo.DomQuery.select(this.headerSelector)[index];
47428         },
47429
47430         getHeaderCellMeasure : function(index){
47431             return this.getHeaderCell(index).firstChild;
47432         },
47433
47434         getHeaderCellText : function(index){
47435             return this.getHeaderCell(index).firstChild.firstChild;
47436         },
47437
47438         getLockedTable : function(){
47439             return this.lockedBody.dom.firstChild;
47440         },
47441
47442         getBodyTable : function(){
47443             return this.mainBody.dom.firstChild;
47444         },
47445
47446         getLockedRow : function(index){
47447             return this.getLockedTable().rows[index];
47448         },
47449
47450         getRow : function(index){
47451             return this.getBodyTable().rows[index];
47452         },
47453
47454         getRowComposite : function(index){
47455             if(!this.rowEl){
47456                 this.rowEl = new Roo.CompositeElementLite();
47457             }
47458         var els = [], lrow, mrow;
47459         if(lrow = this.getLockedRow(index)){
47460             els.push(lrow);
47461         }
47462         if(mrow = this.getRow(index)){
47463             els.push(mrow);
47464         }
47465         this.rowEl.elements = els;
47466             return this.rowEl;
47467         },
47468
47469         getCell : function(rowIndex, colIndex){
47470             var locked = this.cm.getLockedCount();
47471             var source;
47472             if(colIndex < locked){
47473                 source = this.lockedBody.dom.firstChild;
47474             }else{
47475                 source = this.mainBody.dom.firstChild;
47476                 colIndex -= locked;
47477             }
47478         return source.rows[rowIndex].childNodes[colIndex];
47479         },
47480
47481         getCellText : function(rowIndex, colIndex){
47482             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47483         },
47484
47485         getCellBox : function(cell){
47486             var b = this.fly(cell).getBox();
47487         if(Roo.isOpera){ // opera fails to report the Y
47488             b.y = cell.offsetTop + this.mainBody.getY();
47489         }
47490         return b;
47491     },
47492
47493     getCellIndex : function(cell){
47494         var id = String(cell.className).match(this.cellRE);
47495         if(id){
47496             return parseInt(id[1], 10);
47497         }
47498         return 0;
47499     },
47500
47501     findHeaderIndex : function(n){
47502         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47503         return r ? this.getCellIndex(r) : false;
47504     },
47505
47506     findHeaderCell : function(n){
47507         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47508         return r ? r : false;
47509     },
47510
47511     findRowIndex : function(n){
47512         if(!n){
47513             return false;
47514         }
47515         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47516         return r ? r.rowIndex : false;
47517     },
47518
47519     findCellIndex : function(node){
47520         var stop = this.el.dom;
47521         while(node && node != stop){
47522             if(this.findRE.test(node.className)){
47523                 return this.getCellIndex(node);
47524             }
47525             node = node.parentNode;
47526         }
47527         return false;
47528     },
47529
47530     getColumnId : function(index){
47531             return this.cm.getColumnId(index);
47532         },
47533
47534         getSplitters : function(){
47535             if(this.splitterSelector){
47536                return Roo.DomQuery.select(this.splitterSelector);
47537             }else{
47538                 return null;
47539             }
47540         },
47541
47542         getSplitter : function(index){
47543             return this.getSplitters()[index];
47544         },
47545
47546     onRowOver : function(e, t){
47547         var row;
47548         if((row = this.findRowIndex(t)) !== false){
47549             this.getRowComposite(row).addClass("x-grid-row-over");
47550         }
47551     },
47552
47553     onRowOut : function(e, t){
47554         var row;
47555         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47556             this.getRowComposite(row).removeClass("x-grid-row-over");
47557         }
47558     },
47559
47560     renderHeaders : function(){
47561             var cm = this.cm;
47562         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47563         var cb = [], lb = [], sb = [], lsb = [], p = {};
47564         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47565             p.cellId = "x-grid-hd-0-" + i;
47566             p.splitId = "x-grid-csplit-0-" + i;
47567             p.id = cm.getColumnId(i);
47568             p.title = cm.getColumnTooltip(i) || "";
47569             p.value = cm.getColumnHeader(i) || "";
47570             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47571             if(!cm.isLocked(i)){
47572                 cb[cb.length] = ct.apply(p);
47573                 sb[sb.length] = st.apply(p);
47574             }else{
47575                 lb[lb.length] = ct.apply(p);
47576                 lsb[lsb.length] = st.apply(p);
47577             }
47578         }
47579         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47580                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47581         },
47582
47583         updateHeaders : function(){
47584         var html = this.renderHeaders();
47585         this.lockedHd.update(html[0]);
47586         this.mainHd.update(html[1]);
47587     },
47588
47589     /**
47590      * Focuses the specified row.
47591      * @param {Number} row The row index
47592      */
47593     focusRow : function(row){
47594         var x = this.scroller.dom.scrollLeft;
47595         this.focusCell(row, 0, false);
47596         this.scroller.dom.scrollLeft = x;
47597     },
47598
47599     /**
47600      * Focuses the specified cell.
47601      * @param {Number} row The row index
47602      * @param {Number} col The column index
47603      * @param {Boolean} hscroll false to disable horizontal scrolling
47604      */
47605     focusCell : function(row, col, hscroll){
47606         var el = this.ensureVisible(row, col, hscroll);
47607         this.focusEl.alignTo(el, "tl-tl");
47608         if(Roo.isGecko){
47609             this.focusEl.focus();
47610         }else{
47611             this.focusEl.focus.defer(1, this.focusEl);
47612         }
47613     },
47614
47615     /**
47616      * Scrolls the specified cell into view
47617      * @param {Number} row The row index
47618      * @param {Number} col The column index
47619      * @param {Boolean} hscroll false to disable horizontal scrolling
47620      */
47621     ensureVisible : function(row, col, hscroll){
47622         if(typeof row != "number"){
47623             row = row.rowIndex;
47624         }
47625         if(row < 0 && row >= this.ds.getCount()){
47626             return;
47627         }
47628         col = (col !== undefined ? col : 0);
47629         var cm = this.grid.colModel;
47630         while(cm.isHidden(col)){
47631             col++;
47632         }
47633
47634         var el = this.getCell(row, col);
47635         if(!el){
47636             return;
47637         }
47638         var c = this.scroller.dom;
47639
47640         var ctop = parseInt(el.offsetTop, 10);
47641         var cleft = parseInt(el.offsetLeft, 10);
47642         var cbot = ctop + el.offsetHeight;
47643         var cright = cleft + el.offsetWidth;
47644
47645         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47646         var stop = parseInt(c.scrollTop, 10);
47647         var sleft = parseInt(c.scrollLeft, 10);
47648         var sbot = stop + ch;
47649         var sright = sleft + c.clientWidth;
47650
47651         if(ctop < stop){
47652                 c.scrollTop = ctop;
47653         }else if(cbot > sbot){
47654             c.scrollTop = cbot-ch;
47655         }
47656
47657         if(hscroll !== false){
47658             if(cleft < sleft){
47659                 c.scrollLeft = cleft;
47660             }else if(cright > sright){
47661                 c.scrollLeft = cright-c.clientWidth;
47662             }
47663         }
47664         return el;
47665     },
47666
47667     updateColumns : function(){
47668         this.grid.stopEditing();
47669         var cm = this.grid.colModel, colIds = this.getColumnIds();
47670         //var totalWidth = cm.getTotalWidth();
47671         var pos = 0;
47672         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47673             //if(cm.isHidden(i)) continue;
47674             var w = cm.getColumnWidth(i);
47675             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47676             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47677         }
47678         this.updateSplitters();
47679     },
47680
47681     generateRules : function(cm){
47682         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47683         Roo.util.CSS.removeStyleSheet(rulesId);
47684         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47685             var cid = cm.getColumnId(i);
47686             var align = '';
47687             if(cm.config[i].align){
47688                 align = 'text-align:'+cm.config[i].align+';';
47689             }
47690             var hidden = '';
47691             if(cm.isHidden(i)){
47692                 hidden = 'display:none;';
47693             }
47694             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47695             ruleBuf.push(
47696                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47697                     this.hdSelector, cid, " {\n", align, width, "}\n",
47698                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47699                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47700         }
47701         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47702     },
47703
47704     updateSplitters : function(){
47705         var cm = this.cm, s = this.getSplitters();
47706         if(s){ // splitters not created yet
47707             var pos = 0, locked = true;
47708             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47709                 if(cm.isHidden(i)) continue;
47710                 var w = cm.getColumnWidth(i);
47711                 if(!cm.isLocked(i) && locked){
47712                     pos = 0;
47713                     locked = false;
47714                 }
47715                 pos += w;
47716                 s[i].style.left = (pos-this.splitOffset) + "px";
47717             }
47718         }
47719     },
47720
47721     handleHiddenChange : function(colModel, colIndex, hidden){
47722         if(hidden){
47723             this.hideColumn(colIndex);
47724         }else{
47725             this.unhideColumn(colIndex);
47726         }
47727     },
47728
47729     hideColumn : function(colIndex){
47730         var cid = this.getColumnId(colIndex);
47731         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47732         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47733         if(Roo.isSafari){
47734             this.updateHeaders();
47735         }
47736         this.updateSplitters();
47737         this.layout();
47738     },
47739
47740     unhideColumn : function(colIndex){
47741         var cid = this.getColumnId(colIndex);
47742         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47743         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47744
47745         if(Roo.isSafari){
47746             this.updateHeaders();
47747         }
47748         this.updateSplitters();
47749         this.layout();
47750     },
47751
47752     insertRows : function(dm, firstRow, lastRow, isUpdate){
47753         if(firstRow == 0 && lastRow == dm.getCount()-1){
47754             this.refresh();
47755         }else{
47756             if(!isUpdate){
47757                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47758             }
47759             var s = this.getScrollState();
47760             var markup = this.renderRows(firstRow, lastRow);
47761             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47762             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47763             this.restoreScroll(s);
47764             if(!isUpdate){
47765                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47766                 this.syncRowHeights(firstRow, lastRow);
47767                 this.stripeRows(firstRow);
47768                 this.layout();
47769             }
47770         }
47771     },
47772
47773     bufferRows : function(markup, target, index){
47774         var before = null, trows = target.rows, tbody = target.tBodies[0];
47775         if(index < trows.length){
47776             before = trows[index];
47777         }
47778         var b = document.createElement("div");
47779         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47780         var rows = b.firstChild.rows;
47781         for(var i = 0, len = rows.length; i < len; i++){
47782             if(before){
47783                 tbody.insertBefore(rows[0], before);
47784             }else{
47785                 tbody.appendChild(rows[0]);
47786             }
47787         }
47788         b.innerHTML = "";
47789         b = null;
47790     },
47791
47792     deleteRows : function(dm, firstRow, lastRow){
47793         if(dm.getRowCount()<1){
47794             this.fireEvent("beforerefresh", this);
47795             this.mainBody.update("");
47796             this.lockedBody.update("");
47797             this.fireEvent("refresh", this);
47798         }else{
47799             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47800             var bt = this.getBodyTable();
47801             var tbody = bt.firstChild;
47802             var rows = bt.rows;
47803             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47804                 tbody.removeChild(rows[firstRow]);
47805             }
47806             this.stripeRows(firstRow);
47807             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47808         }
47809     },
47810
47811     updateRows : function(dataSource, firstRow, lastRow){
47812         var s = this.getScrollState();
47813         this.refresh();
47814         this.restoreScroll(s);
47815     },
47816
47817     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47818         if(!noRefresh){
47819            this.refresh();
47820         }
47821         this.updateHeaderSortState();
47822     },
47823
47824     getScrollState : function(){
47825         var sb = this.scroller.dom;
47826         return {left: sb.scrollLeft, top: sb.scrollTop};
47827     },
47828
47829     stripeRows : function(startRow){
47830         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47831             return;
47832         }
47833         startRow = startRow || 0;
47834         var rows = this.getBodyTable().rows;
47835         var lrows = this.getLockedTable().rows;
47836         var cls = ' x-grid-row-alt ';
47837         for(var i = startRow, len = rows.length; i < len; i++){
47838             var row = rows[i], lrow = lrows[i];
47839             var isAlt = ((i+1) % 2 == 0);
47840             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47841             if(isAlt == hasAlt){
47842                 continue;
47843             }
47844             if(isAlt){
47845                 row.className += " x-grid-row-alt";
47846             }else{
47847                 row.className = row.className.replace("x-grid-row-alt", "");
47848             }
47849             if(lrow){
47850                 lrow.className = row.className;
47851             }
47852         }
47853     },
47854
47855     restoreScroll : function(state){
47856         var sb = this.scroller.dom;
47857         sb.scrollLeft = state.left;
47858         sb.scrollTop = state.top;
47859         this.syncScroll();
47860     },
47861
47862     syncScroll : function(){
47863         var sb = this.scroller.dom;
47864         var sh = this.mainHd.dom;
47865         var bs = this.mainBody.dom;
47866         var lv = this.lockedBody.dom;
47867         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47868         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47869     },
47870
47871     handleScroll : function(e){
47872         this.syncScroll();
47873         var sb = this.scroller.dom;
47874         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47875         e.stopEvent();
47876     },
47877
47878     handleWheel : function(e){
47879         var d = e.getWheelDelta();
47880         this.scroller.dom.scrollTop -= d*22;
47881         // set this here to prevent jumpy scrolling on large tables
47882         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47883         e.stopEvent();
47884     },
47885
47886     renderRows : function(startRow, endRow){
47887         // pull in all the crap needed to render rows
47888         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47889         var colCount = cm.getColumnCount();
47890
47891         if(ds.getCount() < 1){
47892             return ["", ""];
47893         }
47894
47895         // build a map for all the columns
47896         var cs = [];
47897         for(var i = 0; i < colCount; i++){
47898             var name = cm.getDataIndex(i);
47899             cs[i] = {
47900                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47901                 renderer : cm.getRenderer(i),
47902                 id : cm.getColumnId(i),
47903                 locked : cm.isLocked(i)
47904             };
47905         }
47906
47907         startRow = startRow || 0;
47908         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47909
47910         // records to render
47911         var rs = ds.getRange(startRow, endRow);
47912
47913         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47914     },
47915
47916     // As much as I hate to duplicate code, this was branched because FireFox really hates
47917     // [].join("") on strings. The performance difference was substantial enough to
47918     // branch this function
47919     doRender : Roo.isGecko ?
47920             function(cs, rs, ds, startRow, colCount, stripe){
47921                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47922                 // buffers
47923                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47924                 for(var j = 0, len = rs.length; j < len; j++){
47925                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47926                     for(var i = 0; i < colCount; i++){
47927                         c = cs[i];
47928                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47929                         p.id = c.id;
47930                         p.css = p.attr = "";
47931                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47932                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47933                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47934                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47935                         }
47936                         var markup = ct.apply(p);
47937                         if(!c.locked){
47938                             cb+= markup;
47939                         }else{
47940                             lcb+= markup;
47941                         }
47942                     }
47943                     var alt = [];
47944                     if(stripe && ((rowIndex+1) % 2 == 0)){
47945                         alt[0] = "x-grid-row-alt";
47946                     }
47947                     if(r.dirty){
47948                         alt[1] = " x-grid-dirty-row";
47949                     }
47950                     rp.cells = lcb;
47951                     if(this.getRowClass){
47952                         alt[2] = this.getRowClass(r, rowIndex);
47953                     }
47954                     rp.alt = alt.join(" ");
47955                     lbuf+= rt.apply(rp);
47956                     rp.cells = cb;
47957                     buf+=  rt.apply(rp);
47958                 }
47959                 return [lbuf, buf];
47960             } :
47961             function(cs, rs, ds, startRow, colCount, stripe){
47962                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47963                 // buffers
47964                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47965                 for(var j = 0, len = rs.length; j < len; j++){
47966                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47967                     for(var i = 0; i < colCount; i++){
47968                         c = cs[i];
47969                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47970                         p.id = c.id;
47971                         p.css = p.attr = "";
47972                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47973                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47974                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47975                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47976                         }
47977                         var markup = ct.apply(p);
47978                         if(!c.locked){
47979                             cb[cb.length] = markup;
47980                         }else{
47981                             lcb[lcb.length] = markup;
47982                         }
47983                     }
47984                     var alt = [];
47985                     if(stripe && ((rowIndex+1) % 2 == 0)){
47986                         alt[0] = "x-grid-row-alt";
47987                     }
47988                     if(r.dirty){
47989                         alt[1] = " x-grid-dirty-row";
47990                     }
47991                     rp.cells = lcb;
47992                     if(this.getRowClass){
47993                         alt[2] = this.getRowClass(r, rowIndex);
47994                     }
47995                     rp.alt = alt.join(" ");
47996                     rp.cells = lcb.join("");
47997                     lbuf[lbuf.length] = rt.apply(rp);
47998                     rp.cells = cb.join("");
47999                     buf[buf.length] =  rt.apply(rp);
48000                 }
48001                 return [lbuf.join(""), buf.join("")];
48002             },
48003
48004     renderBody : function(){
48005         var markup = this.renderRows();
48006         var bt = this.templates.body;
48007         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48008     },
48009
48010     /**
48011      * Refreshes the grid
48012      * @param {Boolean} headersToo
48013      */
48014     refresh : function(headersToo){
48015         this.fireEvent("beforerefresh", this);
48016         this.grid.stopEditing();
48017         var result = this.renderBody();
48018         this.lockedBody.update(result[0]);
48019         this.mainBody.update(result[1]);
48020         if(headersToo === true){
48021             this.updateHeaders();
48022             this.updateColumns();
48023             this.updateSplitters();
48024             this.updateHeaderSortState();
48025         }
48026         this.syncRowHeights();
48027         this.layout();
48028         this.fireEvent("refresh", this);
48029     },
48030
48031     handleColumnMove : function(cm, oldIndex, newIndex){
48032         this.indexMap = null;
48033         var s = this.getScrollState();
48034         this.refresh(true);
48035         this.restoreScroll(s);
48036         this.afterMove(newIndex);
48037     },
48038
48039     afterMove : function(colIndex){
48040         if(this.enableMoveAnim && Roo.enableFx){
48041             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48042         }
48043     },
48044
48045     updateCell : function(dm, rowIndex, dataIndex){
48046         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48047         if(typeof colIndex == "undefined"){ // not present in grid
48048             return;
48049         }
48050         var cm = this.grid.colModel;
48051         var cell = this.getCell(rowIndex, colIndex);
48052         var cellText = this.getCellText(rowIndex, colIndex);
48053
48054         var p = {
48055             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48056             id : cm.getColumnId(colIndex),
48057             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48058         };
48059         var renderer = cm.getRenderer(colIndex);
48060         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48061         if(typeof val == "undefined" || val === "") val = "&#160;";
48062         cellText.innerHTML = val;
48063         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48064         this.syncRowHeights(rowIndex, rowIndex);
48065     },
48066
48067     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48068         var maxWidth = 0;
48069         if(this.grid.autoSizeHeaders){
48070             var h = this.getHeaderCellMeasure(colIndex);
48071             maxWidth = Math.max(maxWidth, h.scrollWidth);
48072         }
48073         var tb, index;
48074         if(this.cm.isLocked(colIndex)){
48075             tb = this.getLockedTable();
48076             index = colIndex;
48077         }else{
48078             tb = this.getBodyTable();
48079             index = colIndex - this.cm.getLockedCount();
48080         }
48081         if(tb && tb.rows){
48082             var rows = tb.rows;
48083             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48084             for(var i = 0; i < stopIndex; i++){
48085                 var cell = rows[i].childNodes[index].firstChild;
48086                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48087             }
48088         }
48089         return maxWidth + /*margin for error in IE*/ 5;
48090     },
48091     /**
48092      * Autofit a column to its content.
48093      * @param {Number} colIndex
48094      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48095      */
48096      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48097          if(this.cm.isHidden(colIndex)){
48098              return; // can't calc a hidden column
48099          }
48100         if(forceMinSize){
48101             var cid = this.cm.getColumnId(colIndex);
48102             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48103            if(this.grid.autoSizeHeaders){
48104                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48105            }
48106         }
48107         var newWidth = this.calcColumnWidth(colIndex);
48108         this.cm.setColumnWidth(colIndex,
48109             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48110         if(!suppressEvent){
48111             this.grid.fireEvent("columnresize", colIndex, newWidth);
48112         }
48113     },
48114
48115     /**
48116      * Autofits all columns to their content and then expands to fit any extra space in the grid
48117      */
48118      autoSizeColumns : function(){
48119         var cm = this.grid.colModel;
48120         var colCount = cm.getColumnCount();
48121         for(var i = 0; i < colCount; i++){
48122             this.autoSizeColumn(i, true, true);
48123         }
48124         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48125             this.fitColumns();
48126         }else{
48127             this.updateColumns();
48128             this.layout();
48129         }
48130     },
48131
48132     /**
48133      * Autofits all columns to the grid's width proportionate with their current size
48134      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48135      */
48136     fitColumns : function(reserveScrollSpace){
48137         var cm = this.grid.colModel;
48138         var colCount = cm.getColumnCount();
48139         var cols = [];
48140         var width = 0;
48141         var i, w;
48142         for (i = 0; i < colCount; i++){
48143             if(!cm.isHidden(i) && !cm.isFixed(i)){
48144                 w = cm.getColumnWidth(i);
48145                 cols.push(i);
48146                 cols.push(w);
48147                 width += w;
48148             }
48149         }
48150         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48151         if(reserveScrollSpace){
48152             avail -= 17;
48153         }
48154         var frac = (avail - cm.getTotalWidth())/width;
48155         while (cols.length){
48156             w = cols.pop();
48157             i = cols.pop();
48158             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48159         }
48160         this.updateColumns();
48161         this.layout();
48162     },
48163
48164     onRowSelect : function(rowIndex){
48165         var row = this.getRowComposite(rowIndex);
48166         row.addClass("x-grid-row-selected");
48167     },
48168
48169     onRowDeselect : function(rowIndex){
48170         var row = this.getRowComposite(rowIndex);
48171         row.removeClass("x-grid-row-selected");
48172     },
48173
48174     onCellSelect : function(row, col){
48175         var cell = this.getCell(row, col);
48176         if(cell){
48177             Roo.fly(cell).addClass("x-grid-cell-selected");
48178         }
48179     },
48180
48181     onCellDeselect : function(row, col){
48182         var cell = this.getCell(row, col);
48183         if(cell){
48184             Roo.fly(cell).removeClass("x-grid-cell-selected");
48185         }
48186     },
48187
48188     updateHeaderSortState : function(){
48189         var state = this.ds.getSortState();
48190         if(!state){
48191             return;
48192         }
48193         this.sortState = state;
48194         var sortColumn = this.cm.findColumnIndex(state.field);
48195         if(sortColumn != -1){
48196             var sortDir = state.direction;
48197             var sc = this.sortClasses;
48198             var hds = this.el.select(this.headerSelector).removeClass(sc);
48199             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48200         }
48201     },
48202
48203     handleHeaderClick : function(g, index){
48204         if(this.headersDisabled){
48205             return;
48206         }
48207         var dm = g.dataSource, cm = g.colModel;
48208             if(!cm.isSortable(index)){
48209             return;
48210         }
48211             g.stopEditing();
48212         dm.sort(cm.getDataIndex(index));
48213     },
48214
48215
48216     destroy : function(){
48217         if(this.colMenu){
48218             this.colMenu.removeAll();
48219             Roo.menu.MenuMgr.unregister(this.colMenu);
48220             this.colMenu.getEl().remove();
48221             delete this.colMenu;
48222         }
48223         if(this.hmenu){
48224             this.hmenu.removeAll();
48225             Roo.menu.MenuMgr.unregister(this.hmenu);
48226             this.hmenu.getEl().remove();
48227             delete this.hmenu;
48228         }
48229         if(this.grid.enableColumnMove){
48230             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48231             if(dds){
48232                 for(var dd in dds){
48233                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48234                         var elid = dds[dd].dragElId;
48235                         dds[dd].unreg();
48236                         Roo.get(elid).remove();
48237                     } else if(dds[dd].config.isTarget){
48238                         dds[dd].proxyTop.remove();
48239                         dds[dd].proxyBottom.remove();
48240                         dds[dd].unreg();
48241                     }
48242                     if(Roo.dd.DDM.locationCache[dd]){
48243                         delete Roo.dd.DDM.locationCache[dd];
48244                     }
48245                 }
48246                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48247             }
48248         }
48249         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48250         this.bind(null, null);
48251         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48252     },
48253
48254     handleLockChange : function(){
48255         this.refresh(true);
48256     },
48257
48258     onDenyColumnLock : function(){
48259
48260     },
48261
48262     onDenyColumnHide : function(){
48263
48264     },
48265
48266     handleHdMenuClick : function(item){
48267         var index = this.hdCtxIndex;
48268         var cm = this.cm, ds = this.ds;
48269         switch(item.id){
48270             case "asc":
48271                 ds.sort(cm.getDataIndex(index), "ASC");
48272                 break;
48273             case "desc":
48274                 ds.sort(cm.getDataIndex(index), "DESC");
48275                 break;
48276             case "lock":
48277                 var lc = cm.getLockedCount();
48278                 if(cm.getColumnCount(true) <= lc+1){
48279                     this.onDenyColumnLock();
48280                     return;
48281                 }
48282                 if(lc != index){
48283                     cm.setLocked(index, true, true);
48284                     cm.moveColumn(index, lc);
48285                     this.grid.fireEvent("columnmove", index, lc);
48286                 }else{
48287                     cm.setLocked(index, true);
48288                 }
48289             break;
48290             case "unlock":
48291                 var lc = cm.getLockedCount();
48292                 if((lc-1) != index){
48293                     cm.setLocked(index, false, true);
48294                     cm.moveColumn(index, lc-1);
48295                     this.grid.fireEvent("columnmove", index, lc-1);
48296                 }else{
48297                     cm.setLocked(index, false);
48298                 }
48299             break;
48300             default:
48301                 index = cm.getIndexById(item.id.substr(4));
48302                 if(index != -1){
48303                     if(item.checked && cm.getColumnCount(true) <= 1){
48304                         this.onDenyColumnHide();
48305                         return false;
48306                     }
48307                     cm.setHidden(index, item.checked);
48308                 }
48309         }
48310         return true;
48311     },
48312
48313     beforeColMenuShow : function(){
48314         var cm = this.cm,  colCount = cm.getColumnCount();
48315         this.colMenu.removeAll();
48316         for(var i = 0; i < colCount; i++){
48317             this.colMenu.add(new Roo.menu.CheckItem({
48318                 id: "col-"+cm.getColumnId(i),
48319                 text: cm.getColumnHeader(i),
48320                 checked: !cm.isHidden(i),
48321                 hideOnClick:false
48322             }));
48323         }
48324     },
48325
48326     handleHdCtx : function(g, index, e){
48327         e.stopEvent();
48328         var hd = this.getHeaderCell(index);
48329         this.hdCtxIndex = index;
48330         var ms = this.hmenu.items, cm = this.cm;
48331         ms.get("asc").setDisabled(!cm.isSortable(index));
48332         ms.get("desc").setDisabled(!cm.isSortable(index));
48333         if(this.grid.enableColLock !== false){
48334             ms.get("lock").setDisabled(cm.isLocked(index));
48335             ms.get("unlock").setDisabled(!cm.isLocked(index));
48336         }
48337         this.hmenu.show(hd, "tl-bl");
48338     },
48339
48340     handleHdOver : function(e){
48341         var hd = this.findHeaderCell(e.getTarget());
48342         if(hd && !this.headersDisabled){
48343             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48344                this.fly(hd).addClass("x-grid-hd-over");
48345             }
48346         }
48347     },
48348
48349     handleHdOut : function(e){
48350         var hd = this.findHeaderCell(e.getTarget());
48351         if(hd){
48352             this.fly(hd).removeClass("x-grid-hd-over");
48353         }
48354     },
48355
48356     handleSplitDblClick : function(e, t){
48357         var i = this.getCellIndex(t);
48358         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48359             this.autoSizeColumn(i, true);
48360             this.layout();
48361         }
48362     },
48363
48364     render : function(){
48365
48366         var cm = this.cm;
48367         var colCount = cm.getColumnCount();
48368
48369         if(this.grid.monitorWindowResize === true){
48370             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48371         }
48372         var header = this.renderHeaders();
48373         var body = this.templates.body.apply({rows:""});
48374         var html = this.templates.master.apply({
48375             lockedBody: body,
48376             body: body,
48377             lockedHeader: header[0],
48378             header: header[1]
48379         });
48380
48381         //this.updateColumns();
48382
48383         this.grid.getGridEl().dom.innerHTML = html;
48384
48385         this.initElements();
48386         
48387         // a kludge to fix the random scolling effect in webkit
48388         this.el.on("scroll", function() {
48389             this.el.dom.scrollTop=0; // hopefully not recursive..
48390         },this);
48391
48392         this.scroller.on("scroll", this.handleScroll, this);
48393         this.lockedBody.on("mousewheel", this.handleWheel, this);
48394         this.mainBody.on("mousewheel", this.handleWheel, this);
48395
48396         this.mainHd.on("mouseover", this.handleHdOver, this);
48397         this.mainHd.on("mouseout", this.handleHdOut, this);
48398         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48399                 {delegate: "."+this.splitClass});
48400
48401         this.lockedHd.on("mouseover", this.handleHdOver, this);
48402         this.lockedHd.on("mouseout", this.handleHdOut, this);
48403         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48404                 {delegate: "."+this.splitClass});
48405
48406         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48407             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48408         }
48409
48410         this.updateSplitters();
48411
48412         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48413             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48414             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48415         }
48416
48417         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48418             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48419             this.hmenu.add(
48420                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48421                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48422             );
48423             if(this.grid.enableColLock !== false){
48424                 this.hmenu.add('-',
48425                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48426                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48427                 );
48428             }
48429             if(this.grid.enableColumnHide !== false){
48430
48431                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48432                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48433                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48434
48435                 this.hmenu.add('-',
48436                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48437                 );
48438             }
48439             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48440
48441             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48442         }
48443
48444         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48445             this.dd = new Roo.grid.GridDragZone(this.grid, {
48446                 ddGroup : this.grid.ddGroup || 'GridDD'
48447             });
48448         }
48449
48450         /*
48451         for(var i = 0; i < colCount; i++){
48452             if(cm.isHidden(i)){
48453                 this.hideColumn(i);
48454             }
48455             if(cm.config[i].align){
48456                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48457                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48458             }
48459         }*/
48460         
48461         this.updateHeaderSortState();
48462
48463         this.beforeInitialResize();
48464         this.layout(true);
48465
48466         // two part rendering gives faster view to the user
48467         this.renderPhase2.defer(1, this);
48468     },
48469
48470     renderPhase2 : function(){
48471         // render the rows now
48472         this.refresh();
48473         if(this.grid.autoSizeColumns){
48474             this.autoSizeColumns();
48475         }
48476     },
48477
48478     beforeInitialResize : function(){
48479
48480     },
48481
48482     onColumnSplitterMoved : function(i, w){
48483         this.userResized = true;
48484         var cm = this.grid.colModel;
48485         cm.setColumnWidth(i, w, true);
48486         var cid = cm.getColumnId(i);
48487         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48488         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48489         this.updateSplitters();
48490         this.layout();
48491         this.grid.fireEvent("columnresize", i, w);
48492     },
48493
48494     syncRowHeights : function(startIndex, endIndex){
48495         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48496             startIndex = startIndex || 0;
48497             var mrows = this.getBodyTable().rows;
48498             var lrows = this.getLockedTable().rows;
48499             var len = mrows.length-1;
48500             endIndex = Math.min(endIndex || len, len);
48501             for(var i = startIndex; i <= endIndex; i++){
48502                 var m = mrows[i], l = lrows[i];
48503                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48504                 m.style.height = l.style.height = h + "px";
48505             }
48506         }
48507     },
48508
48509     layout : function(initialRender, is2ndPass){
48510         var g = this.grid;
48511         var auto = g.autoHeight;
48512         var scrollOffset = 16;
48513         var c = g.getGridEl(), cm = this.cm,
48514                 expandCol = g.autoExpandColumn,
48515                 gv = this;
48516         //c.beginMeasure();
48517
48518         if(!c.dom.offsetWidth){ // display:none?
48519             if(initialRender){
48520                 this.lockedWrap.show();
48521                 this.mainWrap.show();
48522             }
48523             return;
48524         }
48525
48526         var hasLock = this.cm.isLocked(0);
48527
48528         var tbh = this.headerPanel.getHeight();
48529         var bbh = this.footerPanel.getHeight();
48530
48531         if(auto){
48532             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48533             var newHeight = ch + c.getBorderWidth("tb");
48534             if(g.maxHeight){
48535                 newHeight = Math.min(g.maxHeight, newHeight);
48536             }
48537             c.setHeight(newHeight);
48538         }
48539
48540         if(g.autoWidth){
48541             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48542         }
48543
48544         var s = this.scroller;
48545
48546         var csize = c.getSize(true);
48547
48548         this.el.setSize(csize.width, csize.height);
48549
48550         this.headerPanel.setWidth(csize.width);
48551         this.footerPanel.setWidth(csize.width);
48552
48553         var hdHeight = this.mainHd.getHeight();
48554         var vw = csize.width;
48555         var vh = csize.height - (tbh + bbh);
48556
48557         s.setSize(vw, vh);
48558
48559         var bt = this.getBodyTable();
48560         var ltWidth = hasLock ?
48561                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48562
48563         var scrollHeight = bt.offsetHeight;
48564         var scrollWidth = ltWidth + bt.offsetWidth;
48565         var vscroll = false, hscroll = false;
48566
48567         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48568
48569         var lw = this.lockedWrap, mw = this.mainWrap;
48570         var lb = this.lockedBody, mb = this.mainBody;
48571
48572         setTimeout(function(){
48573             var t = s.dom.offsetTop;
48574             var w = s.dom.clientWidth,
48575                 h = s.dom.clientHeight;
48576
48577             lw.setTop(t);
48578             lw.setSize(ltWidth, h);
48579
48580             mw.setLeftTop(ltWidth, t);
48581             mw.setSize(w-ltWidth, h);
48582
48583             lb.setHeight(h-hdHeight);
48584             mb.setHeight(h-hdHeight);
48585
48586             if(is2ndPass !== true && !gv.userResized && expandCol){
48587                 // high speed resize without full column calculation
48588                 
48589                 var ci = cm.getIndexById(expandCol);
48590                 if (ci < 0) {
48591                     ci = cm.findColumnIndex(expandCol);
48592                 }
48593                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48594                 var expandId = cm.getColumnId(ci);
48595                 var  tw = cm.getTotalWidth(false);
48596                 var currentWidth = cm.getColumnWidth(ci);
48597                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48598                 if(currentWidth != cw){
48599                     cm.setColumnWidth(ci, cw, true);
48600                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48601                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48602                     gv.updateSplitters();
48603                     gv.layout(false, true);
48604                 }
48605             }
48606
48607             if(initialRender){
48608                 lw.show();
48609                 mw.show();
48610             }
48611             //c.endMeasure();
48612         }, 10);
48613     },
48614
48615     onWindowResize : function(){
48616         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48617             return;
48618         }
48619         this.layout();
48620     },
48621
48622     appendFooter : function(parentEl){
48623         return null;
48624     },
48625
48626     sortAscText : "Sort Ascending",
48627     sortDescText : "Sort Descending",
48628     lockText : "Lock Column",
48629     unlockText : "Unlock Column",
48630     columnsText : "Columns"
48631 });
48632
48633
48634 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48635     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48636     this.proxy.el.addClass('x-grid3-col-dd');
48637 };
48638
48639 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48640     handleMouseDown : function(e){
48641
48642     },
48643
48644     callHandleMouseDown : function(e){
48645         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48646     }
48647 });
48648 /*
48649  * Based on:
48650  * Ext JS Library 1.1.1
48651  * Copyright(c) 2006-2007, Ext JS, LLC.
48652  *
48653  * Originally Released Under LGPL - original licence link has changed is not relivant.
48654  *
48655  * Fork - LGPL
48656  * <script type="text/javascript">
48657  */
48658  
48659 // private
48660 // This is a support class used internally by the Grid components
48661 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48662     this.grid = grid;
48663     this.view = grid.getView();
48664     this.proxy = this.view.resizeProxy;
48665     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48666         "gridSplitters" + this.grid.getGridEl().id, {
48667         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48668     });
48669     this.setHandleElId(Roo.id(hd));
48670     this.setOuterHandleElId(Roo.id(hd2));
48671     this.scroll = false;
48672 };
48673 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48674     fly: Roo.Element.fly,
48675
48676     b4StartDrag : function(x, y){
48677         this.view.headersDisabled = true;
48678         this.proxy.setHeight(this.view.mainWrap.getHeight());
48679         var w = this.cm.getColumnWidth(this.cellIndex);
48680         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48681         this.resetConstraints();
48682         this.setXConstraint(minw, 1000);
48683         this.setYConstraint(0, 0);
48684         this.minX = x - minw;
48685         this.maxX = x + 1000;
48686         this.startPos = x;
48687         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48688     },
48689
48690
48691     handleMouseDown : function(e){
48692         ev = Roo.EventObject.setEvent(e);
48693         var t = this.fly(ev.getTarget());
48694         if(t.hasClass("x-grid-split")){
48695             this.cellIndex = this.view.getCellIndex(t.dom);
48696             this.split = t.dom;
48697             this.cm = this.grid.colModel;
48698             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48699                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48700             }
48701         }
48702     },
48703
48704     endDrag : function(e){
48705         this.view.headersDisabled = false;
48706         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48707         var diff = endX - this.startPos;
48708         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48709     },
48710
48711     autoOffset : function(){
48712         this.setDelta(0,0);
48713     }
48714 });/*
48715  * Based on:
48716  * Ext JS Library 1.1.1
48717  * Copyright(c) 2006-2007, Ext JS, LLC.
48718  *
48719  * Originally Released Under LGPL - original licence link has changed is not relivant.
48720  *
48721  * Fork - LGPL
48722  * <script type="text/javascript">
48723  */
48724  
48725 // private
48726 // This is a support class used internally by the Grid components
48727 Roo.grid.GridDragZone = function(grid, config){
48728     this.view = grid.getView();
48729     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48730     if(this.view.lockedBody){
48731         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48732         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48733     }
48734     this.scroll = false;
48735     this.grid = grid;
48736     this.ddel = document.createElement('div');
48737     this.ddel.className = 'x-grid-dd-wrap';
48738 };
48739
48740 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48741     ddGroup : "GridDD",
48742
48743     getDragData : function(e){
48744         var t = Roo.lib.Event.getTarget(e);
48745         var rowIndex = this.view.findRowIndex(t);
48746         if(rowIndex !== false){
48747             var sm = this.grid.selModel;
48748             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48749               //  sm.mouseDown(e, t);
48750             //}
48751             if (e.hasModifier()){
48752                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48753             }
48754             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48755         }
48756         return false;
48757     },
48758
48759     onInitDrag : function(e){
48760         var data = this.dragData;
48761         this.ddel.innerHTML = this.grid.getDragDropText();
48762         this.proxy.update(this.ddel);
48763         // fire start drag?
48764     },
48765
48766     afterRepair : function(){
48767         this.dragging = false;
48768     },
48769
48770     getRepairXY : function(e, data){
48771         return false;
48772     },
48773
48774     onEndDrag : function(data, e){
48775         // fire end drag?
48776     },
48777
48778     onValidDrop : function(dd, e, id){
48779         // fire drag drop?
48780         this.hideProxy();
48781     },
48782
48783     beforeInvalidDrop : function(e, id){
48784
48785     }
48786 });/*
48787  * Based on:
48788  * Ext JS Library 1.1.1
48789  * Copyright(c) 2006-2007, Ext JS, LLC.
48790  *
48791  * Originally Released Under LGPL - original licence link has changed is not relivant.
48792  *
48793  * Fork - LGPL
48794  * <script type="text/javascript">
48795  */
48796  
48797
48798 /**
48799  * @class Roo.grid.ColumnModel
48800  * @extends Roo.util.Observable
48801  * This is the default implementation of a ColumnModel used by the Grid. It defines
48802  * the columns in the grid.
48803  * <br>Usage:<br>
48804  <pre><code>
48805  var colModel = new Roo.grid.ColumnModel([
48806         {header: "Ticker", width: 60, sortable: true, locked: true},
48807         {header: "Company Name", width: 150, sortable: true},
48808         {header: "Market Cap.", width: 100, sortable: true},
48809         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48810         {header: "Employees", width: 100, sortable: true, resizable: false}
48811  ]);
48812  </code></pre>
48813  * <p>
48814  
48815  * The config options listed for this class are options which may appear in each
48816  * individual column definition.
48817  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48818  * @constructor
48819  * @param {Object} config An Array of column config objects. See this class's
48820  * config objects for details.
48821 */
48822 Roo.grid.ColumnModel = function(config){
48823         /**
48824      * The config passed into the constructor
48825      */
48826     this.config = config;
48827     this.lookup = {};
48828
48829     // if no id, create one
48830     // if the column does not have a dataIndex mapping,
48831     // map it to the order it is in the config
48832     for(var i = 0, len = config.length; i < len; i++){
48833         var c = config[i];
48834         if(typeof c.dataIndex == "undefined"){
48835             c.dataIndex = i;
48836         }
48837         if(typeof c.renderer == "string"){
48838             c.renderer = Roo.util.Format[c.renderer];
48839         }
48840         if(typeof c.id == "undefined"){
48841             c.id = Roo.id();
48842         }
48843         if(c.editor && c.editor.xtype){
48844             c.editor  = Roo.factory(c.editor, Roo.grid);
48845         }
48846         if(c.editor && c.editor.isFormField){
48847             c.editor = new Roo.grid.GridEditor(c.editor);
48848         }
48849         this.lookup[c.id] = c;
48850     }
48851
48852     /**
48853      * The width of columns which have no width specified (defaults to 100)
48854      * @type Number
48855      */
48856     this.defaultWidth = 100;
48857
48858     /**
48859      * Default sortable of columns which have no sortable specified (defaults to false)
48860      * @type Boolean
48861      */
48862     this.defaultSortable = false;
48863
48864     this.addEvents({
48865         /**
48866              * @event widthchange
48867              * Fires when the width of a column changes.
48868              * @param {ColumnModel} this
48869              * @param {Number} columnIndex The column index
48870              * @param {Number} newWidth The new width
48871              */
48872             "widthchange": true,
48873         /**
48874              * @event headerchange
48875              * Fires when the text of a header changes.
48876              * @param {ColumnModel} this
48877              * @param {Number} columnIndex The column index
48878              * @param {Number} newText The new header text
48879              */
48880             "headerchange": true,
48881         /**
48882              * @event hiddenchange
48883              * Fires when a column is hidden or "unhidden".
48884              * @param {ColumnModel} this
48885              * @param {Number} columnIndex The column index
48886              * @param {Boolean} hidden true if hidden, false otherwise
48887              */
48888             "hiddenchange": true,
48889             /**
48890          * @event columnmoved
48891          * Fires when a column is moved.
48892          * @param {ColumnModel} this
48893          * @param {Number} oldIndex
48894          * @param {Number} newIndex
48895          */
48896         "columnmoved" : true,
48897         /**
48898          * @event columlockchange
48899          * Fires when a column's locked state is changed
48900          * @param {ColumnModel} this
48901          * @param {Number} colIndex
48902          * @param {Boolean} locked true if locked
48903          */
48904         "columnlockchange" : true
48905     });
48906     Roo.grid.ColumnModel.superclass.constructor.call(this);
48907 };
48908 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48909     /**
48910      * @cfg {String} header The header text to display in the Grid view.
48911      */
48912     /**
48913      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48914      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48915      * specified, the column's index is used as an index into the Record's data Array.
48916      */
48917     /**
48918      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48919      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48920      */
48921     /**
48922      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48923      * Defaults to the value of the {@link #defaultSortable} property.
48924      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48925      */
48926     /**
48927      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48928      */
48929     /**
48930      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48931      */
48932     /**
48933      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48934      */
48935     /**
48936      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48937      */
48938     /**
48939      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48940      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48941      * default renderer uses the raw data value.
48942      */
48943        /**
48944      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48945      */
48946     /**
48947      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48948      */
48949
48950     /**
48951      * Returns the id of the column at the specified index.
48952      * @param {Number} index The column index
48953      * @return {String} the id
48954      */
48955     getColumnId : function(index){
48956         return this.config[index].id;
48957     },
48958
48959     /**
48960      * Returns the column for a specified id.
48961      * @param {String} id The column id
48962      * @return {Object} the column
48963      */
48964     getColumnById : function(id){
48965         return this.lookup[id];
48966     },
48967
48968     
48969     /**
48970      * Returns the column for a specified dataIndex.
48971      * @param {String} dataIndex The column dataIndex
48972      * @return {Object|Boolean} the column or false if not found
48973      */
48974     getColumnByDataIndex: function(dataIndex){
48975         var index = this.findColumnIndex(dataIndex);
48976         return index > -1 ? this.config[index] : false;
48977     },
48978     
48979     /**
48980      * Returns the index for a specified column id.
48981      * @param {String} id The column id
48982      * @return {Number} the index, or -1 if not found
48983      */
48984     getIndexById : function(id){
48985         for(var i = 0, len = this.config.length; i < len; i++){
48986             if(this.config[i].id == id){
48987                 return i;
48988             }
48989         }
48990         return -1;
48991     },
48992     
48993     /**
48994      * Returns the index for a specified column dataIndex.
48995      * @param {String} dataIndex The column dataIndex
48996      * @return {Number} the index, or -1 if not found
48997      */
48998     
48999     findColumnIndex : function(dataIndex){
49000         for(var i = 0, len = this.config.length; i < len; i++){
49001             if(this.config[i].dataIndex == dataIndex){
49002                 return i;
49003             }
49004         }
49005         return -1;
49006     },
49007     
49008     
49009     moveColumn : function(oldIndex, newIndex){
49010         var c = this.config[oldIndex];
49011         this.config.splice(oldIndex, 1);
49012         this.config.splice(newIndex, 0, c);
49013         this.dataMap = null;
49014         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49015     },
49016
49017     isLocked : function(colIndex){
49018         return this.config[colIndex].locked === true;
49019     },
49020
49021     setLocked : function(colIndex, value, suppressEvent){
49022         if(this.isLocked(colIndex) == value){
49023             return;
49024         }
49025         this.config[colIndex].locked = value;
49026         if(!suppressEvent){
49027             this.fireEvent("columnlockchange", this, colIndex, value);
49028         }
49029     },
49030
49031     getTotalLockedWidth : function(){
49032         var totalWidth = 0;
49033         for(var i = 0; i < this.config.length; i++){
49034             if(this.isLocked(i) && !this.isHidden(i)){
49035                 this.totalWidth += this.getColumnWidth(i);
49036             }
49037         }
49038         return totalWidth;
49039     },
49040
49041     getLockedCount : function(){
49042         for(var i = 0, len = this.config.length; i < len; i++){
49043             if(!this.isLocked(i)){
49044                 return i;
49045             }
49046         }
49047     },
49048
49049     /**
49050      * Returns the number of columns.
49051      * @return {Number}
49052      */
49053     getColumnCount : function(visibleOnly){
49054         if(visibleOnly === true){
49055             var c = 0;
49056             for(var i = 0, len = this.config.length; i < len; i++){
49057                 if(!this.isHidden(i)){
49058                     c++;
49059                 }
49060             }
49061             return c;
49062         }
49063         return this.config.length;
49064     },
49065
49066     /**
49067      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49068      * @param {Function} fn
49069      * @param {Object} scope (optional)
49070      * @return {Array} result
49071      */
49072     getColumnsBy : function(fn, scope){
49073         var r = [];
49074         for(var i = 0, len = this.config.length; i < len; i++){
49075             var c = this.config[i];
49076             if(fn.call(scope||this, c, i) === true){
49077                 r[r.length] = c;
49078             }
49079         }
49080         return r;
49081     },
49082
49083     /**
49084      * Returns true if the specified column is sortable.
49085      * @param {Number} col The column index
49086      * @return {Boolean}
49087      */
49088     isSortable : function(col){
49089         if(typeof this.config[col].sortable == "undefined"){
49090             return this.defaultSortable;
49091         }
49092         return this.config[col].sortable;
49093     },
49094
49095     /**
49096      * Returns the rendering (formatting) function defined for the column.
49097      * @param {Number} col The column index.
49098      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49099      */
49100     getRenderer : function(col){
49101         if(!this.config[col].renderer){
49102             return Roo.grid.ColumnModel.defaultRenderer;
49103         }
49104         return this.config[col].renderer;
49105     },
49106
49107     /**
49108      * Sets the rendering (formatting) function for a column.
49109      * @param {Number} col The column index
49110      * @param {Function} fn The function to use to process the cell's raw data
49111      * to return HTML markup for the grid view. The render function is called with
49112      * the following parameters:<ul>
49113      * <li>Data value.</li>
49114      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49115      * <li>css A CSS style string to apply to the table cell.</li>
49116      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49117      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49118      * <li>Row index</li>
49119      * <li>Column index</li>
49120      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49121      */
49122     setRenderer : function(col, fn){
49123         this.config[col].renderer = fn;
49124     },
49125
49126     /**
49127      * Returns the width for the specified column.
49128      * @param {Number} col The column index
49129      * @return {Number}
49130      */
49131     getColumnWidth : function(col){
49132         return this.config[col].width || this.defaultWidth;
49133     },
49134
49135     /**
49136      * Sets the width for a column.
49137      * @param {Number} col The column index
49138      * @param {Number} width The new width
49139      */
49140     setColumnWidth : function(col, width, suppressEvent){
49141         this.config[col].width = width;
49142         this.totalWidth = null;
49143         if(!suppressEvent){
49144              this.fireEvent("widthchange", this, col, width);
49145         }
49146     },
49147
49148     /**
49149      * Returns the total width of all columns.
49150      * @param {Boolean} includeHidden True to include hidden column widths
49151      * @return {Number}
49152      */
49153     getTotalWidth : function(includeHidden){
49154         if(!this.totalWidth){
49155             this.totalWidth = 0;
49156             for(var i = 0, len = this.config.length; i < len; i++){
49157                 if(includeHidden || !this.isHidden(i)){
49158                     this.totalWidth += this.getColumnWidth(i);
49159                 }
49160             }
49161         }
49162         return this.totalWidth;
49163     },
49164
49165     /**
49166      * Returns the header for the specified column.
49167      * @param {Number} col The column index
49168      * @return {String}
49169      */
49170     getColumnHeader : function(col){
49171         return this.config[col].header;
49172     },
49173
49174     /**
49175      * Sets the header for a column.
49176      * @param {Number} col The column index
49177      * @param {String} header The new header
49178      */
49179     setColumnHeader : function(col, header){
49180         this.config[col].header = header;
49181         this.fireEvent("headerchange", this, col, header);
49182     },
49183
49184     /**
49185      * Returns the tooltip for the specified column.
49186      * @param {Number} col The column index
49187      * @return {String}
49188      */
49189     getColumnTooltip : function(col){
49190             return this.config[col].tooltip;
49191     },
49192     /**
49193      * Sets the tooltip for a column.
49194      * @param {Number} col The column index
49195      * @param {String} tooltip The new tooltip
49196      */
49197     setColumnTooltip : function(col, tooltip){
49198             this.config[col].tooltip = tooltip;
49199     },
49200
49201     /**
49202      * Returns the dataIndex for the specified column.
49203      * @param {Number} col The column index
49204      * @return {Number}
49205      */
49206     getDataIndex : function(col){
49207         return this.config[col].dataIndex;
49208     },
49209
49210     /**
49211      * Sets the dataIndex for a column.
49212      * @param {Number} col The column index
49213      * @param {Number} dataIndex The new dataIndex
49214      */
49215     setDataIndex : function(col, dataIndex){
49216         this.config[col].dataIndex = dataIndex;
49217     },
49218
49219     
49220     
49221     /**
49222      * Returns true if the cell is editable.
49223      * @param {Number} colIndex The column index
49224      * @param {Number} rowIndex The row index
49225      * @return {Boolean}
49226      */
49227     isCellEditable : function(colIndex, rowIndex){
49228         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49229     },
49230
49231     /**
49232      * Returns the editor defined for the cell/column.
49233      * return false or null to disable editing.
49234      * @param {Number} colIndex The column index
49235      * @param {Number} rowIndex The row index
49236      * @return {Object}
49237      */
49238     getCellEditor : function(colIndex, rowIndex){
49239         return this.config[colIndex].editor;
49240     },
49241
49242     /**
49243      * Sets if a column is editable.
49244      * @param {Number} col The column index
49245      * @param {Boolean} editable True if the column is editable
49246      */
49247     setEditable : function(col, editable){
49248         this.config[col].editable = editable;
49249     },
49250
49251
49252     /**
49253      * Returns true if the column is hidden.
49254      * @param {Number} colIndex The column index
49255      * @return {Boolean}
49256      */
49257     isHidden : function(colIndex){
49258         return this.config[colIndex].hidden;
49259     },
49260
49261
49262     /**
49263      * Returns true if the column width cannot be changed
49264      */
49265     isFixed : function(colIndex){
49266         return this.config[colIndex].fixed;
49267     },
49268
49269     /**
49270      * Returns true if the column can be resized
49271      * @return {Boolean}
49272      */
49273     isResizable : function(colIndex){
49274         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49275     },
49276     /**
49277      * Sets if a column is hidden.
49278      * @param {Number} colIndex The column index
49279      * @param {Boolean} hidden True if the column is hidden
49280      */
49281     setHidden : function(colIndex, hidden){
49282         this.config[colIndex].hidden = hidden;
49283         this.totalWidth = null;
49284         this.fireEvent("hiddenchange", this, colIndex, hidden);
49285     },
49286
49287     /**
49288      * Sets the editor for a column.
49289      * @param {Number} col The column index
49290      * @param {Object} editor The editor object
49291      */
49292     setEditor : function(col, editor){
49293         this.config[col].editor = editor;
49294     }
49295 });
49296
49297 Roo.grid.ColumnModel.defaultRenderer = function(value){
49298         if(typeof value == "string" && value.length < 1){
49299             return "&#160;";
49300         }
49301         return value;
49302 };
49303
49304 // Alias for backwards compatibility
49305 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49306 /*
49307  * Based on:
49308  * Ext JS Library 1.1.1
49309  * Copyright(c) 2006-2007, Ext JS, LLC.
49310  *
49311  * Originally Released Under LGPL - original licence link has changed is not relivant.
49312  *
49313  * Fork - LGPL
49314  * <script type="text/javascript">
49315  */
49316
49317 /**
49318  * @class Roo.grid.AbstractSelectionModel
49319  * @extends Roo.util.Observable
49320  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49321  * implemented by descendant classes.  This class should not be directly instantiated.
49322  * @constructor
49323  */
49324 Roo.grid.AbstractSelectionModel = function(){
49325     this.locked = false;
49326     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49327 };
49328
49329 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49330     /** @ignore Called by the grid automatically. Do not call directly. */
49331     init : function(grid){
49332         this.grid = grid;
49333         this.initEvents();
49334     },
49335
49336     /**
49337      * Locks the selections.
49338      */
49339     lock : function(){
49340         this.locked = true;
49341     },
49342
49343     /**
49344      * Unlocks the selections.
49345      */
49346     unlock : function(){
49347         this.locked = false;
49348     },
49349
49350     /**
49351      * Returns true if the selections are locked.
49352      * @return {Boolean}
49353      */
49354     isLocked : function(){
49355         return this.locked;
49356     }
49357 });/*
49358  * Based on:
49359  * Ext JS Library 1.1.1
49360  * Copyright(c) 2006-2007, Ext JS, LLC.
49361  *
49362  * Originally Released Under LGPL - original licence link has changed is not relivant.
49363  *
49364  * Fork - LGPL
49365  * <script type="text/javascript">
49366  */
49367 /**
49368  * @extends Roo.grid.AbstractSelectionModel
49369  * @class Roo.grid.RowSelectionModel
49370  * The default SelectionModel used by {@link Roo.grid.Grid}.
49371  * It supports multiple selections and keyboard selection/navigation. 
49372  * @constructor
49373  * @param {Object} config
49374  */
49375 Roo.grid.RowSelectionModel = function(config){
49376     Roo.apply(this, config);
49377     this.selections = new Roo.util.MixedCollection(false, function(o){
49378         return o.id;
49379     });
49380
49381     this.last = false;
49382     this.lastActive = false;
49383
49384     this.addEvents({
49385         /**
49386              * @event selectionchange
49387              * Fires when the selection changes
49388              * @param {SelectionModel} this
49389              */
49390             "selectionchange" : true,
49391         /**
49392              * @event afterselectionchange
49393              * Fires after the selection changes (eg. by key press or clicking)
49394              * @param {SelectionModel} this
49395              */
49396             "afterselectionchange" : true,
49397         /**
49398              * @event beforerowselect
49399              * Fires when a row is selected being selected, return false to cancel.
49400              * @param {SelectionModel} this
49401              * @param {Number} rowIndex The selected index
49402              * @param {Boolean} keepExisting False if other selections will be cleared
49403              */
49404             "beforerowselect" : true,
49405         /**
49406              * @event rowselect
49407              * Fires when a row is selected.
49408              * @param {SelectionModel} this
49409              * @param {Number} rowIndex The selected index
49410              * @param {Roo.data.Record} r The record
49411              */
49412             "rowselect" : true,
49413         /**
49414              * @event rowdeselect
49415              * Fires when a row is deselected.
49416              * @param {SelectionModel} this
49417              * @param {Number} rowIndex The selected index
49418              */
49419         "rowdeselect" : true
49420     });
49421     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49422     this.locked = false;
49423 };
49424
49425 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49426     /**
49427      * @cfg {Boolean} singleSelect
49428      * True to allow selection of only one row at a time (defaults to false)
49429      */
49430     singleSelect : false,
49431
49432     // private
49433     initEvents : function(){
49434
49435         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49436             this.grid.on("mousedown", this.handleMouseDown, this);
49437         }else{ // allow click to work like normal
49438             this.grid.on("rowclick", this.handleDragableRowClick, this);
49439         }
49440
49441         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49442             "up" : function(e){
49443                 if(!e.shiftKey){
49444                     this.selectPrevious(e.shiftKey);
49445                 }else if(this.last !== false && this.lastActive !== false){
49446                     var last = this.last;
49447                     this.selectRange(this.last,  this.lastActive-1);
49448                     this.grid.getView().focusRow(this.lastActive);
49449                     if(last !== false){
49450                         this.last = last;
49451                     }
49452                 }else{
49453                     this.selectFirstRow();
49454                 }
49455                 this.fireEvent("afterselectionchange", this);
49456             },
49457             "down" : function(e){
49458                 if(!e.shiftKey){
49459                     this.selectNext(e.shiftKey);
49460                 }else if(this.last !== false && this.lastActive !== false){
49461                     var last = this.last;
49462                     this.selectRange(this.last,  this.lastActive+1);
49463                     this.grid.getView().focusRow(this.lastActive);
49464                     if(last !== false){
49465                         this.last = last;
49466                     }
49467                 }else{
49468                     this.selectFirstRow();
49469                 }
49470                 this.fireEvent("afterselectionchange", this);
49471             },
49472             scope: this
49473         });
49474
49475         var view = this.grid.view;
49476         view.on("refresh", this.onRefresh, this);
49477         view.on("rowupdated", this.onRowUpdated, this);
49478         view.on("rowremoved", this.onRemove, this);
49479     },
49480
49481     // private
49482     onRefresh : function(){
49483         var ds = this.grid.dataSource, i, v = this.grid.view;
49484         var s = this.selections;
49485         s.each(function(r){
49486             if((i = ds.indexOfId(r.id)) != -1){
49487                 v.onRowSelect(i);
49488             }else{
49489                 s.remove(r);
49490             }
49491         });
49492     },
49493
49494     // private
49495     onRemove : function(v, index, r){
49496         this.selections.remove(r);
49497     },
49498
49499     // private
49500     onRowUpdated : function(v, index, r){
49501         if(this.isSelected(r)){
49502             v.onRowSelect(index);
49503         }
49504     },
49505
49506     /**
49507      * Select records.
49508      * @param {Array} records The records to select
49509      * @param {Boolean} keepExisting (optional) True to keep existing selections
49510      */
49511     selectRecords : function(records, keepExisting){
49512         if(!keepExisting){
49513             this.clearSelections();
49514         }
49515         var ds = this.grid.dataSource;
49516         for(var i = 0, len = records.length; i < len; i++){
49517             this.selectRow(ds.indexOf(records[i]), true);
49518         }
49519     },
49520
49521     /**
49522      * Gets the number of selected rows.
49523      * @return {Number}
49524      */
49525     getCount : function(){
49526         return this.selections.length;
49527     },
49528
49529     /**
49530      * Selects the first row in the grid.
49531      */
49532     selectFirstRow : function(){
49533         this.selectRow(0);
49534     },
49535
49536     /**
49537      * Select the last row.
49538      * @param {Boolean} keepExisting (optional) True to keep existing selections
49539      */
49540     selectLastRow : function(keepExisting){
49541         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49542     },
49543
49544     /**
49545      * Selects the row immediately following the last selected row.
49546      * @param {Boolean} keepExisting (optional) True to keep existing selections
49547      */
49548     selectNext : function(keepExisting){
49549         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49550             this.selectRow(this.last+1, keepExisting);
49551             this.grid.getView().focusRow(this.last);
49552         }
49553     },
49554
49555     /**
49556      * Selects the row that precedes the last selected row.
49557      * @param {Boolean} keepExisting (optional) True to keep existing selections
49558      */
49559     selectPrevious : function(keepExisting){
49560         if(this.last){
49561             this.selectRow(this.last-1, keepExisting);
49562             this.grid.getView().focusRow(this.last);
49563         }
49564     },
49565
49566     /**
49567      * Returns the selected records
49568      * @return {Array} Array of selected records
49569      */
49570     getSelections : function(){
49571         return [].concat(this.selections.items);
49572     },
49573
49574     /**
49575      * Returns the first selected record.
49576      * @return {Record}
49577      */
49578     getSelected : function(){
49579         return this.selections.itemAt(0);
49580     },
49581
49582
49583     /**
49584      * Clears all selections.
49585      */
49586     clearSelections : function(fast){
49587         if(this.locked) return;
49588         if(fast !== true){
49589             var ds = this.grid.dataSource;
49590             var s = this.selections;
49591             s.each(function(r){
49592                 this.deselectRow(ds.indexOfId(r.id));
49593             }, this);
49594             s.clear();
49595         }else{
49596             this.selections.clear();
49597         }
49598         this.last = false;
49599     },
49600
49601
49602     /**
49603      * Selects all rows.
49604      */
49605     selectAll : function(){
49606         if(this.locked) return;
49607         this.selections.clear();
49608         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49609             this.selectRow(i, true);
49610         }
49611     },
49612
49613     /**
49614      * Returns True if there is a selection.
49615      * @return {Boolean}
49616      */
49617     hasSelection : function(){
49618         return this.selections.length > 0;
49619     },
49620
49621     /**
49622      * Returns True if the specified row is selected.
49623      * @param {Number/Record} record The record or index of the record to check
49624      * @return {Boolean}
49625      */
49626     isSelected : function(index){
49627         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49628         return (r && this.selections.key(r.id) ? true : false);
49629     },
49630
49631     /**
49632      * Returns True if the specified record id is selected.
49633      * @param {String} id The id of record to check
49634      * @return {Boolean}
49635      */
49636     isIdSelected : function(id){
49637         return (this.selections.key(id) ? true : false);
49638     },
49639
49640     // private
49641     handleMouseDown : function(e, t){
49642         var view = this.grid.getView(), rowIndex;
49643         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49644             return;
49645         };
49646         if(e.shiftKey && this.last !== false){
49647             var last = this.last;
49648             this.selectRange(last, rowIndex, e.ctrlKey);
49649             this.last = last; // reset the last
49650             view.focusRow(rowIndex);
49651         }else{
49652             var isSelected = this.isSelected(rowIndex);
49653             if(e.button !== 0 && isSelected){
49654                 view.focusRow(rowIndex);
49655             }else if(e.ctrlKey && isSelected){
49656                 this.deselectRow(rowIndex);
49657             }else if(!isSelected){
49658                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49659                 view.focusRow(rowIndex);
49660             }
49661         }
49662         this.fireEvent("afterselectionchange", this);
49663     },
49664     // private
49665     handleDragableRowClick :  function(grid, rowIndex, e) 
49666     {
49667         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49668             this.selectRow(rowIndex, false);
49669             grid.view.focusRow(rowIndex);
49670              this.fireEvent("afterselectionchange", this);
49671         }
49672     },
49673     
49674     /**
49675      * Selects multiple rows.
49676      * @param {Array} rows Array of the indexes of the row to select
49677      * @param {Boolean} keepExisting (optional) True to keep existing selections
49678      */
49679     selectRows : function(rows, keepExisting){
49680         if(!keepExisting){
49681             this.clearSelections();
49682         }
49683         for(var i = 0, len = rows.length; i < len; i++){
49684             this.selectRow(rows[i], true);
49685         }
49686     },
49687
49688     /**
49689      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49690      * @param {Number} startRow The index of the first row in the range
49691      * @param {Number} endRow The index of the last row in the range
49692      * @param {Boolean} keepExisting (optional) True to retain existing selections
49693      */
49694     selectRange : function(startRow, endRow, keepExisting){
49695         if(this.locked) return;
49696         if(!keepExisting){
49697             this.clearSelections();
49698         }
49699         if(startRow <= endRow){
49700             for(var i = startRow; i <= endRow; i++){
49701                 this.selectRow(i, true);
49702             }
49703         }else{
49704             for(var i = startRow; i >= endRow; i--){
49705                 this.selectRow(i, true);
49706             }
49707         }
49708     },
49709
49710     /**
49711      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49712      * @param {Number} startRow The index of the first row in the range
49713      * @param {Number} endRow The index of the last row in the range
49714      */
49715     deselectRange : function(startRow, endRow, preventViewNotify){
49716         if(this.locked) return;
49717         for(var i = startRow; i <= endRow; i++){
49718             this.deselectRow(i, preventViewNotify);
49719         }
49720     },
49721
49722     /**
49723      * Selects a row.
49724      * @param {Number} row The index of the row to select
49725      * @param {Boolean} keepExisting (optional) True to keep existing selections
49726      */
49727     selectRow : function(index, keepExisting, preventViewNotify){
49728         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49729         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49730             if(!keepExisting || this.singleSelect){
49731                 this.clearSelections();
49732             }
49733             var r = this.grid.dataSource.getAt(index);
49734             this.selections.add(r);
49735             this.last = this.lastActive = index;
49736             if(!preventViewNotify){
49737                 this.grid.getView().onRowSelect(index);
49738             }
49739             this.fireEvent("rowselect", this, index, r);
49740             this.fireEvent("selectionchange", this);
49741         }
49742     },
49743
49744     /**
49745      * Deselects a row.
49746      * @param {Number} row The index of the row to deselect
49747      */
49748     deselectRow : function(index, preventViewNotify){
49749         if(this.locked) return;
49750         if(this.last == index){
49751             this.last = false;
49752         }
49753         if(this.lastActive == index){
49754             this.lastActive = false;
49755         }
49756         var r = this.grid.dataSource.getAt(index);
49757         this.selections.remove(r);
49758         if(!preventViewNotify){
49759             this.grid.getView().onRowDeselect(index);
49760         }
49761         this.fireEvent("rowdeselect", this, index);
49762         this.fireEvent("selectionchange", this);
49763     },
49764
49765     // private
49766     restoreLast : function(){
49767         if(this._last){
49768             this.last = this._last;
49769         }
49770     },
49771
49772     // private
49773     acceptsNav : function(row, col, cm){
49774         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49775     },
49776
49777     // private
49778     onEditorKey : function(field, e){
49779         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49780         if(k == e.TAB){
49781             e.stopEvent();
49782             ed.completeEdit();
49783             if(e.shiftKey){
49784                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49785             }else{
49786                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49787             }
49788         }else if(k == e.ENTER && !e.ctrlKey){
49789             e.stopEvent();
49790             ed.completeEdit();
49791             if(e.shiftKey){
49792                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49793             }else{
49794                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49795             }
49796         }else if(k == e.ESC){
49797             ed.cancelEdit();
49798         }
49799         if(newCell){
49800             g.startEditing(newCell[0], newCell[1]);
49801         }
49802     }
49803 });/*
49804  * Based on:
49805  * Ext JS Library 1.1.1
49806  * Copyright(c) 2006-2007, Ext JS, LLC.
49807  *
49808  * Originally Released Under LGPL - original licence link has changed is not relivant.
49809  *
49810  * Fork - LGPL
49811  * <script type="text/javascript">
49812  */
49813 /**
49814  * @class Roo.grid.CellSelectionModel
49815  * @extends Roo.grid.AbstractSelectionModel
49816  * This class provides the basic implementation for cell selection in a grid.
49817  * @constructor
49818  * @param {Object} config The object containing the configuration of this model.
49819  */
49820 Roo.grid.CellSelectionModel = function(config){
49821     Roo.apply(this, config);
49822
49823     this.selection = null;
49824
49825     this.addEvents({
49826         /**
49827              * @event beforerowselect
49828              * Fires before a cell is selected.
49829              * @param {SelectionModel} this
49830              * @param {Number} rowIndex The selected row index
49831              * @param {Number} colIndex The selected cell index
49832              */
49833             "beforecellselect" : true,
49834         /**
49835              * @event cellselect
49836              * Fires when a cell is selected.
49837              * @param {SelectionModel} this
49838              * @param {Number} rowIndex The selected row index
49839              * @param {Number} colIndex The selected cell index
49840              */
49841             "cellselect" : true,
49842         /**
49843              * @event selectionchange
49844              * Fires when the active selection changes.
49845              * @param {SelectionModel} this
49846              * @param {Object} selection null for no selection or an object (o) with two properties
49847                 <ul>
49848                 <li>o.record: the record object for the row the selection is in</li>
49849                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49850                 </ul>
49851              */
49852             "selectionchange" : true
49853     });
49854     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49855 };
49856
49857 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49858
49859     /** @ignore */
49860     initEvents : function(){
49861         this.grid.on("mousedown", this.handleMouseDown, this);
49862         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49863         var view = this.grid.view;
49864         view.on("refresh", this.onViewChange, this);
49865         view.on("rowupdated", this.onRowUpdated, this);
49866         view.on("beforerowremoved", this.clearSelections, this);
49867         view.on("beforerowsinserted", this.clearSelections, this);
49868         if(this.grid.isEditor){
49869             this.grid.on("beforeedit", this.beforeEdit,  this);
49870         }
49871     },
49872
49873         //private
49874     beforeEdit : function(e){
49875         this.select(e.row, e.column, false, true, e.record);
49876     },
49877
49878         //private
49879     onRowUpdated : function(v, index, r){
49880         if(this.selection && this.selection.record == r){
49881             v.onCellSelect(index, this.selection.cell[1]);
49882         }
49883     },
49884
49885         //private
49886     onViewChange : function(){
49887         this.clearSelections(true);
49888     },
49889
49890         /**
49891          * Returns the currently selected cell,.
49892          * @return {Array} The selected cell (row, column) or null if none selected.
49893          */
49894     getSelectedCell : function(){
49895         return this.selection ? this.selection.cell : null;
49896     },
49897
49898     /**
49899      * Clears all selections.
49900      * @param {Boolean} true to prevent the gridview from being notified about the change.
49901      */
49902     clearSelections : function(preventNotify){
49903         var s = this.selection;
49904         if(s){
49905             if(preventNotify !== true){
49906                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49907             }
49908             this.selection = null;
49909             this.fireEvent("selectionchange", this, null);
49910         }
49911     },
49912
49913     /**
49914      * Returns true if there is a selection.
49915      * @return {Boolean}
49916      */
49917     hasSelection : function(){
49918         return this.selection ? true : false;
49919     },
49920
49921     /** @ignore */
49922     handleMouseDown : function(e, t){
49923         var v = this.grid.getView();
49924         if(this.isLocked()){
49925             return;
49926         };
49927         var row = v.findRowIndex(t);
49928         var cell = v.findCellIndex(t);
49929         if(row !== false && cell !== false){
49930             this.select(row, cell);
49931         }
49932     },
49933
49934     /**
49935      * Selects a cell.
49936      * @param {Number} rowIndex
49937      * @param {Number} collIndex
49938      */
49939     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49940         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49941             this.clearSelections();
49942             r = r || this.grid.dataSource.getAt(rowIndex);
49943             this.selection = {
49944                 record : r,
49945                 cell : [rowIndex, colIndex]
49946             };
49947             if(!preventViewNotify){
49948                 var v = this.grid.getView();
49949                 v.onCellSelect(rowIndex, colIndex);
49950                 if(preventFocus !== true){
49951                     v.focusCell(rowIndex, colIndex);
49952                 }
49953             }
49954             this.fireEvent("cellselect", this, rowIndex, colIndex);
49955             this.fireEvent("selectionchange", this, this.selection);
49956         }
49957     },
49958
49959         //private
49960     isSelectable : function(rowIndex, colIndex, cm){
49961         return !cm.isHidden(colIndex);
49962     },
49963
49964     /** @ignore */
49965     handleKeyDown : function(e){
49966         Roo.log('Cell Sel Model handleKeyDown');
49967         if(!e.isNavKeyPress()){
49968             return;
49969         }
49970         var g = this.grid, s = this.selection;
49971         if(!s){
49972             e.stopEvent();
49973             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49974             if(cell){
49975                 this.select(cell[0], cell[1]);
49976             }
49977             return;
49978         }
49979         var sm = this;
49980         var walk = function(row, col, step){
49981             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49982         };
49983         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49984         var newCell;
49985
49986         switch(k){
49987             case e.TAB:
49988                 // handled by onEditorKey
49989                 if (g.isEditor && g.editing) {
49990                     return;
49991                 }
49992                 if(e.shiftKey){
49993                      newCell = walk(r, c-1, -1);
49994                 }else{
49995                      newCell = walk(r, c+1, 1);
49996                 }
49997              break;
49998              case e.DOWN:
49999                  newCell = walk(r+1, c, 1);
50000              break;
50001              case e.UP:
50002                  newCell = walk(r-1, c, -1);
50003              break;
50004              case e.RIGHT:
50005                  newCell = walk(r, c+1, 1);
50006              break;
50007              case e.LEFT:
50008                  newCell = walk(r, c-1, -1);
50009              break;
50010              case e.ENTER:
50011                  if(g.isEditor && !g.editing){
50012                     g.startEditing(r, c);
50013                     e.stopEvent();
50014                     return;
50015                 }
50016              break;
50017         };
50018         if(newCell){
50019             this.select(newCell[0], newCell[1]);
50020             e.stopEvent();
50021         }
50022     },
50023
50024     acceptsNav : function(row, col, cm){
50025         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50026     },
50027
50028     onEditorKey : function(field, e){
50029         
50030         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50031         ///Roo.log('onEditorKey' + k);
50032         
50033         if(k == e.TAB){
50034             if(e.shiftKey){
50035                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50036             }else{
50037                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50038             }
50039             e.stopEvent();
50040         }else if(k == e.ENTER && !e.ctrlKey){
50041             ed.completeEdit();
50042             e.stopEvent();
50043             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50044         }else if(k == e.ESC){
50045             ed.cancelEdit();
50046         }
50047         
50048         
50049         if(newCell){
50050             //Roo.log('next cell after edit');
50051             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50052         }
50053     }
50054 });/*
50055  * Based on:
50056  * Ext JS Library 1.1.1
50057  * Copyright(c) 2006-2007, Ext JS, LLC.
50058  *
50059  * Originally Released Under LGPL - original licence link has changed is not relivant.
50060  *
50061  * Fork - LGPL
50062  * <script type="text/javascript">
50063  */
50064  
50065 /**
50066  * @class Roo.grid.EditorGrid
50067  * @extends Roo.grid.Grid
50068  * Class for creating and editable grid.
50069  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50070  * The container MUST have some type of size defined for the grid to fill. The container will be 
50071  * automatically set to position relative if it isn't already.
50072  * @param {Object} dataSource The data model to bind to
50073  * @param {Object} colModel The column model with info about this grid's columns
50074  */
50075 Roo.grid.EditorGrid = function(container, config){
50076     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50077     this.getGridEl().addClass("xedit-grid");
50078
50079     if(!this.selModel){
50080         this.selModel = new Roo.grid.CellSelectionModel();
50081     }
50082
50083     this.activeEditor = null;
50084
50085         this.addEvents({
50086             /**
50087              * @event beforeedit
50088              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50089              * <ul style="padding:5px;padding-left:16px;">
50090              * <li>grid - This grid</li>
50091              * <li>record - The record being edited</li>
50092              * <li>field - The field name being edited</li>
50093              * <li>value - The value for the field being edited.</li>
50094              * <li>row - The grid row index</li>
50095              * <li>column - The grid column index</li>
50096              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50097              * </ul>
50098              * @param {Object} e An edit event (see above for description)
50099              */
50100             "beforeedit" : true,
50101             /**
50102              * @event afteredit
50103              * Fires after a cell is edited. <br />
50104              * <ul style="padding:5px;padding-left:16px;">
50105              * <li>grid - This grid</li>
50106              * <li>record - The record being edited</li>
50107              * <li>field - The field name being edited</li>
50108              * <li>value - The value being set</li>
50109              * <li>originalValue - The original value for the field, before the edit.</li>
50110              * <li>row - The grid row index</li>
50111              * <li>column - The grid column index</li>
50112              * </ul>
50113              * @param {Object} e An edit event (see above for description)
50114              */
50115             "afteredit" : true,
50116             /**
50117              * @event validateedit
50118              * Fires after a cell is edited, but before the value is set in the record. 
50119          * You can use this to modify the value being set in the field, Return false
50120              * to cancel the change. The edit event object has the following properties <br />
50121              * <ul style="padding:5px;padding-left:16px;">
50122          * <li>editor - This editor</li>
50123              * <li>grid - This grid</li>
50124              * <li>record - The record being edited</li>
50125              * <li>field - The field name being edited</li>
50126              * <li>value - The value being set</li>
50127              * <li>originalValue - The original value for the field, before the edit.</li>
50128              * <li>row - The grid row index</li>
50129              * <li>column - The grid column index</li>
50130              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50131              * </ul>
50132              * @param {Object} e An edit event (see above for description)
50133              */
50134             "validateedit" : true
50135         });
50136     this.on("bodyscroll", this.stopEditing,  this);
50137     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50138 };
50139
50140 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50141     /**
50142      * @cfg {Number} clicksToEdit
50143      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50144      */
50145     clicksToEdit: 2,
50146
50147     // private
50148     isEditor : true,
50149     // private
50150     trackMouseOver: false, // causes very odd FF errors
50151
50152     onCellDblClick : function(g, row, col){
50153         this.startEditing(row, col);
50154     },
50155
50156     onEditComplete : function(ed, value, startValue){
50157         this.editing = false;
50158         this.activeEditor = null;
50159         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50160         var r = ed.record;
50161         var field = this.colModel.getDataIndex(ed.col);
50162         var e = {
50163             grid: this,
50164             record: r,
50165             field: field,
50166             originalValue: startValue,
50167             value: value,
50168             row: ed.row,
50169             column: ed.col,
50170             cancel:false,
50171             editor: ed
50172         };
50173         if(String(value) !== String(startValue)){
50174             
50175             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50176                 r.set(field, e.value);
50177                 // if we are dealing with a combo box..
50178                 // then we also set the 'name' colum to be the displayField
50179                 if (ed.field.displayField && ed.field.name) {
50180                     r.set(ed.field.name, ed.field.el.dom.value);
50181                 }
50182                 
50183                 delete e.cancel; //?? why!!!
50184                 this.fireEvent("afteredit", e);
50185             }
50186         } else {
50187             this.fireEvent("afteredit", e); // always fire it!
50188         }
50189         this.view.focusCell(ed.row, ed.col);
50190     },
50191
50192     /**
50193      * Starts editing the specified for the specified row/column
50194      * @param {Number} rowIndex
50195      * @param {Number} colIndex
50196      */
50197     startEditing : function(row, col){
50198         this.stopEditing();
50199         if(this.colModel.isCellEditable(col, row)){
50200             this.view.ensureVisible(row, col, true);
50201             var r = this.dataSource.getAt(row);
50202             var field = this.colModel.getDataIndex(col);
50203             var e = {
50204                 grid: this,
50205                 record: r,
50206                 field: field,
50207                 value: r.data[field],
50208                 row: row,
50209                 column: col,
50210                 cancel:false
50211             };
50212             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50213                 this.editing = true;
50214                 var ed = this.colModel.getCellEditor(col, row);
50215                 
50216                 if (!ed) {
50217                     return;
50218                 }
50219                 if(!ed.rendered){
50220                     ed.render(ed.parentEl || document.body);
50221                 }
50222                 ed.field.reset();
50223                 (function(){ // complex but required for focus issues in safari, ie and opera
50224                     ed.row = row;
50225                     ed.col = col;
50226                     ed.record = r;
50227                     ed.on("complete", this.onEditComplete, this, {single: true});
50228                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50229                     this.activeEditor = ed;
50230                     var v = r.data[field];
50231                     ed.startEdit(this.view.getCell(row, col), v);
50232                     // combo's with 'displayField and name set
50233                     if (ed.field.displayField && ed.field.name) {
50234                         ed.field.el.dom.value = r.data[ed.field.name];
50235                     }
50236                     
50237                     
50238                 }).defer(50, this);
50239             }
50240         }
50241     },
50242         
50243     /**
50244      * Stops any active editing
50245      */
50246     stopEditing : function(){
50247         if(this.activeEditor){
50248             this.activeEditor.completeEdit();
50249         }
50250         this.activeEditor = null;
50251     }
50252 });/*
50253  * Based on:
50254  * Ext JS Library 1.1.1
50255  * Copyright(c) 2006-2007, Ext JS, LLC.
50256  *
50257  * Originally Released Under LGPL - original licence link has changed is not relivant.
50258  *
50259  * Fork - LGPL
50260  * <script type="text/javascript">
50261  */
50262
50263 // private - not really -- you end up using it !
50264 // This is a support class used internally by the Grid components
50265
50266 /**
50267  * @class Roo.grid.GridEditor
50268  * @extends Roo.Editor
50269  * Class for creating and editable grid elements.
50270  * @param {Object} config any settings (must include field)
50271  */
50272 Roo.grid.GridEditor = function(field, config){
50273     if (!config && field.field) {
50274         config = field;
50275         field = Roo.factory(config.field, Roo.form);
50276     }
50277     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50278     field.monitorTab = false;
50279 };
50280
50281 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50282     
50283     /**
50284      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50285      */
50286     
50287     alignment: "tl-tl",
50288     autoSize: "width",
50289     hideEl : false,
50290     cls: "x-small-editor x-grid-editor",
50291     shim:false,
50292     shadow:"frame"
50293 });/*
50294  * Based on:
50295  * Ext JS Library 1.1.1
50296  * Copyright(c) 2006-2007, Ext JS, LLC.
50297  *
50298  * Originally Released Under LGPL - original licence link has changed is not relivant.
50299  *
50300  * Fork - LGPL
50301  * <script type="text/javascript">
50302  */
50303   
50304
50305   
50306 Roo.grid.PropertyRecord = Roo.data.Record.create([
50307     {name:'name',type:'string'},  'value'
50308 ]);
50309
50310
50311 Roo.grid.PropertyStore = function(grid, source){
50312     this.grid = grid;
50313     this.store = new Roo.data.Store({
50314         recordType : Roo.grid.PropertyRecord
50315     });
50316     this.store.on('update', this.onUpdate,  this);
50317     if(source){
50318         this.setSource(source);
50319     }
50320     Roo.grid.PropertyStore.superclass.constructor.call(this);
50321 };
50322
50323
50324
50325 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50326     setSource : function(o){
50327         this.source = o;
50328         this.store.removeAll();
50329         var data = [];
50330         for(var k in o){
50331             if(this.isEditableValue(o[k])){
50332                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50333             }
50334         }
50335         this.store.loadRecords({records: data}, {}, true);
50336     },
50337
50338     onUpdate : function(ds, record, type){
50339         if(type == Roo.data.Record.EDIT){
50340             var v = record.data['value'];
50341             var oldValue = record.modified['value'];
50342             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50343                 this.source[record.id] = v;
50344                 record.commit();
50345                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50346             }else{
50347                 record.reject();
50348             }
50349         }
50350     },
50351
50352     getProperty : function(row){
50353        return this.store.getAt(row);
50354     },
50355
50356     isEditableValue: function(val){
50357         if(val && val instanceof Date){
50358             return true;
50359         }else if(typeof val == 'object' || typeof val == 'function'){
50360             return false;
50361         }
50362         return true;
50363     },
50364
50365     setValue : function(prop, value){
50366         this.source[prop] = value;
50367         this.store.getById(prop).set('value', value);
50368     },
50369
50370     getSource : function(){
50371         return this.source;
50372     }
50373 });
50374
50375 Roo.grid.PropertyColumnModel = function(grid, store){
50376     this.grid = grid;
50377     var g = Roo.grid;
50378     g.PropertyColumnModel.superclass.constructor.call(this, [
50379         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50380         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50381     ]);
50382     this.store = store;
50383     this.bselect = Roo.DomHelper.append(document.body, {
50384         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50385             {tag: 'option', value: 'true', html: 'true'},
50386             {tag: 'option', value: 'false', html: 'false'}
50387         ]
50388     });
50389     Roo.id(this.bselect);
50390     var f = Roo.form;
50391     this.editors = {
50392         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50393         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50394         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50395         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50396         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50397     };
50398     this.renderCellDelegate = this.renderCell.createDelegate(this);
50399     this.renderPropDelegate = this.renderProp.createDelegate(this);
50400 };
50401
50402 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50403     
50404     
50405     nameText : 'Name',
50406     valueText : 'Value',
50407     
50408     dateFormat : 'm/j/Y',
50409     
50410     
50411     renderDate : function(dateVal){
50412         return dateVal.dateFormat(this.dateFormat);
50413     },
50414
50415     renderBool : function(bVal){
50416         return bVal ? 'true' : 'false';
50417     },
50418
50419     isCellEditable : function(colIndex, rowIndex){
50420         return colIndex == 1;
50421     },
50422
50423     getRenderer : function(col){
50424         return col == 1 ?
50425             this.renderCellDelegate : this.renderPropDelegate;
50426     },
50427
50428     renderProp : function(v){
50429         return this.getPropertyName(v);
50430     },
50431
50432     renderCell : function(val){
50433         var rv = val;
50434         if(val instanceof Date){
50435             rv = this.renderDate(val);
50436         }else if(typeof val == 'boolean'){
50437             rv = this.renderBool(val);
50438         }
50439         return Roo.util.Format.htmlEncode(rv);
50440     },
50441
50442     getPropertyName : function(name){
50443         var pn = this.grid.propertyNames;
50444         return pn && pn[name] ? pn[name] : name;
50445     },
50446
50447     getCellEditor : function(colIndex, rowIndex){
50448         var p = this.store.getProperty(rowIndex);
50449         var n = p.data['name'], val = p.data['value'];
50450         
50451         if(typeof(this.grid.customEditors[n]) == 'string'){
50452             return this.editors[this.grid.customEditors[n]];
50453         }
50454         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50455             return this.grid.customEditors[n];
50456         }
50457         if(val instanceof Date){
50458             return this.editors['date'];
50459         }else if(typeof val == 'number'){
50460             return this.editors['number'];
50461         }else if(typeof val == 'boolean'){
50462             return this.editors['boolean'];
50463         }else{
50464             return this.editors['string'];
50465         }
50466     }
50467 });
50468
50469 /**
50470  * @class Roo.grid.PropertyGrid
50471  * @extends Roo.grid.EditorGrid
50472  * This class represents the  interface of a component based property grid control.
50473  * <br><br>Usage:<pre><code>
50474  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50475       
50476  });
50477  // set any options
50478  grid.render();
50479  * </code></pre>
50480   
50481  * @constructor
50482  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50483  * The container MUST have some type of size defined for the grid to fill. The container will be
50484  * automatically set to position relative if it isn't already.
50485  * @param {Object} config A config object that sets properties on this grid.
50486  */
50487 Roo.grid.PropertyGrid = function(container, config){
50488     config = config || {};
50489     var store = new Roo.grid.PropertyStore(this);
50490     this.store = store;
50491     var cm = new Roo.grid.PropertyColumnModel(this, store);
50492     store.store.sort('name', 'ASC');
50493     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50494         ds: store.store,
50495         cm: cm,
50496         enableColLock:false,
50497         enableColumnMove:false,
50498         stripeRows:false,
50499         trackMouseOver: false,
50500         clicksToEdit:1
50501     }, config));
50502     this.getGridEl().addClass('x-props-grid');
50503     this.lastEditRow = null;
50504     this.on('columnresize', this.onColumnResize, this);
50505     this.addEvents({
50506          /**
50507              * @event beforepropertychange
50508              * Fires before a property changes (return false to stop?)
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         "beforepropertychange": true,
50515         /**
50516              * @event propertychange
50517              * Fires after a property changes
50518              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50519              * @param {String} id Record Id
50520              * @param {String} newval New Value
50521          * @param {String} oldval Old Value
50522              */
50523         "propertychange": true
50524     });
50525     this.customEditors = this.customEditors || {};
50526 };
50527 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50528     
50529      /**
50530      * @cfg {Object} customEditors map of colnames=> custom editors.
50531      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50532      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50533      * false disables editing of the field.
50534          */
50535     
50536       /**
50537      * @cfg {Object} propertyNames map of property Names to their displayed value
50538          */
50539     
50540     render : function(){
50541         Roo.grid.PropertyGrid.superclass.render.call(this);
50542         this.autoSize.defer(100, this);
50543     },
50544
50545     autoSize : function(){
50546         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50547         if(this.view){
50548             this.view.fitColumns();
50549         }
50550     },
50551
50552     onColumnResize : function(){
50553         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50554         this.autoSize();
50555     },
50556     /**
50557      * Sets the data for the Grid
50558      * accepts a Key => Value object of all the elements avaiable.
50559      * @param {Object} data  to appear in grid.
50560      */
50561     setSource : function(source){
50562         this.store.setSource(source);
50563         //this.autoSize();
50564     },
50565     /**
50566      * Gets all the data from the grid.
50567      * @return {Object} data  data stored in grid
50568      */
50569     getSource : function(){
50570         return this.store.getSource();
50571     }
50572 });/*
50573  * Based on:
50574  * Ext JS Library 1.1.1
50575  * Copyright(c) 2006-2007, Ext JS, LLC.
50576  *
50577  * Originally Released Under LGPL - original licence link has changed is not relivant.
50578  *
50579  * Fork - LGPL
50580  * <script type="text/javascript">
50581  */
50582  
50583 /**
50584  * @class Roo.LoadMask
50585  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50586  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50587  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50588  * element's UpdateManager load indicator and will be destroyed after the initial load.
50589  * @constructor
50590  * Create a new LoadMask
50591  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50592  * @param {Object} config The config object
50593  */
50594 Roo.LoadMask = function(el, config){
50595     this.el = Roo.get(el);
50596     Roo.apply(this, config);
50597     if(this.store){
50598         this.store.on('beforeload', this.onBeforeLoad, this);
50599         this.store.on('load', this.onLoad, this);
50600         this.store.on('loadexception', this.onLoad, this);
50601         this.removeMask = false;
50602     }else{
50603         var um = this.el.getUpdateManager();
50604         um.showLoadIndicator = false; // disable the default indicator
50605         um.on('beforeupdate', this.onBeforeLoad, this);
50606         um.on('update', this.onLoad, this);
50607         um.on('failure', this.onLoad, this);
50608         this.removeMask = true;
50609     }
50610 };
50611
50612 Roo.LoadMask.prototype = {
50613     /**
50614      * @cfg {Boolean} removeMask
50615      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50616      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50617      */
50618     /**
50619      * @cfg {String} msg
50620      * The text to display in a centered loading message box (defaults to 'Loading...')
50621      */
50622     msg : 'Loading...',
50623     /**
50624      * @cfg {String} msgCls
50625      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50626      */
50627     msgCls : 'x-mask-loading',
50628
50629     /**
50630      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50631      * @type Boolean
50632      */
50633     disabled: false,
50634
50635     /**
50636      * Disables the mask to prevent it from being displayed
50637      */
50638     disable : function(){
50639        this.disabled = true;
50640     },
50641
50642     /**
50643      * Enables the mask so that it can be displayed
50644      */
50645     enable : function(){
50646         this.disabled = false;
50647     },
50648
50649     // private
50650     onLoad : function(){
50651         this.el.unmask(this.removeMask);
50652     },
50653
50654     // private
50655     onBeforeLoad : function(){
50656         if(!this.disabled){
50657             this.el.mask(this.msg, this.msgCls);
50658         }
50659     },
50660
50661     // private
50662     destroy : function(){
50663         if(this.store){
50664             this.store.un('beforeload', this.onBeforeLoad, this);
50665             this.store.un('load', this.onLoad, this);
50666             this.store.un('loadexception', this.onLoad, this);
50667         }else{
50668             var um = this.el.getUpdateManager();
50669             um.un('beforeupdate', this.onBeforeLoad, this);
50670             um.un('update', this.onLoad, this);
50671             um.un('failure', this.onLoad, this);
50672         }
50673     }
50674 };/*
50675  * Based on:
50676  * Ext JS Library 1.1.1
50677  * Copyright(c) 2006-2007, Ext JS, LLC.
50678  *
50679  * Originally Released Under LGPL - original licence link has changed is not relivant.
50680  *
50681  * Fork - LGPL
50682  * <script type="text/javascript">
50683  */
50684 Roo.XTemplate = function(){
50685     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50686     var s = this.html;
50687
50688     s = ['<tpl>', s, '</tpl>'].join('');
50689
50690     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50691
50692     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50693     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50694     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50695     var m, id = 0;
50696     var tpls = [];
50697
50698     while(m = s.match(re)){
50699        var m2 = m[0].match(nameRe);
50700        var m3 = m[0].match(ifRe);
50701        var m4 = m[0].match(execRe);
50702        var exp = null, fn = null, exec = null;
50703        var name = m2 && m2[1] ? m2[1] : '';
50704        if(m3){
50705            exp = m3 && m3[1] ? m3[1] : null;
50706            if(exp){
50707                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50708            }
50709        }
50710        if(m4){
50711            exp = m4 && m4[1] ? m4[1] : null;
50712            if(exp){
50713                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50714            }
50715        }
50716        if(name){
50717            switch(name){
50718                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50719                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50720                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50721            }
50722        }
50723        tpls.push({
50724             id: id,
50725             target: name,
50726             exec: exec,
50727             test: fn,
50728             body: m[1]||''
50729         });
50730        s = s.replace(m[0], '{xtpl'+ id + '}');
50731        ++id;
50732     }
50733     for(var i = tpls.length-1; i >= 0; --i){
50734         this.compileTpl(tpls[i]);
50735     }
50736     this.master = tpls[tpls.length-1];
50737     this.tpls = tpls;
50738 };
50739 Roo.extend(Roo.XTemplate, Roo.Template, {
50740
50741     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50742
50743     applySubTemplate : function(id, values, parent){
50744         var t = this.tpls[id];
50745         if(t.test && !t.test.call(this, values, parent)){
50746             return '';
50747         }
50748         if(t.exec && t.exec.call(this, values, parent)){
50749             return '';
50750         }
50751         var vs = t.target ? t.target.call(this, values, parent) : values;
50752         parent = t.target ? values : parent;
50753         if(t.target && vs instanceof Array){
50754             var buf = [];
50755             for(var i = 0, len = vs.length; i < len; i++){
50756                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50757             }
50758             return buf.join('');
50759         }
50760         return t.compiled.call(this, vs, parent);
50761     },
50762
50763     compileTpl : function(tpl){
50764         var fm = Roo.util.Format;
50765         var useF = this.disableFormats !== true;
50766         var sep = Roo.isGecko ? "+" : ",";
50767         var fn = function(m, name, format, args){
50768             if(name.substr(0, 4) == 'xtpl'){
50769                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50770             }
50771             var v;
50772             if(name.indexOf('.') != -1){
50773                 v = name;
50774             }else{
50775                 v = "values['" + name + "']";
50776             }
50777             if(format && useF){
50778                 args = args ? ',' + args : "";
50779                 if(format.substr(0, 5) != "this."){
50780                     format = "fm." + format + '(';
50781                 }else{
50782                     format = 'this.call("'+ format.substr(5) + '", ';
50783                     args = ", values";
50784                 }
50785             }else{
50786                 args= ''; format = "("+v+" === undefined ? '' : ";
50787             }
50788             return "'"+ sep + format + v + args + ")"+sep+"'";
50789         };
50790         var body;
50791         // branched to use + in gecko and [].join() in others
50792         if(Roo.isGecko){
50793             body = "tpl.compiled = function(values, parent){ return '" +
50794                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50795                     "';};";
50796         }else{
50797             body = ["tpl.compiled = function(values, parent){ return ['"];
50798             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50799             body.push("'].join('');};");
50800             body = body.join('');
50801         }
50802         /** eval:var:zzzzzzz */
50803         eval(body);
50804         return this;
50805     },
50806
50807     applyTemplate : function(values){
50808         return this.master.compiled.call(this, values, {});
50809         var s = this.subs;
50810     },
50811
50812     apply : function(){
50813         return this.applyTemplate.apply(this, arguments);
50814     },
50815
50816     compile : function(){return this;}
50817 });
50818
50819 Roo.XTemplate.from = function(el){
50820     el = Roo.getDom(el);
50821     return new Roo.XTemplate(el.value || el.innerHTML);
50822 };/*
50823  * Original code for Roojs - LGPL
50824  * <script type="text/javascript">
50825  */
50826  
50827 /**
50828  * @class Roo.XComponent
50829  * A delayed Element creator...
50830  * 
50831  * Mypart.xyx = new Roo.XComponent({
50832
50833     parent : 'Mypart.xyz', // empty == document.element.!!
50834     order : '001',
50835     name : 'xxxx'
50836     region : 'xxxx'
50837     disabled : function() {} 
50838      
50839     tree : function() { // return an tree of xtype declared components
50840         var MODULE = this;
50841         return 
50842         {
50843             xtype : 'NestedLayoutPanel',
50844             // technicall
50845         }
50846      ]
50847  *})
50848  * @extends Roo.util.Observable
50849  * @constructor
50850  * @param cfg {Object} configuration of component
50851  * 
50852  */
50853 Roo.XComponent = function(cfg) {
50854     Roo.apply(this, cfg);
50855     this.addEvents({ 
50856         /**
50857              * @event built
50858              * Fires when this the componnt is built
50859              * @param {Roo.XComponent} c the component
50860              */
50861         'built' : true,
50862         /**
50863              * @event buildcomplete
50864              * Fires on the top level element when all elements have been built
50865              * @param {Roo.XComponent} c the top level component.
50866          */
50867         'buildcomplete' : true
50868         
50869     });
50870     
50871     Roo.XComponent.register(this);
50872     this.modules = false;
50873     this.el = false; // where the layout goes..
50874     
50875     
50876 }
50877 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50878     /**
50879      * @property el
50880      * The created element (with Roo.factory())
50881      * @type {Roo.Layout}
50882      */
50883     el  : false,
50884     
50885     /**
50886      * @property el
50887      * for BC  - use el in new code
50888      * @type {Roo.Layout}
50889      */
50890     panel : false,
50891     
50892     /**
50893      * @property layout
50894      * for BC  - use el in new code
50895      * @type {Roo.Layout}
50896      */
50897     layout : false,
50898     
50899      /**
50900      * @cfg {Function|boolean} disabled
50901      * If this module is disabled by some rule, return true from the funtion
50902      */
50903     disabled : false,
50904     
50905     /**
50906      * @cfg {String} parent 
50907      * Name of parent element which it get xtype added to..
50908      */
50909     parent: false,
50910     
50911     /**
50912      * @cfg {String} order
50913      * Used to set the order in which elements are created (usefull for multiple tabs)
50914      */
50915     
50916     order : false,
50917     /**
50918      * @cfg {String} name
50919      * String to display while loading.
50920      */
50921     name : false,
50922     /**
50923      * @cfg {Array} items
50924      * A single item array - the first element is the root of the tree..
50925      * It's done this way to stay compatible with the Xtype system...
50926      */
50927     items : false
50928      
50929      
50930     
50931 });
50932
50933 Roo.apply(Roo.XComponent, {
50934     
50935     /**
50936      * @property  buildCompleted
50937      * True when the builder has completed building the interface.
50938      * @type Boolean
50939      */
50940     buildCompleted : false,
50941      
50942     /**
50943      * @property  topModule
50944      * the upper most module - uses document.element as it's constructor.
50945      * @type Object
50946      */
50947      
50948     topModule  : false,
50949       
50950     /**
50951      * @property  modules
50952      * array of modules to be created by registration system.
50953      * @type Roo.XComponent
50954      */
50955     
50956     modules : [],
50957       
50958     
50959     /**
50960      * Register components to be built later.
50961      *
50962      * This solves the following issues
50963      * - Building is not done on page load, but after an authentication process has occured.
50964      * - Interface elements are registered on page load
50965      * - Parent Interface elements may not be loaded before child, so this handles that..
50966      * 
50967      *
50968      * example:
50969      * 
50970      * MyApp.register({
50971           order : '000001',
50972           module : 'Pman.Tab.projectMgr',
50973           region : 'center',
50974           parent : 'Pman.layout',
50975           disabled : false,  // or use a function..
50976         })
50977      
50978      * * @param {Object} details about module
50979      */
50980     register : function(obj) {
50981         this.modules.push(obj);
50982          
50983     },
50984     /**
50985      * convert a string to an object..
50986      * 
50987      */
50988     
50989     toObject : function(str)
50990     {
50991         if (!str || typeof(str) == 'object') {
50992             return str;
50993         }
50994         var ar = str.split('.');
50995         var rt, o;
50996         rt = ar.shift();
50997             /** eval:var:o */
50998         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50999         if (o === false) {
51000             throw "Module not found : " + str;
51001         }
51002         Roo.each(ar, function(e) {
51003             if (typeof(o[e]) == 'undefined') {
51004                 throw "Module not found : " + str;
51005             }
51006             o = o[e];
51007         });
51008         return o;
51009         
51010     },
51011     
51012     
51013     /**
51014      * move modules into their correct place in the tree..
51015      * 
51016      */
51017     preBuild : function ()
51018     {
51019         
51020         Roo.each(this.modules , function (obj)
51021         {
51022             obj.parent = this.toObject(obj.parent);
51023             
51024             if (!obj.parent) {
51025                 this.topModule = obj;
51026                 return;
51027             }
51028             
51029             if (!obj.parent.modules) {
51030                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51031                     function(o) { return o.order + '' }
51032                 );
51033             }
51034             
51035             obj.parent.modules.add(obj);
51036         }, this);
51037     },
51038     
51039      /**
51040      * make a list of modules to build.
51041      * @return {Array} list of modules. 
51042      */ 
51043     
51044     buildOrder : function()
51045     {
51046         var _this = this;
51047         var cmp = function(a,b) {   
51048             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51049         };
51050         
51051         if (!this.topModule || !this.topModule.modules) {
51052             throw "No top level modules to build";
51053         }
51054        
51055         // make a flat list in order of modules to build.
51056         var mods = [ this.topModule ];
51057         
51058         
51059         // add modules to their parents..
51060         var addMod = function(m) {
51061            // Roo.debug && Roo.log(m.modKey);
51062             
51063             mods.push(m);
51064             if (m.modules) {
51065                 m.modules.keySort('ASC',  cmp );
51066                 m.modules.each(addMod);
51067             }
51068             // not sure if this is used any more..
51069             if (m.finalize) {
51070                 m.finalize.name = m.name + " (clean up) ";
51071                 mods.push(m.finalize);
51072             }
51073             
51074         }
51075         this.topModule.modules.keySort('ASC',  cmp );
51076         this.topModule.modules.each(addMod);
51077         return mods;
51078     },
51079     
51080      /**
51081      * Build the registered modules.
51082      * @param {Object} parent element.
51083      * @param {Function} optional method to call after module has been added.
51084      * 
51085      */ 
51086    
51087     build : function() 
51088     {
51089         
51090         this.preBuild();
51091         var mods = this.buildOrder();
51092       
51093         //this.allmods = mods;
51094         //Roo.debug && Roo.log(mods);
51095         //return;
51096         if (!mods.length) { // should not happen
51097             throw "NO modules!!!";
51098         }
51099         
51100         
51101         
51102         // flash it up as modal - so we store the mask!?
51103         Roo.MessageBox.show({ title: 'loading' });
51104         Roo.MessageBox.show({
51105            title: "Please wait...",
51106            msg: "Building Interface...",
51107            width:450,
51108            progress:true,
51109            closable:false,
51110            modal: false
51111           
51112         });
51113         var total = mods.length;
51114         
51115         var _this = this;
51116         var progressRun = function() {
51117             if (!mods.length) {
51118                 Roo.debug && Roo.log('hide?');
51119                 Roo.MessageBox.hide();
51120                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
51121                 return;    
51122             }
51123             
51124             var m = mods.shift();
51125             Roo.debug && Roo.log(m);
51126             if (typeof(m) == 'function') { // not sure if this is supported any more..
51127                 m.call(this);
51128                 return progressRun.defer(10, _this);
51129             } 
51130             
51131             Roo.MessageBox.updateProgress(
51132                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51133                     " of " + total + 
51134                     (m.name ? (' - ' + m.name) : '')
51135                     );
51136             
51137          
51138             
51139             var disabled = (typeof(m.disabled) == 'function') ?
51140                 m.disabled.call(m.module.disabled) : m.disabled;    
51141             
51142             
51143             if (disabled) {
51144                 return progressRun(); // we do not update the display!
51145             }
51146             
51147             if (!m.parent) {
51148                 // it's a top level one..
51149                 var layoutbase = new Ext.BorderLayout(document.body, {
51150                
51151                     center: {
51152                          titlebar: false,
51153                          autoScroll:false,
51154                          closeOnTab: true,
51155                          tabPosition: 'top',
51156                          //resizeTabs: true,
51157                          alwaysShowTabs: true,
51158                          minTabWidth: 140
51159                     }
51160                 });
51161                 var tree = m.tree();
51162                 tree.region = 'center';
51163                 m.el = layoutbase.addxtype(tree);
51164                 m.panel = m.el;
51165                 m.layout = m.panel.layout;    
51166                 return progressRun.defer(10, _this);
51167             }
51168             
51169             var tree = m.tree();
51170             tree.region = tree.region || m.region;
51171             m.el = m.parent.el.addxtype(tree);
51172             m.fireEvent('built', m);
51173             m.panel = m.el;
51174             m.layout = m.panel.layout;    
51175             progressRun.defer(10, _this); 
51176             
51177         }
51178         progressRun.defer(1, _this);
51179      
51180         
51181         
51182     }
51183      
51184    
51185     
51186     
51187 });
51188  //<script type="text/javascript">
51189
51190
51191 /**
51192  * @class Roo.Login
51193  * @extends Roo.LayoutDialog
51194  * A generic Login Dialog..... - only one needed in theory!?!?
51195  *
51196  * Fires XComponent builder on success...
51197  * 
51198  * Sends 
51199  *    username,password, lang = for login actions.
51200  *    check = 1 for periodic checking that sesion is valid.
51201  *    passwordRequest = email request password
51202  *    logout = 1 = to logout
51203  * 
51204  * Affects: (this id="????" elements)
51205  *   loading  (removed) (used to indicate application is loading)
51206  *   loading-mask (hides) (used to hide application when it's building loading)
51207  *   
51208  * 
51209  * Usage: 
51210  *    
51211  * 
51212  * Myapp.login = Roo.Login({
51213      url: xxxx,
51214    
51215      realm : 'Myapp', 
51216      
51217      
51218      method : 'POST',
51219      
51220      
51221      * 
51222  })
51223  * 
51224  * 
51225  * 
51226  **/
51227  
51228 Roo.Login = function(cfg)
51229 {
51230     this.addEvents({
51231         'refreshed' : true
51232     });
51233     
51234     Roo.apply(this,cfg);
51235     
51236     Roo.onReady(function() {
51237         this.onLoad();
51238     }, this);
51239     // call parent..
51240     
51241    
51242     Roo.Login.superclass.constructor.call(this, this);
51243     //this.addxtype(this.items[0]);
51244     
51245     
51246 }
51247
51248
51249 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51250     
51251     /**
51252      * @cfg {String} method
51253      * Method used to query for login details.
51254      */
51255     
51256     method : 'POST',
51257     /**
51258      * @cfg {String} url
51259      * URL to query login data. - eg. baseURL + '/Login.php'
51260      */
51261     url : '',
51262     
51263     /**
51264      * @property user
51265      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51266      * @type {Object} 
51267      */
51268     user : false,
51269     /**
51270      * @property checkFails
51271      * Number of times we have attempted to get authentication check, and failed.
51272      * @type {Number} 
51273      */
51274     checkFails : 0,
51275       /**
51276      * @property intervalID
51277      * The window interval that does the constant login checking.
51278      * @type {Number} 
51279      */
51280     intervalID : 0,
51281     
51282     
51283     onLoad : function() // called on page load...
51284     {
51285         // load 
51286          
51287         if (Roo.get('loading')) { // clear any loading indicator..
51288             Roo.get('loading').remove();
51289         }
51290         
51291         //this.switchLang('en'); // set the language to english..
51292        
51293         this.check({
51294             success:  function(response, opts)  {  // check successfull...
51295             
51296                 var res = this.processResponse(response);
51297                 this.checkFails =0;
51298                 if (!res.success) { // error!
51299                     this.checkFails = 5;
51300                     //console.log('call failure');
51301                     return this.failure(response,opts);
51302                 }
51303                 
51304                 if (!res.data.id) { // id=0 == login failure.
51305                     return this.show();
51306                 }
51307                 
51308                               
51309                         //console.log(success);
51310                 this.fillAuth(res.data);   
51311                 this.checkFails =0;
51312                 Roo.XComponent.build();
51313             },
51314             failure : this.show
51315         });
51316         
51317     }, 
51318     
51319     
51320     check: function(cfg) // called every so often to refresh cookie etc..
51321     {
51322         if (cfg.again) { // could be undefined..
51323             this.checkFails++;
51324         } else {
51325             this.checkFails = 0;
51326         }
51327         var _this = this;
51328         if (this.sending) {
51329             if ( this.checkFails > 4) {
51330                 Roo.MessageBox.alert("Error",  
51331                     "Error getting authentication status. - try reloading, or wait a while", function() {
51332                         _this.sending = false;
51333                     }); 
51334                 return;
51335             }
51336             cfg.again = true;
51337             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51338             return;
51339         }
51340         this.sending = true;
51341         
51342         Roo.Ajax.request({  
51343             url: this.url,
51344             params: {
51345                 getAuthUser: true
51346             },  
51347             method: this.method,
51348             success:  cfg.success || this.success,
51349             failure : cfg.failure || this.failure,
51350             scope : this,
51351             callCfg : cfg
51352               
51353         });  
51354     }, 
51355     
51356     
51357     logout: function()
51358     {
51359         window.onbeforeunload = function() { }; // false does not work for IE..
51360         this.user = false;
51361         var _this = this;
51362         
51363         Roo.Ajax.request({  
51364             url: this.url,
51365             params: {
51366                 logout: 1
51367             },  
51368             method: 'GET',
51369             failure : function() {
51370                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51371                     document.location = document.location.toString() + '?ts=' + Math.random();
51372                 });
51373                 
51374             },
51375             success : function() {
51376                 _this.user = false;
51377                 this.checkFails =0;
51378                 // fixme..
51379                 document.location = document.location.toString() + '?ts=' + Math.random();
51380             }
51381               
51382               
51383         }); 
51384     },
51385     
51386     processResponse : function (response)
51387     {
51388         var res = '';
51389         try {
51390             res = Roo.decode(response.responseText);
51391             // oops...
51392             if (typeof(res) != 'object') {
51393                 res = { success : false, errorMsg : res, errors : true };
51394             }
51395             if (typeof(res.success) == 'undefined') {
51396                 res.success = false;
51397             }
51398             
51399         } catch(e) {
51400             res = { success : false,  errorMsg : response.responseText, errors : true };
51401         }
51402         return res;
51403     },
51404     
51405     success : function(response, opts)  // check successfull...
51406     {  
51407         this.sending = false;
51408         var res = this.processResponse(response);
51409         if (!res.success) {
51410             return this.failure(response, opts);
51411         }
51412         if (!res.data || !res.data.id) {
51413             return this.failure(response,opts);
51414         }
51415         //console.log(res);
51416         this.fillAuth(res.data);
51417         
51418         this.checkFails =0;
51419         
51420     },
51421     
51422     
51423     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51424     {
51425         this.authUser = -1;
51426         this.sending = false;
51427         var res = this.processResponse(response);
51428         //console.log(res);
51429         if ( this.checkFails > 2) {
51430         
51431             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51432                 "Error getting authentication status. - try reloading"); 
51433             return;
51434         }
51435         opts.callCfg.again = true;
51436         this.check.defer(1000, this, [ opts.callCfg ]);
51437         return;  
51438     },
51439     
51440     
51441     
51442     fillAuth: function(au) {
51443         this.startAuthCheck();
51444         this.authUserId = au.id;
51445         this.authUser = au;
51446         this.lastChecked = new Date();
51447         this.fireEvent('refreshed', au);
51448         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51449         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51450         au.lang = au.lang || 'en';
51451         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51452         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51453         this.switchLang(au.lang );
51454         
51455      
51456         // open system... - -on setyp..
51457         if (this.authUserId  < 0) {
51458             Roo.MessageBox.alert("Warning", 
51459                 "This is an open system - please set up a admin user with a password.");  
51460         }
51461          
51462         //Pman.onload(); // which should do nothing if it's a re-auth result...
51463         
51464              
51465     },
51466     
51467     startAuthCheck : function() // starter for timeout checking..
51468     {
51469         if (this.intervalID) { // timer already in place...
51470             return false;
51471         }
51472         var _this = this;
51473         this.intervalID =  window.setInterval(function() {
51474               _this.check(false);
51475             }, 120000); // every 120 secs = 2mins..
51476         
51477         
51478     },
51479          
51480     
51481     switchLang : function (lang) 
51482     {
51483         _T = typeof(_T) == 'undefined' ? false : _T;
51484           if (!_T || !lang.length) {
51485             return;
51486         }
51487         
51488         if (!_T && lang != 'en') {
51489             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51490             return;
51491         }
51492         
51493         if (typeof(_T.en) == 'undefined') {
51494             _T.en = {};
51495             Roo.apply(_T.en, _T);
51496         }
51497         
51498         if (typeof(_T[lang]) == 'undefined') {
51499             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51500             return;
51501         }
51502         
51503         
51504         Roo.apply(_T, _T[lang]);
51505         // just need to set the text values for everything...
51506         var _this = this;
51507         /* this will not work ...
51508         if (this.form) { 
51509             
51510                
51511             function formLabel(name, val) {
51512                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51513             }
51514             
51515             formLabel('password', "Password"+':');
51516             formLabel('username', "Email Address"+':');
51517             formLabel('lang', "Language"+':');
51518             this.dialog.setTitle("Login");
51519             this.dialog.buttons[0].setText("Forgot Password");
51520             this.dialog.buttons[1].setText("Login");
51521         }
51522         */
51523         
51524         
51525     },
51526     
51527     
51528     title: "Login",
51529     modal: true,
51530     width:  350,
51531     //height: 230,
51532     height: 180,
51533     shadow: true,
51534     minWidth:200,
51535     minHeight:180,
51536     //proxyDrag: true,
51537     closable: false,
51538     draggable: false,
51539     collapsible: false,
51540     resizable: false,
51541     center: {  // needed??
51542         autoScroll:false,
51543         titlebar: false,
51544        // tabPosition: 'top',
51545         hideTabs: true,
51546         closeOnTab: true,
51547         alwaysShowTabs: false
51548     } ,
51549     listeners : {
51550         
51551         show  : function(dlg)
51552         {
51553             //console.log(this);
51554             this.form = this.layout.getRegion('center').activePanel.form;
51555             this.form.dialog = dlg;
51556             this.buttons[0].form = this.form;
51557             this.buttons[0].dialog = dlg;
51558             this.buttons[1].form = this.form;
51559             this.buttons[1].dialog = dlg;
51560            
51561            //this.resizeToLogo.defer(1000,this);
51562             // this is all related to resizing for logos..
51563             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51564            //// if (!sz) {
51565              //   this.resizeToLogo.defer(1000,this);
51566              //   return;
51567            // }
51568             //var w = Ext.lib.Dom.getViewWidth() - 100;
51569             //var h = Ext.lib.Dom.getViewHeight() - 100;
51570             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51571             //this.center();
51572             if (this.disabled) {
51573                 this.hide();
51574                 return;
51575             }
51576             
51577             if (this.user.id < 0) { // used for inital setup situations.
51578                 return;
51579             }
51580             
51581             if (this.intervalID) {
51582                 // remove the timer
51583                 window.clearInterval(this.intervalID);
51584                 this.intervalID = false;
51585             }
51586             
51587             
51588             if (Roo.get('loading')) {
51589                 Roo.get('loading').remove();
51590             }
51591             if (Roo.get('loading-mask')) {
51592                 Roo.get('loading-mask').hide();
51593             }
51594             
51595             //incomming._node = tnode;
51596             this.form.reset();
51597             //this.dialog.modal = !modal;
51598             //this.dialog.show();
51599             this.el.unmask(); 
51600             
51601             
51602             this.form.setValues({
51603                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51604                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51605             });
51606             
51607             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51608             if (this.form.findField('username').getValue().length > 0 ){
51609                 this.form.findField('password').focus();
51610             } else {
51611                this.form.findField('username').focus();
51612             }
51613     
51614         }
51615     },
51616     items : [
51617          {
51618        
51619             xtype : 'ContentPanel',
51620             xns : Roo,
51621             region: 'center',
51622             fitToFrame : true,
51623             
51624             items : [
51625     
51626                 {
51627                
51628                     xtype : 'Form',
51629                     xns : Roo.form,
51630                     labelWidth: 100,
51631                     style : 'margin: 10px;',
51632                     
51633                     listeners : {
51634                         actionfailed : function(f, act) {
51635                             // form can return { errors: .... }
51636                                 
51637                             //act.result.errors // invalid form element list...
51638                             //act.result.errorMsg// invalid form element list...
51639                             
51640                             this.dialog.el.unmask();
51641                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51642                                         "Login failed - communication error - try again.");
51643                                       
51644                         },
51645                         actioncomplete: function(re, act) {
51646                              
51647                             Roo.state.Manager.set(
51648                                 this.dialog.realm + '.username',  
51649                                     this.findField('username').getValue()
51650                             );
51651                             Roo.state.Manager.set(
51652                                 this.dialog.realm + '.lang',  
51653                                 this.findField('lang').getValue() 
51654                             );
51655                             
51656                             this.dialog.fillAuth(act.result.data);
51657                               
51658                             this.dialog.hide();
51659                             
51660                             if (Roo.get('loading-mask')) {
51661                                 Roo.get('loading-mask').show();
51662                             }
51663                             Roo.XComponent.build();
51664                             
51665                              
51666                             
51667                         }
51668                     },
51669                     items : [
51670                         {
51671                             xtype : 'TextField',
51672                             xns : Roo.form,
51673                             fieldLabel: "Email Address",
51674                             name: 'username',
51675                             width:200,
51676                             autoCreate : {tag: "input", type: "text", size: "20"}
51677                         },
51678                         {
51679                             xtype : 'TextField',
51680                             xns : Roo.form,
51681                             fieldLabel: "Password",
51682                             inputType: 'password',
51683                             name: 'password',
51684                             width:200,
51685                             autoCreate : {tag: "input", type: "text", size: "20"},
51686                             listeners : {
51687                                 specialkey : function(e,ev) {
51688                                     if (ev.keyCode == 13) {
51689                                         this.form.dialog.el.mask("Logging in");
51690                                         this.form.doAction('submit', {
51691                                             url: this.form.dialog.url,
51692                                             method: this.form.dialog.method
51693                                         });
51694                                     }
51695                                 }
51696                             }  
51697                         },
51698                         {
51699                             xtype : 'ComboBox',
51700                             xns : Roo.form,
51701                             fieldLabel: "Language",
51702                             name : 'langdisp',
51703                             store: {
51704                                 xtype : 'SimpleStore',
51705                                 fields: ['lang', 'ldisp'],
51706                                 data : [
51707                                     [ 'en', 'English' ],
51708                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51709                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51710                                 ]
51711                             },
51712                             
51713                             valueField : 'lang',
51714                             hiddenName:  'lang',
51715                             width: 200,
51716                             displayField:'ldisp',
51717                             typeAhead: false,
51718                             editable: false,
51719                             mode: 'local',
51720                             triggerAction: 'all',
51721                             emptyText:'Select a Language...',
51722                             selectOnFocus:true,
51723                             listeners : {
51724                                 select :  function(cb, rec, ix) {
51725                                     this.form.switchLang(rec.data.lang);
51726                                 }
51727                             }
51728                         
51729                         }
51730                     ]
51731                 }
51732                   
51733                 
51734             ]
51735         }
51736     ],
51737     buttons : [
51738         {
51739             xtype : 'Button',
51740             xns : 'Roo',
51741             text : "Forgot Password",
51742             listeners : {
51743                 click : function() {
51744                     //console.log(this);
51745                     var n = this.form.findField('username').getValue();
51746                     if (!n.length) {
51747                         Roo.MessageBox.alert("Error", "Fill in your email address");
51748                         return;
51749                     }
51750                     Roo.Ajax.request({
51751                         url: this.dialog.url,
51752                         params: {
51753                             passwordRequest: n
51754                         },
51755                         method: this.dialog.method,
51756                         success:  function(response, opts)  {  // check successfull...
51757                         
51758                             var res = this.dialog.processResponse(response);
51759                             if (!res.success) { // error!
51760                                Roo.MessageBox.alert("Error" ,
51761                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51762                                return;
51763                             }
51764                             Roo.MessageBox.alert("Notice" ,
51765                                 "Please check you email for the Password Reset message");
51766                         },
51767                         failure : function() {
51768                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51769                         }
51770                         
51771                     });
51772                 }
51773             }
51774         },
51775         {
51776             xtype : 'Button',
51777             xns : 'Roo',
51778             text : "Login",
51779             listeners : {
51780                 
51781                 click : function () {
51782                         
51783                     this.dialog.el.mask("Logging in");
51784                     this.form.doAction('submit', {
51785                             url: this.dialog.url,
51786                             method: this.dialog.method
51787                     });
51788                 }
51789             }
51790         }
51791     ]
51792   
51793   
51794 })
51795  
51796
51797
51798