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 = JSON && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12829 /** 
12830  * Shorthand for {@link Roo.util.JSON#decode}
12831  * @member Roo decode 
12832  * @method */
12833 Roo.decode = JSON && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12834 /*
12835  * Based on:
12836  * Ext JS Library 1.1.1
12837  * Copyright(c) 2006-2007, Ext JS, LLC.
12838  *
12839  * Originally Released Under LGPL - original licence link has changed is not relivant.
12840  *
12841  * Fork - LGPL
12842  * <script type="text/javascript">
12843  */
12844  
12845 /**
12846  * @class Roo.util.Format
12847  * Reusable data formatting functions
12848  * @singleton
12849  */
12850 Roo.util.Format = function(){
12851     var trimRe = /^\s+|\s+$/g;
12852     return {
12853         /**
12854          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12855          * @param {String} value The string to truncate
12856          * @param {Number} length The maximum length to allow before truncating
12857          * @return {String} The converted text
12858          */
12859         ellipsis : function(value, len){
12860             if(value && value.length > len){
12861                 return value.substr(0, len-3)+"...";
12862             }
12863             return value;
12864         },
12865
12866         /**
12867          * Checks a reference and converts it to empty string if it is undefined
12868          * @param {Mixed} value Reference to check
12869          * @return {Mixed} Empty string if converted, otherwise the original value
12870          */
12871         undef : function(value){
12872             return typeof value != "undefined" ? value : "";
12873         },
12874
12875         /**
12876          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12877          * @param {String} value The string to encode
12878          * @return {String} The encoded text
12879          */
12880         htmlEncode : function(value){
12881             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12882         },
12883
12884         /**
12885          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12886          * @param {String} value The string to decode
12887          * @return {String} The decoded text
12888          */
12889         htmlDecode : function(value){
12890             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12891         },
12892
12893         /**
12894          * Trims any whitespace from either side of a string
12895          * @param {String} value The text to trim
12896          * @return {String} The trimmed text
12897          */
12898         trim : function(value){
12899             return String(value).replace(trimRe, "");
12900         },
12901
12902         /**
12903          * Returns a substring from within an original string
12904          * @param {String} value The original text
12905          * @param {Number} start The start index of the substring
12906          * @param {Number} length The length of the substring
12907          * @return {String} The substring
12908          */
12909         substr : function(value, start, length){
12910             return String(value).substr(start, length);
12911         },
12912
12913         /**
12914          * Converts a string to all lower case letters
12915          * @param {String} value The text to convert
12916          * @return {String} The converted text
12917          */
12918         lowercase : function(value){
12919             return String(value).toLowerCase();
12920         },
12921
12922         /**
12923          * Converts a string to all upper case letters
12924          * @param {String} value The text to convert
12925          * @return {String} The converted text
12926          */
12927         uppercase : function(value){
12928             return String(value).toUpperCase();
12929         },
12930
12931         /**
12932          * Converts the first character only of a string to upper case
12933          * @param {String} value The text to convert
12934          * @return {String} The converted text
12935          */
12936         capitalize : function(value){
12937             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12938         },
12939
12940         // private
12941         call : function(value, fn){
12942             if(arguments.length > 2){
12943                 var args = Array.prototype.slice.call(arguments, 2);
12944                 args.unshift(value);
12945                  
12946                 return /** eval:var:value */  eval(fn).apply(window, args);
12947             }else{
12948                 /** eval:var:value */
12949                 return /** eval:var:value */ eval(fn).call(window, value);
12950             }
12951         },
12952
12953        
12954         /**
12955          * safer version of Math.toFixed..??/
12956          * @param {Number/String} value The numeric value to format
12957          * @param {Number/String} value Decimal places 
12958          * @return {String} The formatted currency string
12959          */
12960         toFixed : function(v, n)
12961         {
12962             // why not use to fixed - precision is buggered???
12963             if (!n) {
12964                 return Math.round(v-0);
12965             }
12966             var fact = Math.pow(10,n+1);
12967             v = (Math.round((v-0)*fact))/fact;
12968             var z = (''+fact).substring(2);
12969             if (v == Math.floor(v)) {
12970                 return Math.floor(v) + '.' + z;
12971             }
12972             
12973             // now just padd decimals..
12974             var ps = String(v).split('.');
12975             var fd = (ps[1] + z);
12976             var r = fd.substring(0,n); 
12977             var rm = fd.substring(n); 
12978             if (rm < 5) {
12979                 return ps[0] + '.' + r;
12980             }
12981             r*=1; // turn it into a number;
12982             r++;
12983             if (String(r).length != n) {
12984                 ps[0]*=1;
12985                 ps[0]++;
12986                 r = String(r).substring(1); // chop the end off.
12987             }
12988             
12989             return ps[0] + '.' + r;
12990              
12991         },
12992         
12993         /**
12994          * Format a number as US currency
12995          * @param {Number/String} value The numeric value to format
12996          * @return {String} The formatted currency string
12997          */
12998         usMoney : function(v){
12999             v = (Math.round((v-0)*100))/100;
13000             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13001             v = String(v);
13002             var ps = v.split('.');
13003             var whole = ps[0];
13004             var sub = ps[1] ? '.'+ ps[1] : '.00';
13005             var r = /(\d+)(\d{3})/;
13006             while (r.test(whole)) {
13007                 whole = whole.replace(r, '$1' + ',' + '$2');
13008             }
13009             return "$" + whole + sub ;
13010         },
13011         
13012         /**
13013          * Parse a value into a formatted date using the specified format pattern.
13014          * @param {Mixed} value The value to format
13015          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13016          * @return {String} The formatted date string
13017          */
13018         date : function(v, format){
13019             if(!v){
13020                 return "";
13021             }
13022             if(!(v instanceof Date)){
13023                 v = new Date(Date.parse(v));
13024             }
13025             return v.dateFormat(format || "m/d/Y");
13026         },
13027
13028         /**
13029          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13030          * @param {String} format Any valid date format string
13031          * @return {Function} The date formatting function
13032          */
13033         dateRenderer : function(format){
13034             return function(v){
13035                 return Roo.util.Format.date(v, format);  
13036             };
13037         },
13038
13039         // private
13040         stripTagsRE : /<\/?[^>]+>/gi,
13041         
13042         /**
13043          * Strips all HTML tags
13044          * @param {Mixed} value The text from which to strip tags
13045          * @return {String} The stripped text
13046          */
13047         stripTags : function(v){
13048             return !v ? v : String(v).replace(this.stripTagsRE, "");
13049         }
13050     };
13051 }();/*
13052  * Based on:
13053  * Ext JS Library 1.1.1
13054  * Copyright(c) 2006-2007, Ext JS, LLC.
13055  *
13056  * Originally Released Under LGPL - original licence link has changed is not relivant.
13057  *
13058  * Fork - LGPL
13059  * <script type="text/javascript">
13060  */
13061
13062
13063  
13064
13065 /**
13066  * @class Roo.MasterTemplate
13067  * @extends Roo.Template
13068  * Provides a template that can have child templates. The syntax is:
13069 <pre><code>
13070 var t = new Roo.MasterTemplate(
13071         '&lt;select name="{name}"&gt;',
13072                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13073         '&lt;/select&gt;'
13074 );
13075 t.add('options', {value: 'foo', text: 'bar'});
13076 // or you can add multiple child elements in one shot
13077 t.addAll('options', [
13078     {value: 'foo', text: 'bar'},
13079     {value: 'foo2', text: 'bar2'},
13080     {value: 'foo3', text: 'bar3'}
13081 ]);
13082 // then append, applying the master template values
13083 t.append('my-form', {name: 'my-select'});
13084 </code></pre>
13085 * A name attribute for the child template is not required if you have only one child
13086 * template or you want to refer to them by index.
13087  */
13088 Roo.MasterTemplate = function(){
13089     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13090     this.originalHtml = this.html;
13091     var st = {};
13092     var m, re = this.subTemplateRe;
13093     re.lastIndex = 0;
13094     var subIndex = 0;
13095     while(m = re.exec(this.html)){
13096         var name = m[1], content = m[2];
13097         st[subIndex] = {
13098             name: name,
13099             index: subIndex,
13100             buffer: [],
13101             tpl : new Roo.Template(content)
13102         };
13103         if(name){
13104             st[name] = st[subIndex];
13105         }
13106         st[subIndex].tpl.compile();
13107         st[subIndex].tpl.call = this.call.createDelegate(this);
13108         subIndex++;
13109     }
13110     this.subCount = subIndex;
13111     this.subs = st;
13112 };
13113 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13114     /**
13115     * The regular expression used to match sub templates
13116     * @type RegExp
13117     * @property
13118     */
13119     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13120
13121     /**
13122      * Applies the passed values to a child template.
13123      * @param {String/Number} name (optional) The name or index of the child template
13124      * @param {Array/Object} values The values to be applied to the template
13125      * @return {MasterTemplate} this
13126      */
13127      add : function(name, values){
13128         if(arguments.length == 1){
13129             values = arguments[0];
13130             name = 0;
13131         }
13132         var s = this.subs[name];
13133         s.buffer[s.buffer.length] = s.tpl.apply(values);
13134         return this;
13135     },
13136
13137     /**
13138      * Applies all the passed values to a child template.
13139      * @param {String/Number} name (optional) The name or index of the child template
13140      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13141      * @param {Boolean} reset (optional) True to reset the template first
13142      * @return {MasterTemplate} this
13143      */
13144     fill : function(name, values, reset){
13145         var a = arguments;
13146         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13147             values = a[0];
13148             name = 0;
13149             reset = a[1];
13150         }
13151         if(reset){
13152             this.reset();
13153         }
13154         for(var i = 0, len = values.length; i < len; i++){
13155             this.add(name, values[i]);
13156         }
13157         return this;
13158     },
13159
13160     /**
13161      * Resets the template for reuse
13162      * @return {MasterTemplate} this
13163      */
13164      reset : function(){
13165         var s = this.subs;
13166         for(var i = 0; i < this.subCount; i++){
13167             s[i].buffer = [];
13168         }
13169         return this;
13170     },
13171
13172     applyTemplate : function(values){
13173         var s = this.subs;
13174         var replaceIndex = -1;
13175         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13176             return s[++replaceIndex].buffer.join("");
13177         });
13178         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13179     },
13180
13181     apply : function(){
13182         return this.applyTemplate.apply(this, arguments);
13183     },
13184
13185     compile : function(){return this;}
13186 });
13187
13188 /**
13189  * Alias for fill().
13190  * @method
13191  */
13192 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13193  /**
13194  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13195  * var tpl = Roo.MasterTemplate.from('element-id');
13196  * @param {String/HTMLElement} el
13197  * @param {Object} config
13198  * @static
13199  */
13200 Roo.MasterTemplate.from = function(el, config){
13201     el = Roo.getDom(el);
13202     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13203 };/*
13204  * Based on:
13205  * Ext JS Library 1.1.1
13206  * Copyright(c) 2006-2007, Ext JS, LLC.
13207  *
13208  * Originally Released Under LGPL - original licence link has changed is not relivant.
13209  *
13210  * Fork - LGPL
13211  * <script type="text/javascript">
13212  */
13213
13214  
13215 /**
13216  * @class Roo.util.CSS
13217  * Utility class for manipulating CSS rules
13218  * @singleton
13219  */
13220 Roo.util.CSS = function(){
13221         var rules = null;
13222         var doc = document;
13223
13224     var camelRe = /(-[a-z])/gi;
13225     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13226
13227    return {
13228    /**
13229     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13230     * tag and appended to the HEAD of the document.
13231     * @param {String|Object} cssText The text containing the css rules
13232     * @param {String} id An id to add to the stylesheet for later removal
13233     * @return {StyleSheet}
13234     */
13235     createStyleSheet : function(cssText, id){
13236         var ss;
13237         var head = doc.getElementsByTagName("head")[0];
13238         var nrules = doc.createElement("style");
13239         nrules.setAttribute("type", "text/css");
13240         if(id){
13241             nrules.setAttribute("id", id);
13242         }
13243         if (typeof(cssText) != 'string') {
13244             // support object maps..
13245             // not sure if this a good idea.. 
13246             // perhaps it should be merged with the general css handling
13247             // and handle js style props.
13248             var cssTextNew = [];
13249             for(var n in cssText) {
13250                 var citems = [];
13251                 for(var k in cssText[n]) {
13252                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13253                 }
13254                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13255                 
13256             }
13257             cssText = cssTextNew.join("\n");
13258             
13259         }
13260        
13261        
13262        if(Roo.isIE){
13263            head.appendChild(nrules);
13264            ss = nrules.styleSheet;
13265            ss.cssText = cssText;
13266        }else{
13267            try{
13268                 nrules.appendChild(doc.createTextNode(cssText));
13269            }catch(e){
13270                nrules.cssText = cssText; 
13271            }
13272            head.appendChild(nrules);
13273            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13274        }
13275        this.cacheStyleSheet(ss);
13276        return ss;
13277    },
13278
13279    /**
13280     * Removes a style or link tag by id
13281     * @param {String} id The id of the tag
13282     */
13283    removeStyleSheet : function(id){
13284        var existing = doc.getElementById(id);
13285        if(existing){
13286            existing.parentNode.removeChild(existing);
13287        }
13288    },
13289
13290    /**
13291     * Dynamically swaps an existing stylesheet reference for a new one
13292     * @param {String} id The id of an existing link tag to remove
13293     * @param {String} url The href of the new stylesheet to include
13294     */
13295    swapStyleSheet : function(id, url){
13296        this.removeStyleSheet(id);
13297        var ss = doc.createElement("link");
13298        ss.setAttribute("rel", "stylesheet");
13299        ss.setAttribute("type", "text/css");
13300        ss.setAttribute("id", id);
13301        ss.setAttribute("href", url);
13302        doc.getElementsByTagName("head")[0].appendChild(ss);
13303    },
13304    
13305    /**
13306     * Refresh the rule cache if you have dynamically added stylesheets
13307     * @return {Object} An object (hash) of rules indexed by selector
13308     */
13309    refreshCache : function(){
13310        return this.getRules(true);
13311    },
13312
13313    // private
13314    cacheStyleSheet : function(stylesheet){
13315        if(!rules){
13316            rules = {};
13317        }
13318        try{// try catch for cross domain access issue
13319            var ssRules = stylesheet.cssRules || stylesheet.rules;
13320            for(var j = ssRules.length-1; j >= 0; --j){
13321                rules[ssRules[j].selectorText] = ssRules[j];
13322            }
13323        }catch(e){}
13324    },
13325    
13326    /**
13327     * Gets all css rules for the document
13328     * @param {Boolean} refreshCache true to refresh the internal cache
13329     * @return {Object} An object (hash) of rules indexed by selector
13330     */
13331    getRules : function(refreshCache){
13332                 if(rules == null || refreshCache){
13333                         rules = {};
13334                         var ds = doc.styleSheets;
13335                         for(var i =0, len = ds.length; i < len; i++){
13336                             try{
13337                         this.cacheStyleSheet(ds[i]);
13338                     }catch(e){} 
13339                 }
13340                 }
13341                 return rules;
13342         },
13343         
13344         /**
13345     * Gets an an individual CSS rule by selector(s)
13346     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13347     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13348     * @return {CSSRule} The CSS rule or null if one is not found
13349     */
13350    getRule : function(selector, refreshCache){
13351                 var rs = this.getRules(refreshCache);
13352                 if(!(selector instanceof Array)){
13353                     return rs[selector];
13354                 }
13355                 for(var i = 0; i < selector.length; i++){
13356                         if(rs[selector[i]]){
13357                                 return rs[selector[i]];
13358                         }
13359                 }
13360                 return null;
13361         },
13362         
13363         
13364         /**
13365     * Updates a rule property
13366     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13367     * @param {String} property The css property
13368     * @param {String} value The new value for the property
13369     * @return {Boolean} true If a rule was found and updated
13370     */
13371    updateRule : function(selector, property, value){
13372                 if(!(selector instanceof Array)){
13373                         var rule = this.getRule(selector);
13374                         if(rule){
13375                                 rule.style[property.replace(camelRe, camelFn)] = value;
13376                                 return true;
13377                         }
13378                 }else{
13379                         for(var i = 0; i < selector.length; i++){
13380                                 if(this.updateRule(selector[i], property, value)){
13381                                         return true;
13382                                 }
13383                         }
13384                 }
13385                 return false;
13386         }
13387    };   
13388 }();/*
13389  * Based on:
13390  * Ext JS Library 1.1.1
13391  * Copyright(c) 2006-2007, Ext JS, LLC.
13392  *
13393  * Originally Released Under LGPL - original licence link has changed is not relivant.
13394  *
13395  * Fork - LGPL
13396  * <script type="text/javascript">
13397  */
13398
13399  
13400
13401 /**
13402  * @class Roo.util.ClickRepeater
13403  * @extends Roo.util.Observable
13404  * 
13405  * A wrapper class which can be applied to any element. Fires a "click" event while the
13406  * mouse is pressed. The interval between firings may be specified in the config but
13407  * defaults to 10 milliseconds.
13408  * 
13409  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13410  * 
13411  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13412  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13413  * Similar to an autorepeat key delay.
13414  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13415  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13416  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13417  *           "interval" and "delay" are ignored. "immediate" is honored.
13418  * @cfg {Boolean} preventDefault True to prevent the default click event
13419  * @cfg {Boolean} stopDefault True to stop the default click event
13420  * 
13421  * @history
13422  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13423  *     2007-02-02 jvs Renamed to ClickRepeater
13424  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13425  *
13426  *  @constructor
13427  * @param {String/HTMLElement/Element} el The element to listen on
13428  * @param {Object} config
13429  **/
13430 Roo.util.ClickRepeater = function(el, config)
13431 {
13432     this.el = Roo.get(el);
13433     this.el.unselectable();
13434
13435     Roo.apply(this, config);
13436
13437     this.addEvents({
13438     /**
13439      * @event mousedown
13440      * Fires when the mouse button is depressed.
13441      * @param {Roo.util.ClickRepeater} this
13442      */
13443         "mousedown" : true,
13444     /**
13445      * @event click
13446      * Fires on a specified interval during the time the element is pressed.
13447      * @param {Roo.util.ClickRepeater} this
13448      */
13449         "click" : true,
13450     /**
13451      * @event mouseup
13452      * Fires when the mouse key is released.
13453      * @param {Roo.util.ClickRepeater} this
13454      */
13455         "mouseup" : true
13456     });
13457
13458     this.el.on("mousedown", this.handleMouseDown, this);
13459     if(this.preventDefault || this.stopDefault){
13460         this.el.on("click", function(e){
13461             if(this.preventDefault){
13462                 e.preventDefault();
13463             }
13464             if(this.stopDefault){
13465                 e.stopEvent();
13466             }
13467         }, this);
13468     }
13469
13470     // allow inline handler
13471     if(this.handler){
13472         this.on("click", this.handler,  this.scope || this);
13473     }
13474
13475     Roo.util.ClickRepeater.superclass.constructor.call(this);
13476 };
13477
13478 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13479     interval : 20,
13480     delay: 250,
13481     preventDefault : true,
13482     stopDefault : false,
13483     timer : 0,
13484
13485     // private
13486     handleMouseDown : function(){
13487         clearTimeout(this.timer);
13488         this.el.blur();
13489         if(this.pressClass){
13490             this.el.addClass(this.pressClass);
13491         }
13492         this.mousedownTime = new Date();
13493
13494         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13495         this.el.on("mouseout", this.handleMouseOut, this);
13496
13497         this.fireEvent("mousedown", this);
13498         this.fireEvent("click", this);
13499         
13500         this.timer = this.click.defer(this.delay || this.interval, this);
13501     },
13502
13503     // private
13504     click : function(){
13505         this.fireEvent("click", this);
13506         this.timer = this.click.defer(this.getInterval(), this);
13507     },
13508
13509     // private
13510     getInterval: function(){
13511         if(!this.accelerate){
13512             return this.interval;
13513         }
13514         var pressTime = this.mousedownTime.getElapsed();
13515         if(pressTime < 500){
13516             return 400;
13517         }else if(pressTime < 1700){
13518             return 320;
13519         }else if(pressTime < 2600){
13520             return 250;
13521         }else if(pressTime < 3500){
13522             return 180;
13523         }else if(pressTime < 4400){
13524             return 140;
13525         }else if(pressTime < 5300){
13526             return 80;
13527         }else if(pressTime < 6200){
13528             return 50;
13529         }else{
13530             return 10;
13531         }
13532     },
13533
13534     // private
13535     handleMouseOut : function(){
13536         clearTimeout(this.timer);
13537         if(this.pressClass){
13538             this.el.removeClass(this.pressClass);
13539         }
13540         this.el.on("mouseover", this.handleMouseReturn, this);
13541     },
13542
13543     // private
13544     handleMouseReturn : function(){
13545         this.el.un("mouseover", this.handleMouseReturn);
13546         if(this.pressClass){
13547             this.el.addClass(this.pressClass);
13548         }
13549         this.click();
13550     },
13551
13552     // private
13553     handleMouseUp : function(){
13554         clearTimeout(this.timer);
13555         this.el.un("mouseover", this.handleMouseReturn);
13556         this.el.un("mouseout", this.handleMouseOut);
13557         Roo.get(document).un("mouseup", this.handleMouseUp);
13558         this.el.removeClass(this.pressClass);
13559         this.fireEvent("mouseup", this);
13560     }
13561 });/*
13562  * Based on:
13563  * Ext JS Library 1.1.1
13564  * Copyright(c) 2006-2007, Ext JS, LLC.
13565  *
13566  * Originally Released Under LGPL - original licence link has changed is not relivant.
13567  *
13568  * Fork - LGPL
13569  * <script type="text/javascript">
13570  */
13571
13572  
13573 /**
13574  * @class Roo.KeyNav
13575  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13576  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13577  * way to implement custom navigation schemes for any UI component.</p>
13578  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13579  * pageUp, pageDown, del, home, end.  Usage:</p>
13580  <pre><code>
13581 var nav = new Roo.KeyNav("my-element", {
13582     "left" : function(e){
13583         this.moveLeft(e.ctrlKey);
13584     },
13585     "right" : function(e){
13586         this.moveRight(e.ctrlKey);
13587     },
13588     "enter" : function(e){
13589         this.save();
13590     },
13591     scope : this
13592 });
13593 </code></pre>
13594  * @constructor
13595  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13596  * @param {Object} config The config
13597  */
13598 Roo.KeyNav = function(el, config){
13599     this.el = Roo.get(el);
13600     Roo.apply(this, config);
13601     if(!this.disabled){
13602         this.disabled = true;
13603         this.enable();
13604     }
13605 };
13606
13607 Roo.KeyNav.prototype = {
13608     /**
13609      * @cfg {Boolean} disabled
13610      * True to disable this KeyNav instance (defaults to false)
13611      */
13612     disabled : false,
13613     /**
13614      * @cfg {String} defaultEventAction
13615      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13616      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13617      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13618      */
13619     defaultEventAction: "stopEvent",
13620     /**
13621      * @cfg {Boolean} forceKeyDown
13622      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13623      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13624      * handle keydown instead of keypress.
13625      */
13626     forceKeyDown : false,
13627
13628     // private
13629     prepareEvent : function(e){
13630         var k = e.getKey();
13631         var h = this.keyToHandler[k];
13632         //if(h && this[h]){
13633         //    e.stopPropagation();
13634         //}
13635         if(Roo.isSafari && h && k >= 37 && k <= 40){
13636             e.stopEvent();
13637         }
13638     },
13639
13640     // private
13641     relay : function(e){
13642         var k = e.getKey();
13643         var h = this.keyToHandler[k];
13644         if(h && this[h]){
13645             if(this.doRelay(e, this[h], h) !== true){
13646                 e[this.defaultEventAction]();
13647             }
13648         }
13649     },
13650
13651     // private
13652     doRelay : function(e, h, hname){
13653         return h.call(this.scope || this, e);
13654     },
13655
13656     // possible handlers
13657     enter : false,
13658     left : false,
13659     right : false,
13660     up : false,
13661     down : false,
13662     tab : false,
13663     esc : false,
13664     pageUp : false,
13665     pageDown : false,
13666     del : false,
13667     home : false,
13668     end : false,
13669
13670     // quick lookup hash
13671     keyToHandler : {
13672         37 : "left",
13673         39 : "right",
13674         38 : "up",
13675         40 : "down",
13676         33 : "pageUp",
13677         34 : "pageDown",
13678         46 : "del",
13679         36 : "home",
13680         35 : "end",
13681         13 : "enter",
13682         27 : "esc",
13683         9  : "tab"
13684     },
13685
13686         /**
13687          * Enable this KeyNav
13688          */
13689         enable: function(){
13690                 if(this.disabled){
13691             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13692             // the EventObject will normalize Safari automatically
13693             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13694                 this.el.on("keydown", this.relay,  this);
13695             }else{
13696                 this.el.on("keydown", this.prepareEvent,  this);
13697                 this.el.on("keypress", this.relay,  this);
13698             }
13699                     this.disabled = false;
13700                 }
13701         },
13702
13703         /**
13704          * Disable this KeyNav
13705          */
13706         disable: function(){
13707                 if(!this.disabled){
13708                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13709                 this.el.un("keydown", this.relay);
13710             }else{
13711                 this.el.un("keydown", this.prepareEvent);
13712                 this.el.un("keypress", this.relay);
13713             }
13714                     this.disabled = true;
13715                 }
13716         }
13717 };/*
13718  * Based on:
13719  * Ext JS Library 1.1.1
13720  * Copyright(c) 2006-2007, Ext JS, LLC.
13721  *
13722  * Originally Released Under LGPL - original licence link has changed is not relivant.
13723  *
13724  * Fork - LGPL
13725  * <script type="text/javascript">
13726  */
13727
13728  
13729 /**
13730  * @class Roo.KeyMap
13731  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13732  * The constructor accepts the same config object as defined by {@link #addBinding}.
13733  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13734  * combination it will call the function with this signature (if the match is a multi-key
13735  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13736  * A KeyMap can also handle a string representation of keys.<br />
13737  * Usage:
13738  <pre><code>
13739 // map one key by key code
13740 var map = new Roo.KeyMap("my-element", {
13741     key: 13, // or Roo.EventObject.ENTER
13742     fn: myHandler,
13743     scope: myObject
13744 });
13745
13746 // map multiple keys to one action by string
13747 var map = new Roo.KeyMap("my-element", {
13748     key: "a\r\n\t",
13749     fn: myHandler,
13750     scope: myObject
13751 });
13752
13753 // map multiple keys to multiple actions by strings and array of codes
13754 var map = new Roo.KeyMap("my-element", [
13755     {
13756         key: [10,13],
13757         fn: function(){ alert("Return was pressed"); }
13758     }, {
13759         key: "abc",
13760         fn: function(){ alert('a, b or c was pressed'); }
13761     }, {
13762         key: "\t",
13763         ctrl:true,
13764         shift:true,
13765         fn: function(){ alert('Control + shift + tab was pressed.'); }
13766     }
13767 ]);
13768 </code></pre>
13769  * <b>Note: A KeyMap starts enabled</b>
13770  * @constructor
13771  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13772  * @param {Object} config The config (see {@link #addBinding})
13773  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13774  */
13775 Roo.KeyMap = function(el, config, eventName){
13776     this.el  = Roo.get(el);
13777     this.eventName = eventName || "keydown";
13778     this.bindings = [];
13779     if(config){
13780         this.addBinding(config);
13781     }
13782     this.enable();
13783 };
13784
13785 Roo.KeyMap.prototype = {
13786     /**
13787      * True to stop the event from bubbling and prevent the default browser action if the
13788      * key was handled by the KeyMap (defaults to false)
13789      * @type Boolean
13790      */
13791     stopEvent : false,
13792
13793     /**
13794      * Add a new binding to this KeyMap. The following config object properties are supported:
13795      * <pre>
13796 Property    Type             Description
13797 ----------  ---------------  ----------------------------------------------------------------------
13798 key         String/Array     A single keycode or an array of keycodes to handle
13799 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13800 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13801 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13802 fn          Function         The function to call when KeyMap finds the expected key combination
13803 scope       Object           The scope of the callback function
13804 </pre>
13805      *
13806      * Usage:
13807      * <pre><code>
13808 // Create a KeyMap
13809 var map = new Roo.KeyMap(document, {
13810     key: Roo.EventObject.ENTER,
13811     fn: handleKey,
13812     scope: this
13813 });
13814
13815 //Add a new binding to the existing KeyMap later
13816 map.addBinding({
13817     key: 'abc',
13818     shift: true,
13819     fn: handleKey,
13820     scope: this
13821 });
13822 </code></pre>
13823      * @param {Object/Array} config A single KeyMap config or an array of configs
13824      */
13825         addBinding : function(config){
13826         if(config instanceof Array){
13827             for(var i = 0, len = config.length; i < len; i++){
13828                 this.addBinding(config[i]);
13829             }
13830             return;
13831         }
13832         var keyCode = config.key,
13833             shift = config.shift, 
13834             ctrl = config.ctrl, 
13835             alt = config.alt,
13836             fn = config.fn,
13837             scope = config.scope;
13838         if(typeof keyCode == "string"){
13839             var ks = [];
13840             var keyString = keyCode.toUpperCase();
13841             for(var j = 0, len = keyString.length; j < len; j++){
13842                 ks.push(keyString.charCodeAt(j));
13843             }
13844             keyCode = ks;
13845         }
13846         var keyArray = keyCode instanceof Array;
13847         var handler = function(e){
13848             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13849                 var k = e.getKey();
13850                 if(keyArray){
13851                     for(var i = 0, len = keyCode.length; i < len; i++){
13852                         if(keyCode[i] == k){
13853                           if(this.stopEvent){
13854                               e.stopEvent();
13855                           }
13856                           fn.call(scope || window, k, e);
13857                           return;
13858                         }
13859                     }
13860                 }else{
13861                     if(k == keyCode){
13862                         if(this.stopEvent){
13863                            e.stopEvent();
13864                         }
13865                         fn.call(scope || window, k, e);
13866                     }
13867                 }
13868             }
13869         };
13870         this.bindings.push(handler);  
13871         },
13872
13873     /**
13874      * Shorthand for adding a single key listener
13875      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13876      * following options:
13877      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13878      * @param {Function} fn The function to call
13879      * @param {Object} scope (optional) The scope of the function
13880      */
13881     on : function(key, fn, scope){
13882         var keyCode, shift, ctrl, alt;
13883         if(typeof key == "object" && !(key instanceof Array)){
13884             keyCode = key.key;
13885             shift = key.shift;
13886             ctrl = key.ctrl;
13887             alt = key.alt;
13888         }else{
13889             keyCode = key;
13890         }
13891         this.addBinding({
13892             key: keyCode,
13893             shift: shift,
13894             ctrl: ctrl,
13895             alt: alt,
13896             fn: fn,
13897             scope: scope
13898         })
13899     },
13900
13901     // private
13902     handleKeyDown : function(e){
13903             if(this.enabled){ //just in case
13904             var b = this.bindings;
13905             for(var i = 0, len = b.length; i < len; i++){
13906                 b[i].call(this, e);
13907             }
13908             }
13909         },
13910         
13911         /**
13912          * Returns true if this KeyMap is enabled
13913          * @return {Boolean} 
13914          */
13915         isEnabled : function(){
13916             return this.enabled;  
13917         },
13918         
13919         /**
13920          * Enables this KeyMap
13921          */
13922         enable: function(){
13923                 if(!this.enabled){
13924                     this.el.on(this.eventName, this.handleKeyDown, this);
13925                     this.enabled = true;
13926                 }
13927         },
13928
13929         /**
13930          * Disable this KeyMap
13931          */
13932         disable: function(){
13933                 if(this.enabled){
13934                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13935                     this.enabled = false;
13936                 }
13937         }
13938 };/*
13939  * Based on:
13940  * Ext JS Library 1.1.1
13941  * Copyright(c) 2006-2007, Ext JS, LLC.
13942  *
13943  * Originally Released Under LGPL - original licence link has changed is not relivant.
13944  *
13945  * Fork - LGPL
13946  * <script type="text/javascript">
13947  */
13948
13949  
13950 /**
13951  * @class Roo.util.TextMetrics
13952  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13953  * wide, in pixels, a given block of text will be.
13954  * @singleton
13955  */
13956 Roo.util.TextMetrics = function(){
13957     var shared;
13958     return {
13959         /**
13960          * Measures the size of the specified text
13961          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13962          * that can affect the size of the rendered text
13963          * @param {String} text The text to measure
13964          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13965          * in order to accurately measure the text height
13966          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13967          */
13968         measure : function(el, text, fixedWidth){
13969             if(!shared){
13970                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13971             }
13972             shared.bind(el);
13973             shared.setFixedWidth(fixedWidth || 'auto');
13974             return shared.getSize(text);
13975         },
13976
13977         /**
13978          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13979          * the overhead of multiple calls to initialize the style properties on each measurement.
13980          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13981          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13982          * in order to accurately measure the text height
13983          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13984          */
13985         createInstance : function(el, fixedWidth){
13986             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13987         }
13988     };
13989 }();
13990
13991  
13992
13993 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13994     var ml = new Roo.Element(document.createElement('div'));
13995     document.body.appendChild(ml.dom);
13996     ml.position('absolute');
13997     ml.setLeftTop(-1000, -1000);
13998     ml.hide();
13999
14000     if(fixedWidth){
14001         ml.setWidth(fixedWidth);
14002     }
14003      
14004     var instance = {
14005         /**
14006          * Returns the size of the specified text based on the internal element's style and width properties
14007          * @memberOf Roo.util.TextMetrics.Instance#
14008          * @param {String} text The text to measure
14009          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14010          */
14011         getSize : function(text){
14012             ml.update(text);
14013             var s = ml.getSize();
14014             ml.update('');
14015             return s;
14016         },
14017
14018         /**
14019          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14020          * that can affect the size of the rendered text
14021          * @memberOf Roo.util.TextMetrics.Instance#
14022          * @param {String/HTMLElement} el The element, dom node or id
14023          */
14024         bind : function(el){
14025             ml.setStyle(
14026                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14027             );
14028         },
14029
14030         /**
14031          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14032          * to set a fixed width in order to accurately measure the text height.
14033          * @memberOf Roo.util.TextMetrics.Instance#
14034          * @param {Number} width The width to set on the element
14035          */
14036         setFixedWidth : function(width){
14037             ml.setWidth(width);
14038         },
14039
14040         /**
14041          * Returns the measured width of the specified text
14042          * @memberOf Roo.util.TextMetrics.Instance#
14043          * @param {String} text The text to measure
14044          * @return {Number} width The width in pixels
14045          */
14046         getWidth : function(text){
14047             ml.dom.style.width = 'auto';
14048             return this.getSize(text).width;
14049         },
14050
14051         /**
14052          * Returns the measured height of the specified text.  For multiline text, be sure to call
14053          * {@link #setFixedWidth} if necessary.
14054          * @memberOf Roo.util.TextMetrics.Instance#
14055          * @param {String} text The text to measure
14056          * @return {Number} height The height in pixels
14057          */
14058         getHeight : function(text){
14059             return this.getSize(text).height;
14060         }
14061     };
14062
14063     instance.bind(bindTo);
14064
14065     return instance;
14066 };
14067
14068 // backwards compat
14069 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14070  * Based on:
14071  * Ext JS Library 1.1.1
14072  * Copyright(c) 2006-2007, Ext JS, LLC.
14073  *
14074  * Originally Released Under LGPL - original licence link has changed is not relivant.
14075  *
14076  * Fork - LGPL
14077  * <script type="text/javascript">
14078  */
14079
14080 /**
14081  * @class Roo.state.Provider
14082  * Abstract base class for state provider implementations. This class provides methods
14083  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14084  * Provider interface.
14085  */
14086 Roo.state.Provider = function(){
14087     /**
14088      * @event statechange
14089      * Fires when a state change occurs.
14090      * @param {Provider} this This state provider
14091      * @param {String} key The state key which was changed
14092      * @param {String} value The encoded value for the state
14093      */
14094     this.addEvents({
14095         "statechange": true
14096     });
14097     this.state = {};
14098     Roo.state.Provider.superclass.constructor.call(this);
14099 };
14100 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14101     /**
14102      * Returns the current value for a key
14103      * @param {String} name The key name
14104      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14105      * @return {Mixed} The state data
14106      */
14107     get : function(name, defaultValue){
14108         return typeof this.state[name] == "undefined" ?
14109             defaultValue : this.state[name];
14110     },
14111     
14112     /**
14113      * Clears a value from the state
14114      * @param {String} name The key name
14115      */
14116     clear : function(name){
14117         delete this.state[name];
14118         this.fireEvent("statechange", this, name, null);
14119     },
14120     
14121     /**
14122      * Sets the value for a key
14123      * @param {String} name The key name
14124      * @param {Mixed} value The value to set
14125      */
14126     set : function(name, value){
14127         this.state[name] = value;
14128         this.fireEvent("statechange", this, name, value);
14129     },
14130     
14131     /**
14132      * Decodes a string previously encoded with {@link #encodeValue}.
14133      * @param {String} value The value to decode
14134      * @return {Mixed} The decoded value
14135      */
14136     decodeValue : function(cookie){
14137         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14138         var matches = re.exec(unescape(cookie));
14139         if(!matches || !matches[1]) return; // non state cookie
14140         var type = matches[1];
14141         var v = matches[2];
14142         switch(type){
14143             case "n":
14144                 return parseFloat(v);
14145             case "d":
14146                 return new Date(Date.parse(v));
14147             case "b":
14148                 return (v == "1");
14149             case "a":
14150                 var all = [];
14151                 var values = v.split("^");
14152                 for(var i = 0, len = values.length; i < len; i++){
14153                     all.push(this.decodeValue(values[i]));
14154                 }
14155                 return all;
14156            case "o":
14157                 var all = {};
14158                 var values = v.split("^");
14159                 for(var i = 0, len = values.length; i < len; i++){
14160                     var kv = values[i].split("=");
14161                     all[kv[0]] = this.decodeValue(kv[1]);
14162                 }
14163                 return all;
14164            default:
14165                 return v;
14166         }
14167     },
14168     
14169     /**
14170      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14171      * @param {Mixed} value The value to encode
14172      * @return {String} The encoded value
14173      */
14174     encodeValue : function(v){
14175         var enc;
14176         if(typeof v == "number"){
14177             enc = "n:" + v;
14178         }else if(typeof v == "boolean"){
14179             enc = "b:" + (v ? "1" : "0");
14180         }else if(v instanceof Date){
14181             enc = "d:" + v.toGMTString();
14182         }else if(v instanceof Array){
14183             var flat = "";
14184             for(var i = 0, len = v.length; i < len; i++){
14185                 flat += this.encodeValue(v[i]);
14186                 if(i != len-1) flat += "^";
14187             }
14188             enc = "a:" + flat;
14189         }else if(typeof v == "object"){
14190             var flat = "";
14191             for(var key in v){
14192                 if(typeof v[key] != "function"){
14193                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14194                 }
14195             }
14196             enc = "o:" + flat.substring(0, flat.length-1);
14197         }else{
14198             enc = "s:" + v;
14199         }
14200         return escape(enc);        
14201     }
14202 });
14203
14204 /*
14205  * Based on:
14206  * Ext JS Library 1.1.1
14207  * Copyright(c) 2006-2007, Ext JS, LLC.
14208  *
14209  * Originally Released Under LGPL - original licence link has changed is not relivant.
14210  *
14211  * Fork - LGPL
14212  * <script type="text/javascript">
14213  */
14214 /**
14215  * @class Roo.state.Manager
14216  * This is the global state manager. By default all components that are "state aware" check this class
14217  * for state information if you don't pass them a custom state provider. In order for this class
14218  * to be useful, it must be initialized with a provider when your application initializes.
14219  <pre><code>
14220 // in your initialization function
14221 init : function(){
14222    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14223    ...
14224    // supposed you have a {@link Roo.BorderLayout}
14225    var layout = new Roo.BorderLayout(...);
14226    layout.restoreState();
14227    // or a {Roo.BasicDialog}
14228    var dialog = new Roo.BasicDialog(...);
14229    dialog.restoreState();
14230  </code></pre>
14231  * @singleton
14232  */
14233 Roo.state.Manager = function(){
14234     var provider = new Roo.state.Provider();
14235     
14236     return {
14237         /**
14238          * Configures the default state provider for your application
14239          * @param {Provider} stateProvider The state provider to set
14240          */
14241         setProvider : function(stateProvider){
14242             provider = stateProvider;
14243         },
14244         
14245         /**
14246          * Returns the current value for a key
14247          * @param {String} name The key name
14248          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14249          * @return {Mixed} The state data
14250          */
14251         get : function(key, defaultValue){
14252             return provider.get(key, defaultValue);
14253         },
14254         
14255         /**
14256          * Sets the value for a key
14257          * @param {String} name The key name
14258          * @param {Mixed} value The state data
14259          */
14260          set : function(key, value){
14261             provider.set(key, value);
14262         },
14263         
14264         /**
14265          * Clears a value from the state
14266          * @param {String} name The key name
14267          */
14268         clear : function(key){
14269             provider.clear(key);
14270         },
14271         
14272         /**
14273          * Gets the currently configured state provider
14274          * @return {Provider} The state provider
14275          */
14276         getProvider : function(){
14277             return provider;
14278         }
14279     };
14280 }();
14281 /*
14282  * Based on:
14283  * Ext JS Library 1.1.1
14284  * Copyright(c) 2006-2007, Ext JS, LLC.
14285  *
14286  * Originally Released Under LGPL - original licence link has changed is not relivant.
14287  *
14288  * Fork - LGPL
14289  * <script type="text/javascript">
14290  */
14291 /**
14292  * @class Roo.state.CookieProvider
14293  * @extends Roo.state.Provider
14294  * The default Provider implementation which saves state via cookies.
14295  * <br />Usage:
14296  <pre><code>
14297    var cp = new Roo.state.CookieProvider({
14298        path: "/cgi-bin/",
14299        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14300        domain: "roojs.com"
14301    })
14302    Roo.state.Manager.setProvider(cp);
14303  </code></pre>
14304  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14305  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14306  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14307  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14308  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14309  * domain the page is running on including the 'www' like 'www.roojs.com')
14310  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14311  * @constructor
14312  * Create a new CookieProvider
14313  * @param {Object} config The configuration object
14314  */
14315 Roo.state.CookieProvider = function(config){
14316     Roo.state.CookieProvider.superclass.constructor.call(this);
14317     this.path = "/";
14318     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14319     this.domain = null;
14320     this.secure = false;
14321     Roo.apply(this, config);
14322     this.state = this.readCookies();
14323 };
14324
14325 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14326     // private
14327     set : function(name, value){
14328         if(typeof value == "undefined" || value === null){
14329             this.clear(name);
14330             return;
14331         }
14332         this.setCookie(name, value);
14333         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14334     },
14335
14336     // private
14337     clear : function(name){
14338         this.clearCookie(name);
14339         Roo.state.CookieProvider.superclass.clear.call(this, name);
14340     },
14341
14342     // private
14343     readCookies : function(){
14344         var cookies = {};
14345         var c = document.cookie + ";";
14346         var re = /\s?(.*?)=(.*?);/g;
14347         var matches;
14348         while((matches = re.exec(c)) != null){
14349             var name = matches[1];
14350             var value = matches[2];
14351             if(name && name.substring(0,3) == "ys-"){
14352                 cookies[name.substr(3)] = this.decodeValue(value);
14353             }
14354         }
14355         return cookies;
14356     },
14357
14358     // private
14359     setCookie : function(name, value){
14360         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14361            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14362            ((this.path == null) ? "" : ("; path=" + this.path)) +
14363            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14364            ((this.secure == true) ? "; secure" : "");
14365     },
14366
14367     // private
14368     clearCookie : function(name){
14369         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14370            ((this.path == null) ? "" : ("; path=" + this.path)) +
14371            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14372            ((this.secure == true) ? "; secure" : "");
14373     }
14374 });/*
14375  * Based on:
14376  * Ext JS Library 1.1.1
14377  * Copyright(c) 2006-2007, Ext JS, LLC.
14378  *
14379  * Originally Released Under LGPL - original licence link has changed is not relivant.
14380  *
14381  * Fork - LGPL
14382  * <script type="text/javascript">
14383  */
14384
14385
14386
14387 /*
14388  * These classes are derivatives of the similarly named classes in the YUI Library.
14389  * The original license:
14390  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14391  * Code licensed under the BSD License:
14392  * http://developer.yahoo.net/yui/license.txt
14393  */
14394
14395 (function() {
14396
14397 var Event=Roo.EventManager;
14398 var Dom=Roo.lib.Dom;
14399
14400 /**
14401  * @class Roo.dd.DragDrop
14402  * Defines the interface and base operation of items that that can be
14403  * dragged or can be drop targets.  It was designed to be extended, overriding
14404  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14405  * Up to three html elements can be associated with a DragDrop instance:
14406  * <ul>
14407  * <li>linked element: the element that is passed into the constructor.
14408  * This is the element which defines the boundaries for interaction with
14409  * other DragDrop objects.</li>
14410  * <li>handle element(s): The drag operation only occurs if the element that
14411  * was clicked matches a handle element.  By default this is the linked
14412  * element, but there are times that you will want only a portion of the
14413  * linked element to initiate the drag operation, and the setHandleElId()
14414  * method provides a way to define this.</li>
14415  * <li>drag element: this represents the element that would be moved along
14416  * with the cursor during a drag operation.  By default, this is the linked
14417  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14418  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14419  * </li>
14420  * </ul>
14421  * This class should not be instantiated until the onload event to ensure that
14422  * the associated elements are available.
14423  * The following would define a DragDrop obj that would interact with any
14424  * other DragDrop obj in the "group1" group:
14425  * <pre>
14426  *  dd = new Roo.dd.DragDrop("div1", "group1");
14427  * </pre>
14428  * Since none of the event handlers have been implemented, nothing would
14429  * actually happen if you were to run the code above.  Normally you would
14430  * override this class or one of the default implementations, but you can
14431  * also override the methods you want on an instance of the class...
14432  * <pre>
14433  *  dd.onDragDrop = function(e, id) {
14434  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14435  *  }
14436  * </pre>
14437  * @constructor
14438  * @param {String} id of the element that is linked to this instance
14439  * @param {String} sGroup the group of related DragDrop objects
14440  * @param {object} config an object containing configurable attributes
14441  *                Valid properties for DragDrop:
14442  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14443  */
14444 Roo.dd.DragDrop = function(id, sGroup, config) {
14445     if (id) {
14446         this.init(id, sGroup, config);
14447     }
14448 };
14449
14450 Roo.dd.DragDrop.prototype = {
14451
14452     /**
14453      * The id of the element associated with this object.  This is what we
14454      * refer to as the "linked element" because the size and position of
14455      * this element is used to determine when the drag and drop objects have
14456      * interacted.
14457      * @property id
14458      * @type String
14459      */
14460     id: null,
14461
14462     /**
14463      * Configuration attributes passed into the constructor
14464      * @property config
14465      * @type object
14466      */
14467     config: null,
14468
14469     /**
14470      * The id of the element that will be dragged.  By default this is same
14471      * as the linked element , but could be changed to another element. Ex:
14472      * Roo.dd.DDProxy
14473      * @property dragElId
14474      * @type String
14475      * @private
14476      */
14477     dragElId: null,
14478
14479     /**
14480      * the id of the element that initiates the drag operation.  By default
14481      * this is the linked element, but could be changed to be a child of this
14482      * element.  This lets us do things like only starting the drag when the
14483      * header element within the linked html element is clicked.
14484      * @property handleElId
14485      * @type String
14486      * @private
14487      */
14488     handleElId: null,
14489
14490     /**
14491      * An associative array of HTML tags that will be ignored if clicked.
14492      * @property invalidHandleTypes
14493      * @type {string: string}
14494      */
14495     invalidHandleTypes: null,
14496
14497     /**
14498      * An associative array of ids for elements that will be ignored if clicked
14499      * @property invalidHandleIds
14500      * @type {string: string}
14501      */
14502     invalidHandleIds: null,
14503
14504     /**
14505      * An indexted array of css class names for elements that will be ignored
14506      * if clicked.
14507      * @property invalidHandleClasses
14508      * @type string[]
14509      */
14510     invalidHandleClasses: null,
14511
14512     /**
14513      * The linked element's absolute X position at the time the drag was
14514      * started
14515      * @property startPageX
14516      * @type int
14517      * @private
14518      */
14519     startPageX: 0,
14520
14521     /**
14522      * The linked element's absolute X position at the time the drag was
14523      * started
14524      * @property startPageY
14525      * @type int
14526      * @private
14527      */
14528     startPageY: 0,
14529
14530     /**
14531      * The group defines a logical collection of DragDrop objects that are
14532      * related.  Instances only get events when interacting with other
14533      * DragDrop object in the same group.  This lets us define multiple
14534      * groups using a single DragDrop subclass if we want.
14535      * @property groups
14536      * @type {string: string}
14537      */
14538     groups: null,
14539
14540     /**
14541      * Individual drag/drop instances can be locked.  This will prevent
14542      * onmousedown start drag.
14543      * @property locked
14544      * @type boolean
14545      * @private
14546      */
14547     locked: false,
14548
14549     /**
14550      * Lock this instance
14551      * @method lock
14552      */
14553     lock: function() { this.locked = true; },
14554
14555     /**
14556      * Unlock this instace
14557      * @method unlock
14558      */
14559     unlock: function() { this.locked = false; },
14560
14561     /**
14562      * By default, all insances can be a drop target.  This can be disabled by
14563      * setting isTarget to false.
14564      * @method isTarget
14565      * @type boolean
14566      */
14567     isTarget: true,
14568
14569     /**
14570      * The padding configured for this drag and drop object for calculating
14571      * the drop zone intersection with this object.
14572      * @method padding
14573      * @type int[]
14574      */
14575     padding: null,
14576
14577     /**
14578      * Cached reference to the linked element
14579      * @property _domRef
14580      * @private
14581      */
14582     _domRef: null,
14583
14584     /**
14585      * Internal typeof flag
14586      * @property __ygDragDrop
14587      * @private
14588      */
14589     __ygDragDrop: true,
14590
14591     /**
14592      * Set to true when horizontal contraints are applied
14593      * @property constrainX
14594      * @type boolean
14595      * @private
14596      */
14597     constrainX: false,
14598
14599     /**
14600      * Set to true when vertical contraints are applied
14601      * @property constrainY
14602      * @type boolean
14603      * @private
14604      */
14605     constrainY: false,
14606
14607     /**
14608      * The left constraint
14609      * @property minX
14610      * @type int
14611      * @private
14612      */
14613     minX: 0,
14614
14615     /**
14616      * The right constraint
14617      * @property maxX
14618      * @type int
14619      * @private
14620      */
14621     maxX: 0,
14622
14623     /**
14624      * The up constraint
14625      * @property minY
14626      * @type int
14627      * @type int
14628      * @private
14629      */
14630     minY: 0,
14631
14632     /**
14633      * The down constraint
14634      * @property maxY
14635      * @type int
14636      * @private
14637      */
14638     maxY: 0,
14639
14640     /**
14641      * Maintain offsets when we resetconstraints.  Set to true when you want
14642      * the position of the element relative to its parent to stay the same
14643      * when the page changes
14644      *
14645      * @property maintainOffset
14646      * @type boolean
14647      */
14648     maintainOffset: false,
14649
14650     /**
14651      * Array of pixel locations the element will snap to if we specified a
14652      * horizontal graduation/interval.  This array is generated automatically
14653      * when you define a tick interval.
14654      * @property xTicks
14655      * @type int[]
14656      */
14657     xTicks: null,
14658
14659     /**
14660      * Array of pixel locations the element will snap to if we specified a
14661      * vertical graduation/interval.  This array is generated automatically
14662      * when you define a tick interval.
14663      * @property yTicks
14664      * @type int[]
14665      */
14666     yTicks: null,
14667
14668     /**
14669      * By default the drag and drop instance will only respond to the primary
14670      * button click (left button for a right-handed mouse).  Set to true to
14671      * allow drag and drop to start with any mouse click that is propogated
14672      * by the browser
14673      * @property primaryButtonOnly
14674      * @type boolean
14675      */
14676     primaryButtonOnly: true,
14677
14678     /**
14679      * The availabe property is false until the linked dom element is accessible.
14680      * @property available
14681      * @type boolean
14682      */
14683     available: false,
14684
14685     /**
14686      * By default, drags can only be initiated if the mousedown occurs in the
14687      * region the linked element is.  This is done in part to work around a
14688      * bug in some browsers that mis-report the mousedown if the previous
14689      * mouseup happened outside of the window.  This property is set to true
14690      * if outer handles are defined.
14691      *
14692      * @property hasOuterHandles
14693      * @type boolean
14694      * @default false
14695      */
14696     hasOuterHandles: false,
14697
14698     /**
14699      * Code that executes immediately before the startDrag event
14700      * @method b4StartDrag
14701      * @private
14702      */
14703     b4StartDrag: function(x, y) { },
14704
14705     /**
14706      * Abstract method called after a drag/drop object is clicked
14707      * and the drag or mousedown time thresholds have beeen met.
14708      * @method startDrag
14709      * @param {int} X click location
14710      * @param {int} Y click location
14711      */
14712     startDrag: function(x, y) { /* override this */ },
14713
14714     /**
14715      * Code that executes immediately before the onDrag event
14716      * @method b4Drag
14717      * @private
14718      */
14719     b4Drag: function(e) { },
14720
14721     /**
14722      * Abstract method called during the onMouseMove event while dragging an
14723      * object.
14724      * @method onDrag
14725      * @param {Event} e the mousemove event
14726      */
14727     onDrag: function(e) { /* override this */ },
14728
14729     /**
14730      * Abstract method called when this element fist begins hovering over
14731      * another DragDrop obj
14732      * @method onDragEnter
14733      * @param {Event} e the mousemove event
14734      * @param {String|DragDrop[]} id In POINT mode, the element
14735      * id this is hovering over.  In INTERSECT mode, an array of one or more
14736      * dragdrop items being hovered over.
14737      */
14738     onDragEnter: function(e, id) { /* override this */ },
14739
14740     /**
14741      * Code that executes immediately before the onDragOver event
14742      * @method b4DragOver
14743      * @private
14744      */
14745     b4DragOver: function(e) { },
14746
14747     /**
14748      * Abstract method called when this element is hovering over another
14749      * DragDrop obj
14750      * @method onDragOver
14751      * @param {Event} e the mousemove event
14752      * @param {String|DragDrop[]} id In POINT mode, the element
14753      * id this is hovering over.  In INTERSECT mode, an array of dd items
14754      * being hovered over.
14755      */
14756     onDragOver: function(e, id) { /* override this */ },
14757
14758     /**
14759      * Code that executes immediately before the onDragOut event
14760      * @method b4DragOut
14761      * @private
14762      */
14763     b4DragOut: function(e) { },
14764
14765     /**
14766      * Abstract method called when we are no longer hovering over an element
14767      * @method onDragOut
14768      * @param {Event} e the mousemove event
14769      * @param {String|DragDrop[]} id In POINT mode, the element
14770      * id this was hovering over.  In INTERSECT mode, an array of dd items
14771      * that the mouse is no longer over.
14772      */
14773     onDragOut: function(e, id) { /* override this */ },
14774
14775     /**
14776      * Code that executes immediately before the onDragDrop event
14777      * @method b4DragDrop
14778      * @private
14779      */
14780     b4DragDrop: function(e) { },
14781
14782     /**
14783      * Abstract method called when this item is dropped on another DragDrop
14784      * obj
14785      * @method onDragDrop
14786      * @param {Event} e the mouseup event
14787      * @param {String|DragDrop[]} id In POINT mode, the element
14788      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14789      * was dropped on.
14790      */
14791     onDragDrop: function(e, id) { /* override this */ },
14792
14793     /**
14794      * Abstract method called when this item is dropped on an area with no
14795      * drop target
14796      * @method onInvalidDrop
14797      * @param {Event} e the mouseup event
14798      */
14799     onInvalidDrop: function(e) { /* override this */ },
14800
14801     /**
14802      * Code that executes immediately before the endDrag event
14803      * @method b4EndDrag
14804      * @private
14805      */
14806     b4EndDrag: function(e) { },
14807
14808     /**
14809      * Fired when we are done dragging the object
14810      * @method endDrag
14811      * @param {Event} e the mouseup event
14812      */
14813     endDrag: function(e) { /* override this */ },
14814
14815     /**
14816      * Code executed immediately before the onMouseDown event
14817      * @method b4MouseDown
14818      * @param {Event} e the mousedown event
14819      * @private
14820      */
14821     b4MouseDown: function(e) {  },
14822
14823     /**
14824      * Event handler that fires when a drag/drop obj gets a mousedown
14825      * @method onMouseDown
14826      * @param {Event} e the mousedown event
14827      */
14828     onMouseDown: function(e) { /* override this */ },
14829
14830     /**
14831      * Event handler that fires when a drag/drop obj gets a mouseup
14832      * @method onMouseUp
14833      * @param {Event} e the mouseup event
14834      */
14835     onMouseUp: function(e) { /* override this */ },
14836
14837     /**
14838      * Override the onAvailable method to do what is needed after the initial
14839      * position was determined.
14840      * @method onAvailable
14841      */
14842     onAvailable: function () {
14843     },
14844
14845     /*
14846      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14847      * @type Object
14848      */
14849     defaultPadding : {left:0, right:0, top:0, bottom:0},
14850
14851     /*
14852      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14853  *
14854  * Usage:
14855  <pre><code>
14856  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14857                 { dragElId: "existingProxyDiv" });
14858  dd.startDrag = function(){
14859      this.constrainTo("parent-id");
14860  };
14861  </code></pre>
14862  * Or you can initalize it using the {@link Roo.Element} object:
14863  <pre><code>
14864  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14865      startDrag : function(){
14866          this.constrainTo("parent-id");
14867      }
14868  });
14869  </code></pre>
14870      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14871      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14872      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14873      * an object containing the sides to pad. For example: {right:10, bottom:10}
14874      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14875      */
14876     constrainTo : function(constrainTo, pad, inContent){
14877         if(typeof pad == "number"){
14878             pad = {left: pad, right:pad, top:pad, bottom:pad};
14879         }
14880         pad = pad || this.defaultPadding;
14881         var b = Roo.get(this.getEl()).getBox();
14882         var ce = Roo.get(constrainTo);
14883         var s = ce.getScroll();
14884         var c, cd = ce.dom;
14885         if(cd == document.body){
14886             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14887         }else{
14888             xy = ce.getXY();
14889             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14890         }
14891
14892
14893         var topSpace = b.y - c.y;
14894         var leftSpace = b.x - c.x;
14895
14896         this.resetConstraints();
14897         this.setXConstraint(leftSpace - (pad.left||0), // left
14898                 c.width - leftSpace - b.width - (pad.right||0) //right
14899         );
14900         this.setYConstraint(topSpace - (pad.top||0), //top
14901                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14902         );
14903     },
14904
14905     /**
14906      * Returns a reference to the linked element
14907      * @method getEl
14908      * @return {HTMLElement} the html element
14909      */
14910     getEl: function() {
14911         if (!this._domRef) {
14912             this._domRef = Roo.getDom(this.id);
14913         }
14914
14915         return this._domRef;
14916     },
14917
14918     /**
14919      * Returns a reference to the actual element to drag.  By default this is
14920      * the same as the html element, but it can be assigned to another
14921      * element. An example of this can be found in Roo.dd.DDProxy
14922      * @method getDragEl
14923      * @return {HTMLElement} the html element
14924      */
14925     getDragEl: function() {
14926         return Roo.getDom(this.dragElId);
14927     },
14928
14929     /**
14930      * Sets up the DragDrop object.  Must be called in the constructor of any
14931      * Roo.dd.DragDrop subclass
14932      * @method init
14933      * @param id the id of the linked element
14934      * @param {String} sGroup the group of related items
14935      * @param {object} config configuration attributes
14936      */
14937     init: function(id, sGroup, config) {
14938         this.initTarget(id, sGroup, config);
14939         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14940         // Event.on(this.id, "selectstart", Event.preventDefault);
14941     },
14942
14943     /**
14944      * Initializes Targeting functionality only... the object does not
14945      * get a mousedown handler.
14946      * @method initTarget
14947      * @param id the id of the linked element
14948      * @param {String} sGroup the group of related items
14949      * @param {object} config configuration attributes
14950      */
14951     initTarget: function(id, sGroup, config) {
14952
14953         // configuration attributes
14954         this.config = config || {};
14955
14956         // create a local reference to the drag and drop manager
14957         this.DDM = Roo.dd.DDM;
14958         // initialize the groups array
14959         this.groups = {};
14960
14961         // assume that we have an element reference instead of an id if the
14962         // parameter is not a string
14963         if (typeof id !== "string") {
14964             id = Roo.id(id);
14965         }
14966
14967         // set the id
14968         this.id = id;
14969
14970         // add to an interaction group
14971         this.addToGroup((sGroup) ? sGroup : "default");
14972
14973         // We don't want to register this as the handle with the manager
14974         // so we just set the id rather than calling the setter.
14975         this.handleElId = id;
14976
14977         // the linked element is the element that gets dragged by default
14978         this.setDragElId(id);
14979
14980         // by default, clicked anchors will not start drag operations.
14981         this.invalidHandleTypes = { A: "A" };
14982         this.invalidHandleIds = {};
14983         this.invalidHandleClasses = [];
14984
14985         this.applyConfig();
14986
14987         this.handleOnAvailable();
14988     },
14989
14990     /**
14991      * Applies the configuration parameters that were passed into the constructor.
14992      * This is supposed to happen at each level through the inheritance chain.  So
14993      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14994      * DragDrop in order to get all of the parameters that are available in
14995      * each object.
14996      * @method applyConfig
14997      */
14998     applyConfig: function() {
14999
15000         // configurable properties:
15001         //    padding, isTarget, maintainOffset, primaryButtonOnly
15002         this.padding           = this.config.padding || [0, 0, 0, 0];
15003         this.isTarget          = (this.config.isTarget !== false);
15004         this.maintainOffset    = (this.config.maintainOffset);
15005         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15006
15007     },
15008
15009     /**
15010      * Executed when the linked element is available
15011      * @method handleOnAvailable
15012      * @private
15013      */
15014     handleOnAvailable: function() {
15015         this.available = true;
15016         this.resetConstraints();
15017         this.onAvailable();
15018     },
15019
15020      /**
15021      * Configures the padding for the target zone in px.  Effectively expands
15022      * (or reduces) the virtual object size for targeting calculations.
15023      * Supports css-style shorthand; if only one parameter is passed, all sides
15024      * will have that padding, and if only two are passed, the top and bottom
15025      * will have the first param, the left and right the second.
15026      * @method setPadding
15027      * @param {int} iTop    Top pad
15028      * @param {int} iRight  Right pad
15029      * @param {int} iBot    Bot pad
15030      * @param {int} iLeft   Left pad
15031      */
15032     setPadding: function(iTop, iRight, iBot, iLeft) {
15033         // this.padding = [iLeft, iRight, iTop, iBot];
15034         if (!iRight && 0 !== iRight) {
15035             this.padding = [iTop, iTop, iTop, iTop];
15036         } else if (!iBot && 0 !== iBot) {
15037             this.padding = [iTop, iRight, iTop, iRight];
15038         } else {
15039             this.padding = [iTop, iRight, iBot, iLeft];
15040         }
15041     },
15042
15043     /**
15044      * Stores the initial placement of the linked element.
15045      * @method setInitialPosition
15046      * @param {int} diffX   the X offset, default 0
15047      * @param {int} diffY   the Y offset, default 0
15048      */
15049     setInitPosition: function(diffX, diffY) {
15050         var el = this.getEl();
15051
15052         if (!this.DDM.verifyEl(el)) {
15053             return;
15054         }
15055
15056         var dx = diffX || 0;
15057         var dy = diffY || 0;
15058
15059         var p = Dom.getXY( el );
15060
15061         this.initPageX = p[0] - dx;
15062         this.initPageY = p[1] - dy;
15063
15064         this.lastPageX = p[0];
15065         this.lastPageY = p[1];
15066
15067
15068         this.setStartPosition(p);
15069     },
15070
15071     /**
15072      * Sets the start position of the element.  This is set when the obj
15073      * is initialized, the reset when a drag is started.
15074      * @method setStartPosition
15075      * @param pos current position (from previous lookup)
15076      * @private
15077      */
15078     setStartPosition: function(pos) {
15079         var p = pos || Dom.getXY( this.getEl() );
15080         this.deltaSetXY = null;
15081
15082         this.startPageX = p[0];
15083         this.startPageY = p[1];
15084     },
15085
15086     /**
15087      * Add this instance to a group of related drag/drop objects.  All
15088      * instances belong to at least one group, and can belong to as many
15089      * groups as needed.
15090      * @method addToGroup
15091      * @param sGroup {string} the name of the group
15092      */
15093     addToGroup: function(sGroup) {
15094         this.groups[sGroup] = true;
15095         this.DDM.regDragDrop(this, sGroup);
15096     },
15097
15098     /**
15099      * Remove's this instance from the supplied interaction group
15100      * @method removeFromGroup
15101      * @param {string}  sGroup  The group to drop
15102      */
15103     removeFromGroup: function(sGroup) {
15104         if (this.groups[sGroup]) {
15105             delete this.groups[sGroup];
15106         }
15107
15108         this.DDM.removeDDFromGroup(this, sGroup);
15109     },
15110
15111     /**
15112      * Allows you to specify that an element other than the linked element
15113      * will be moved with the cursor during a drag
15114      * @method setDragElId
15115      * @param id {string} the id of the element that will be used to initiate the drag
15116      */
15117     setDragElId: function(id) {
15118         this.dragElId = id;
15119     },
15120
15121     /**
15122      * Allows you to specify a child of the linked element that should be
15123      * used to initiate the drag operation.  An example of this would be if
15124      * you have a content div with text and links.  Clicking anywhere in the
15125      * content area would normally start the drag operation.  Use this method
15126      * to specify that an element inside of the content div is the element
15127      * that starts the drag operation.
15128      * @method setHandleElId
15129      * @param id {string} the id of the element that will be used to
15130      * initiate the drag.
15131      */
15132     setHandleElId: function(id) {
15133         if (typeof id !== "string") {
15134             id = Roo.id(id);
15135         }
15136         this.handleElId = id;
15137         this.DDM.regHandle(this.id, id);
15138     },
15139
15140     /**
15141      * Allows you to set an element outside of the linked element as a drag
15142      * handle
15143      * @method setOuterHandleElId
15144      * @param id the id of the element that will be used to initiate the drag
15145      */
15146     setOuterHandleElId: function(id) {
15147         if (typeof id !== "string") {
15148             id = Roo.id(id);
15149         }
15150         Event.on(id, "mousedown",
15151                 this.handleMouseDown, this);
15152         this.setHandleElId(id);
15153
15154         this.hasOuterHandles = true;
15155     },
15156
15157     /**
15158      * Remove all drag and drop hooks for this element
15159      * @method unreg
15160      */
15161     unreg: function() {
15162         Event.un(this.id, "mousedown",
15163                 this.handleMouseDown);
15164         this._domRef = null;
15165         this.DDM._remove(this);
15166     },
15167
15168     destroy : function(){
15169         this.unreg();
15170     },
15171
15172     /**
15173      * Returns true if this instance is locked, or the drag drop mgr is locked
15174      * (meaning that all drag/drop is disabled on the page.)
15175      * @method isLocked
15176      * @return {boolean} true if this obj or all drag/drop is locked, else
15177      * false
15178      */
15179     isLocked: function() {
15180         return (this.DDM.isLocked() || this.locked);
15181     },
15182
15183     /**
15184      * Fired when this object is clicked
15185      * @method handleMouseDown
15186      * @param {Event} e
15187      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15188      * @private
15189      */
15190     handleMouseDown: function(e, oDD){
15191         if (this.primaryButtonOnly && e.button != 0) {
15192             return;
15193         }
15194
15195         if (this.isLocked()) {
15196             return;
15197         }
15198
15199         this.DDM.refreshCache(this.groups);
15200
15201         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15202         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15203         } else {
15204             if (this.clickValidator(e)) {
15205
15206                 // set the initial element position
15207                 this.setStartPosition();
15208
15209
15210                 this.b4MouseDown(e);
15211                 this.onMouseDown(e);
15212
15213                 this.DDM.handleMouseDown(e, this);
15214
15215                 this.DDM.stopEvent(e);
15216             } else {
15217
15218
15219             }
15220         }
15221     },
15222
15223     clickValidator: function(e) {
15224         var target = e.getTarget();
15225         return ( this.isValidHandleChild(target) &&
15226                     (this.id == this.handleElId ||
15227                         this.DDM.handleWasClicked(target, this.id)) );
15228     },
15229
15230     /**
15231      * Allows you to specify a tag name that should not start a drag operation
15232      * when clicked.  This is designed to facilitate embedding links within a
15233      * drag handle that do something other than start the drag.
15234      * @method addInvalidHandleType
15235      * @param {string} tagName the type of element to exclude
15236      */
15237     addInvalidHandleType: function(tagName) {
15238         var type = tagName.toUpperCase();
15239         this.invalidHandleTypes[type] = type;
15240     },
15241
15242     /**
15243      * Lets you to specify an element id for a child of a drag handle
15244      * that should not initiate a drag
15245      * @method addInvalidHandleId
15246      * @param {string} id the element id of the element you wish to ignore
15247      */
15248     addInvalidHandleId: function(id) {
15249         if (typeof id !== "string") {
15250             id = Roo.id(id);
15251         }
15252         this.invalidHandleIds[id] = id;
15253     },
15254
15255     /**
15256      * Lets you specify a css class of elements that will not initiate a drag
15257      * @method addInvalidHandleClass
15258      * @param {string} cssClass the class of the elements you wish to ignore
15259      */
15260     addInvalidHandleClass: function(cssClass) {
15261         this.invalidHandleClasses.push(cssClass);
15262     },
15263
15264     /**
15265      * Unsets an excluded tag name set by addInvalidHandleType
15266      * @method removeInvalidHandleType
15267      * @param {string} tagName the type of element to unexclude
15268      */
15269     removeInvalidHandleType: function(tagName) {
15270         var type = tagName.toUpperCase();
15271         // this.invalidHandleTypes[type] = null;
15272         delete this.invalidHandleTypes[type];
15273     },
15274
15275     /**
15276      * Unsets an invalid handle id
15277      * @method removeInvalidHandleId
15278      * @param {string} id the id of the element to re-enable
15279      */
15280     removeInvalidHandleId: function(id) {
15281         if (typeof id !== "string") {
15282             id = Roo.id(id);
15283         }
15284         delete this.invalidHandleIds[id];
15285     },
15286
15287     /**
15288      * Unsets an invalid css class
15289      * @method removeInvalidHandleClass
15290      * @param {string} cssClass the class of the element(s) you wish to
15291      * re-enable
15292      */
15293     removeInvalidHandleClass: function(cssClass) {
15294         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15295             if (this.invalidHandleClasses[i] == cssClass) {
15296                 delete this.invalidHandleClasses[i];
15297             }
15298         }
15299     },
15300
15301     /**
15302      * Checks the tag exclusion list to see if this click should be ignored
15303      * @method isValidHandleChild
15304      * @param {HTMLElement} node the HTMLElement to evaluate
15305      * @return {boolean} true if this is a valid tag type, false if not
15306      */
15307     isValidHandleChild: function(node) {
15308
15309         var valid = true;
15310         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15311         var nodeName;
15312         try {
15313             nodeName = node.nodeName.toUpperCase();
15314         } catch(e) {
15315             nodeName = node.nodeName;
15316         }
15317         valid = valid && !this.invalidHandleTypes[nodeName];
15318         valid = valid && !this.invalidHandleIds[node.id];
15319
15320         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15321             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15322         }
15323
15324
15325         return valid;
15326
15327     },
15328
15329     /**
15330      * Create the array of horizontal tick marks if an interval was specified
15331      * in setXConstraint().
15332      * @method setXTicks
15333      * @private
15334      */
15335     setXTicks: function(iStartX, iTickSize) {
15336         this.xTicks = [];
15337         this.xTickSize = iTickSize;
15338
15339         var tickMap = {};
15340
15341         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15342             if (!tickMap[i]) {
15343                 this.xTicks[this.xTicks.length] = i;
15344                 tickMap[i] = true;
15345             }
15346         }
15347
15348         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15349             if (!tickMap[i]) {
15350                 this.xTicks[this.xTicks.length] = i;
15351                 tickMap[i] = true;
15352             }
15353         }
15354
15355         this.xTicks.sort(this.DDM.numericSort) ;
15356     },
15357
15358     /**
15359      * Create the array of vertical tick marks if an interval was specified in
15360      * setYConstraint().
15361      * @method setYTicks
15362      * @private
15363      */
15364     setYTicks: function(iStartY, iTickSize) {
15365         this.yTicks = [];
15366         this.yTickSize = iTickSize;
15367
15368         var tickMap = {};
15369
15370         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15371             if (!tickMap[i]) {
15372                 this.yTicks[this.yTicks.length] = i;
15373                 tickMap[i] = true;
15374             }
15375         }
15376
15377         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15378             if (!tickMap[i]) {
15379                 this.yTicks[this.yTicks.length] = i;
15380                 tickMap[i] = true;
15381             }
15382         }
15383
15384         this.yTicks.sort(this.DDM.numericSort) ;
15385     },
15386
15387     /**
15388      * By default, the element can be dragged any place on the screen.  Use
15389      * this method to limit the horizontal travel of the element.  Pass in
15390      * 0,0 for the parameters if you want to lock the drag to the y axis.
15391      * @method setXConstraint
15392      * @param {int} iLeft the number of pixels the element can move to the left
15393      * @param {int} iRight the number of pixels the element can move to the
15394      * right
15395      * @param {int} iTickSize optional parameter for specifying that the
15396      * element
15397      * should move iTickSize pixels at a time.
15398      */
15399     setXConstraint: function(iLeft, iRight, iTickSize) {
15400         this.leftConstraint = iLeft;
15401         this.rightConstraint = iRight;
15402
15403         this.minX = this.initPageX - iLeft;
15404         this.maxX = this.initPageX + iRight;
15405         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15406
15407         this.constrainX = true;
15408     },
15409
15410     /**
15411      * Clears any constraints applied to this instance.  Also clears ticks
15412      * since they can't exist independent of a constraint at this time.
15413      * @method clearConstraints
15414      */
15415     clearConstraints: function() {
15416         this.constrainX = false;
15417         this.constrainY = false;
15418         this.clearTicks();
15419     },
15420
15421     /**
15422      * Clears any tick interval defined for this instance
15423      * @method clearTicks
15424      */
15425     clearTicks: function() {
15426         this.xTicks = null;
15427         this.yTicks = null;
15428         this.xTickSize = 0;
15429         this.yTickSize = 0;
15430     },
15431
15432     /**
15433      * By default, the element can be dragged any place on the screen.  Set
15434      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15435      * parameters if you want to lock the drag to the x axis.
15436      * @method setYConstraint
15437      * @param {int} iUp the number of pixels the element can move up
15438      * @param {int} iDown the number of pixels the element can move down
15439      * @param {int} iTickSize optional parameter for specifying that the
15440      * element should move iTickSize pixels at a time.
15441      */
15442     setYConstraint: function(iUp, iDown, iTickSize) {
15443         this.topConstraint = iUp;
15444         this.bottomConstraint = iDown;
15445
15446         this.minY = this.initPageY - iUp;
15447         this.maxY = this.initPageY + iDown;
15448         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15449
15450         this.constrainY = true;
15451
15452     },
15453
15454     /**
15455      * resetConstraints must be called if you manually reposition a dd element.
15456      * @method resetConstraints
15457      * @param {boolean} maintainOffset
15458      */
15459     resetConstraints: function() {
15460
15461
15462         // Maintain offsets if necessary
15463         if (this.initPageX || this.initPageX === 0) {
15464             // figure out how much this thing has moved
15465             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15466             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15467
15468             this.setInitPosition(dx, dy);
15469
15470         // This is the first time we have detected the element's position
15471         } else {
15472             this.setInitPosition();
15473         }
15474
15475         if (this.constrainX) {
15476             this.setXConstraint( this.leftConstraint,
15477                                  this.rightConstraint,
15478                                  this.xTickSize        );
15479         }
15480
15481         if (this.constrainY) {
15482             this.setYConstraint( this.topConstraint,
15483                                  this.bottomConstraint,
15484                                  this.yTickSize         );
15485         }
15486     },
15487
15488     /**
15489      * Normally the drag element is moved pixel by pixel, but we can specify
15490      * that it move a number of pixels at a time.  This method resolves the
15491      * location when we have it set up like this.
15492      * @method getTick
15493      * @param {int} val where we want to place the object
15494      * @param {int[]} tickArray sorted array of valid points
15495      * @return {int} the closest tick
15496      * @private
15497      */
15498     getTick: function(val, tickArray) {
15499
15500         if (!tickArray) {
15501             // If tick interval is not defined, it is effectively 1 pixel,
15502             // so we return the value passed to us.
15503             return val;
15504         } else if (tickArray[0] >= val) {
15505             // The value is lower than the first tick, so we return the first
15506             // tick.
15507             return tickArray[0];
15508         } else {
15509             for (var i=0, len=tickArray.length; i<len; ++i) {
15510                 var next = i + 1;
15511                 if (tickArray[next] && tickArray[next] >= val) {
15512                     var diff1 = val - tickArray[i];
15513                     var diff2 = tickArray[next] - val;
15514                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15515                 }
15516             }
15517
15518             // The value is larger than the last tick, so we return the last
15519             // tick.
15520             return tickArray[tickArray.length - 1];
15521         }
15522     },
15523
15524     /**
15525      * toString method
15526      * @method toString
15527      * @return {string} string representation of the dd obj
15528      */
15529     toString: function() {
15530         return ("DragDrop " + this.id);
15531     }
15532
15533 };
15534
15535 })();
15536 /*
15537  * Based on:
15538  * Ext JS Library 1.1.1
15539  * Copyright(c) 2006-2007, Ext JS, LLC.
15540  *
15541  * Originally Released Under LGPL - original licence link has changed is not relivant.
15542  *
15543  * Fork - LGPL
15544  * <script type="text/javascript">
15545  */
15546
15547
15548 /**
15549  * The drag and drop utility provides a framework for building drag and drop
15550  * applications.  In addition to enabling drag and drop for specific elements,
15551  * the drag and drop elements are tracked by the manager class, and the
15552  * interactions between the various elements are tracked during the drag and
15553  * the implementing code is notified about these important moments.
15554  */
15555
15556 // Only load the library once.  Rewriting the manager class would orphan
15557 // existing drag and drop instances.
15558 if (!Roo.dd.DragDropMgr) {
15559
15560 /**
15561  * @class Roo.dd.DragDropMgr
15562  * DragDropMgr is a singleton that tracks the element interaction for
15563  * all DragDrop items in the window.  Generally, you will not call
15564  * this class directly, but it does have helper methods that could
15565  * be useful in your DragDrop implementations.
15566  * @singleton
15567  */
15568 Roo.dd.DragDropMgr = function() {
15569
15570     var Event = Roo.EventManager;
15571
15572     return {
15573
15574         /**
15575          * Two dimensional Array of registered DragDrop objects.  The first
15576          * dimension is the DragDrop item group, the second the DragDrop
15577          * object.
15578          * @property ids
15579          * @type {string: string}
15580          * @private
15581          * @static
15582          */
15583         ids: {},
15584
15585         /**
15586          * Array of element ids defined as drag handles.  Used to determine
15587          * if the element that generated the mousedown event is actually the
15588          * handle and not the html element itself.
15589          * @property handleIds
15590          * @type {string: string}
15591          * @private
15592          * @static
15593          */
15594         handleIds: {},
15595
15596         /**
15597          * the DragDrop object that is currently being dragged
15598          * @property dragCurrent
15599          * @type DragDrop
15600          * @private
15601          * @static
15602          **/
15603         dragCurrent: null,
15604
15605         /**
15606          * the DragDrop object(s) that are being hovered over
15607          * @property dragOvers
15608          * @type Array
15609          * @private
15610          * @static
15611          */
15612         dragOvers: {},
15613
15614         /**
15615          * the X distance between the cursor and the object being dragged
15616          * @property deltaX
15617          * @type int
15618          * @private
15619          * @static
15620          */
15621         deltaX: 0,
15622
15623         /**
15624          * the Y distance between the cursor and the object being dragged
15625          * @property deltaY
15626          * @type int
15627          * @private
15628          * @static
15629          */
15630         deltaY: 0,
15631
15632         /**
15633          * Flag to determine if we should prevent the default behavior of the
15634          * events we define. By default this is true, but this can be set to
15635          * false if you need the default behavior (not recommended)
15636          * @property preventDefault
15637          * @type boolean
15638          * @static
15639          */
15640         preventDefault: true,
15641
15642         /**
15643          * Flag to determine if we should stop the propagation of the events
15644          * we generate. This is true by default but you may want to set it to
15645          * false if the html element contains other features that require the
15646          * mouse click.
15647          * @property stopPropagation
15648          * @type boolean
15649          * @static
15650          */
15651         stopPropagation: true,
15652
15653         /**
15654          * Internal flag that is set to true when drag and drop has been
15655          * intialized
15656          * @property initialized
15657          * @private
15658          * @static
15659          */
15660         initalized: false,
15661
15662         /**
15663          * All drag and drop can be disabled.
15664          * @property locked
15665          * @private
15666          * @static
15667          */
15668         locked: false,
15669
15670         /**
15671          * Called the first time an element is registered.
15672          * @method init
15673          * @private
15674          * @static
15675          */
15676         init: function() {
15677             this.initialized = true;
15678         },
15679
15680         /**
15681          * In point mode, drag and drop interaction is defined by the
15682          * location of the cursor during the drag/drop
15683          * @property POINT
15684          * @type int
15685          * @static
15686          */
15687         POINT: 0,
15688
15689         /**
15690          * In intersect mode, drag and drop interactio nis defined by the
15691          * overlap of two or more drag and drop objects.
15692          * @property INTERSECT
15693          * @type int
15694          * @static
15695          */
15696         INTERSECT: 1,
15697
15698         /**
15699          * The current drag and drop mode.  Default: POINT
15700          * @property mode
15701          * @type int
15702          * @static
15703          */
15704         mode: 0,
15705
15706         /**
15707          * Runs method on all drag and drop objects
15708          * @method _execOnAll
15709          * @private
15710          * @static
15711          */
15712         _execOnAll: function(sMethod, args) {
15713             for (var i in this.ids) {
15714                 for (var j in this.ids[i]) {
15715                     var oDD = this.ids[i][j];
15716                     if (! this.isTypeOfDD(oDD)) {
15717                         continue;
15718                     }
15719                     oDD[sMethod].apply(oDD, args);
15720                 }
15721             }
15722         },
15723
15724         /**
15725          * Drag and drop initialization.  Sets up the global event handlers
15726          * @method _onLoad
15727          * @private
15728          * @static
15729          */
15730         _onLoad: function() {
15731
15732             this.init();
15733
15734
15735             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15736             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15737             Event.on(window,   "unload",    this._onUnload, this, true);
15738             Event.on(window,   "resize",    this._onResize, this, true);
15739             // Event.on(window,   "mouseout",    this._test);
15740
15741         },
15742
15743         /**
15744          * Reset constraints on all drag and drop objs
15745          * @method _onResize
15746          * @private
15747          * @static
15748          */
15749         _onResize: function(e) {
15750             this._execOnAll("resetConstraints", []);
15751         },
15752
15753         /**
15754          * Lock all drag and drop functionality
15755          * @method lock
15756          * @static
15757          */
15758         lock: function() { this.locked = true; },
15759
15760         /**
15761          * Unlock all drag and drop functionality
15762          * @method unlock
15763          * @static
15764          */
15765         unlock: function() { this.locked = false; },
15766
15767         /**
15768          * Is drag and drop locked?
15769          * @method isLocked
15770          * @return {boolean} True if drag and drop is locked, false otherwise.
15771          * @static
15772          */
15773         isLocked: function() { return this.locked; },
15774
15775         /**
15776          * Location cache that is set for all drag drop objects when a drag is
15777          * initiated, cleared when the drag is finished.
15778          * @property locationCache
15779          * @private
15780          * @static
15781          */
15782         locationCache: {},
15783
15784         /**
15785          * Set useCache to false if you want to force object the lookup of each
15786          * drag and drop linked element constantly during a drag.
15787          * @property useCache
15788          * @type boolean
15789          * @static
15790          */
15791         useCache: true,
15792
15793         /**
15794          * The number of pixels that the mouse needs to move after the
15795          * mousedown before the drag is initiated.  Default=3;
15796          * @property clickPixelThresh
15797          * @type int
15798          * @static
15799          */
15800         clickPixelThresh: 3,
15801
15802         /**
15803          * The number of milliseconds after the mousedown event to initiate the
15804          * drag if we don't get a mouseup event. Default=1000
15805          * @property clickTimeThresh
15806          * @type int
15807          * @static
15808          */
15809         clickTimeThresh: 350,
15810
15811         /**
15812          * Flag that indicates that either the drag pixel threshold or the
15813          * mousdown time threshold has been met
15814          * @property dragThreshMet
15815          * @type boolean
15816          * @private
15817          * @static
15818          */
15819         dragThreshMet: false,
15820
15821         /**
15822          * Timeout used for the click time threshold
15823          * @property clickTimeout
15824          * @type Object
15825          * @private
15826          * @static
15827          */
15828         clickTimeout: null,
15829
15830         /**
15831          * The X position of the mousedown event stored for later use when a
15832          * drag threshold is met.
15833          * @property startX
15834          * @type int
15835          * @private
15836          * @static
15837          */
15838         startX: 0,
15839
15840         /**
15841          * The Y position of the mousedown event stored for later use when a
15842          * drag threshold is met.
15843          * @property startY
15844          * @type int
15845          * @private
15846          * @static
15847          */
15848         startY: 0,
15849
15850         /**
15851          * Each DragDrop instance must be registered with the DragDropMgr.
15852          * This is executed in DragDrop.init()
15853          * @method regDragDrop
15854          * @param {DragDrop} oDD the DragDrop object to register
15855          * @param {String} sGroup the name of the group this element belongs to
15856          * @static
15857          */
15858         regDragDrop: function(oDD, sGroup) {
15859             if (!this.initialized) { this.init(); }
15860
15861             if (!this.ids[sGroup]) {
15862                 this.ids[sGroup] = {};
15863             }
15864             this.ids[sGroup][oDD.id] = oDD;
15865         },
15866
15867         /**
15868          * Removes the supplied dd instance from the supplied group. Executed
15869          * by DragDrop.removeFromGroup, so don't call this function directly.
15870          * @method removeDDFromGroup
15871          * @private
15872          * @static
15873          */
15874         removeDDFromGroup: function(oDD, sGroup) {
15875             if (!this.ids[sGroup]) {
15876                 this.ids[sGroup] = {};
15877             }
15878
15879             var obj = this.ids[sGroup];
15880             if (obj && obj[oDD.id]) {
15881                 delete obj[oDD.id];
15882             }
15883         },
15884
15885         /**
15886          * Unregisters a drag and drop item.  This is executed in
15887          * DragDrop.unreg, use that method instead of calling this directly.
15888          * @method _remove
15889          * @private
15890          * @static
15891          */
15892         _remove: function(oDD) {
15893             for (var g in oDD.groups) {
15894                 if (g && this.ids[g][oDD.id]) {
15895                     delete this.ids[g][oDD.id];
15896                 }
15897             }
15898             delete this.handleIds[oDD.id];
15899         },
15900
15901         /**
15902          * Each DragDrop handle element must be registered.  This is done
15903          * automatically when executing DragDrop.setHandleElId()
15904          * @method regHandle
15905          * @param {String} sDDId the DragDrop id this element is a handle for
15906          * @param {String} sHandleId the id of the element that is the drag
15907          * handle
15908          * @static
15909          */
15910         regHandle: function(sDDId, sHandleId) {
15911             if (!this.handleIds[sDDId]) {
15912                 this.handleIds[sDDId] = {};
15913             }
15914             this.handleIds[sDDId][sHandleId] = sHandleId;
15915         },
15916
15917         /**
15918          * Utility function to determine if a given element has been
15919          * registered as a drag drop item.
15920          * @method isDragDrop
15921          * @param {String} id the element id to check
15922          * @return {boolean} true if this element is a DragDrop item,
15923          * false otherwise
15924          * @static
15925          */
15926         isDragDrop: function(id) {
15927             return ( this.getDDById(id) ) ? true : false;
15928         },
15929
15930         /**
15931          * Returns the drag and drop instances that are in all groups the
15932          * passed in instance belongs to.
15933          * @method getRelated
15934          * @param {DragDrop} p_oDD the obj to get related data for
15935          * @param {boolean} bTargetsOnly if true, only return targetable objs
15936          * @return {DragDrop[]} the related instances
15937          * @static
15938          */
15939         getRelated: function(p_oDD, bTargetsOnly) {
15940             var oDDs = [];
15941             for (var i in p_oDD.groups) {
15942                 for (j in this.ids[i]) {
15943                     var dd = this.ids[i][j];
15944                     if (! this.isTypeOfDD(dd)) {
15945                         continue;
15946                     }
15947                     if (!bTargetsOnly || dd.isTarget) {
15948                         oDDs[oDDs.length] = dd;
15949                     }
15950                 }
15951             }
15952
15953             return oDDs;
15954         },
15955
15956         /**
15957          * Returns true if the specified dd target is a legal target for
15958          * the specifice drag obj
15959          * @method isLegalTarget
15960          * @param {DragDrop} the drag obj
15961          * @param {DragDrop} the target
15962          * @return {boolean} true if the target is a legal target for the
15963          * dd obj
15964          * @static
15965          */
15966         isLegalTarget: function (oDD, oTargetDD) {
15967             var targets = this.getRelated(oDD, true);
15968             for (var i=0, len=targets.length;i<len;++i) {
15969                 if (targets[i].id == oTargetDD.id) {
15970                     return true;
15971                 }
15972             }
15973
15974             return false;
15975         },
15976
15977         /**
15978          * My goal is to be able to transparently determine if an object is
15979          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15980          * returns "object", oDD.constructor.toString() always returns
15981          * "DragDrop" and not the name of the subclass.  So for now it just
15982          * evaluates a well-known variable in DragDrop.
15983          * @method isTypeOfDD
15984          * @param {Object} the object to evaluate
15985          * @return {boolean} true if typeof oDD = DragDrop
15986          * @static
15987          */
15988         isTypeOfDD: function (oDD) {
15989             return (oDD && oDD.__ygDragDrop);
15990         },
15991
15992         /**
15993          * Utility function to determine if a given element has been
15994          * registered as a drag drop handle for the given Drag Drop object.
15995          * @method isHandle
15996          * @param {String} id the element id to check
15997          * @return {boolean} true if this element is a DragDrop handle, false
15998          * otherwise
15999          * @static
16000          */
16001         isHandle: function(sDDId, sHandleId) {
16002             return ( this.handleIds[sDDId] &&
16003                             this.handleIds[sDDId][sHandleId] );
16004         },
16005
16006         /**
16007          * Returns the DragDrop instance for a given id
16008          * @method getDDById
16009          * @param {String} id the id of the DragDrop object
16010          * @return {DragDrop} the drag drop object, null if it is not found
16011          * @static
16012          */
16013         getDDById: function(id) {
16014             for (var i in this.ids) {
16015                 if (this.ids[i][id]) {
16016                     return this.ids[i][id];
16017                 }
16018             }
16019             return null;
16020         },
16021
16022         /**
16023          * Fired after a registered DragDrop object gets the mousedown event.
16024          * Sets up the events required to track the object being dragged
16025          * @method handleMouseDown
16026          * @param {Event} e the event
16027          * @param oDD the DragDrop object being dragged
16028          * @private
16029          * @static
16030          */
16031         handleMouseDown: function(e, oDD) {
16032             if(Roo.QuickTips){
16033                 Roo.QuickTips.disable();
16034             }
16035             this.currentTarget = e.getTarget();
16036
16037             this.dragCurrent = oDD;
16038
16039             var el = oDD.getEl();
16040
16041             // track start position
16042             this.startX = e.getPageX();
16043             this.startY = e.getPageY();
16044
16045             this.deltaX = this.startX - el.offsetLeft;
16046             this.deltaY = this.startY - el.offsetTop;
16047
16048             this.dragThreshMet = false;
16049
16050             this.clickTimeout = setTimeout(
16051                     function() {
16052                         var DDM = Roo.dd.DDM;
16053                         DDM.startDrag(DDM.startX, DDM.startY);
16054                     },
16055                     this.clickTimeThresh );
16056         },
16057
16058         /**
16059          * Fired when either the drag pixel threshol or the mousedown hold
16060          * time threshold has been met.
16061          * @method startDrag
16062          * @param x {int} the X position of the original mousedown
16063          * @param y {int} the Y position of the original mousedown
16064          * @static
16065          */
16066         startDrag: function(x, y) {
16067             clearTimeout(this.clickTimeout);
16068             if (this.dragCurrent) {
16069                 this.dragCurrent.b4StartDrag(x, y);
16070                 this.dragCurrent.startDrag(x, y);
16071             }
16072             this.dragThreshMet = true;
16073         },
16074
16075         /**
16076          * Internal function to handle the mouseup event.  Will be invoked
16077          * from the context of the document.
16078          * @method handleMouseUp
16079          * @param {Event} e the event
16080          * @private
16081          * @static
16082          */
16083         handleMouseUp: function(e) {
16084
16085             if(Roo.QuickTips){
16086                 Roo.QuickTips.enable();
16087             }
16088             if (! this.dragCurrent) {
16089                 return;
16090             }
16091
16092             clearTimeout(this.clickTimeout);
16093
16094             if (this.dragThreshMet) {
16095                 this.fireEvents(e, true);
16096             } else {
16097             }
16098
16099             this.stopDrag(e);
16100
16101             this.stopEvent(e);
16102         },
16103
16104         /**
16105          * Utility to stop event propagation and event default, if these
16106          * features are turned on.
16107          * @method stopEvent
16108          * @param {Event} e the event as returned by this.getEvent()
16109          * @static
16110          */
16111         stopEvent: function(e){
16112             if(this.stopPropagation) {
16113                 e.stopPropagation();
16114             }
16115
16116             if (this.preventDefault) {
16117                 e.preventDefault();
16118             }
16119         },
16120
16121         /**
16122          * Internal function to clean up event handlers after the drag
16123          * operation is complete
16124          * @method stopDrag
16125          * @param {Event} e the event
16126          * @private
16127          * @static
16128          */
16129         stopDrag: function(e) {
16130             // Fire the drag end event for the item that was dragged
16131             if (this.dragCurrent) {
16132                 if (this.dragThreshMet) {
16133                     this.dragCurrent.b4EndDrag(e);
16134                     this.dragCurrent.endDrag(e);
16135                 }
16136
16137                 this.dragCurrent.onMouseUp(e);
16138             }
16139
16140             this.dragCurrent = null;
16141             this.dragOvers = {};
16142         },
16143
16144         /**
16145          * Internal function to handle the mousemove event.  Will be invoked
16146          * from the context of the html element.
16147          *
16148          * @TODO figure out what we can do about mouse events lost when the
16149          * user drags objects beyond the window boundary.  Currently we can
16150          * detect this in internet explorer by verifying that the mouse is
16151          * down during the mousemove event.  Firefox doesn't give us the
16152          * button state on the mousemove event.
16153          * @method handleMouseMove
16154          * @param {Event} e the event
16155          * @private
16156          * @static
16157          */
16158         handleMouseMove: function(e) {
16159             if (! this.dragCurrent) {
16160                 return true;
16161             }
16162
16163             // var button = e.which || e.button;
16164
16165             // check for IE mouseup outside of page boundary
16166             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16167                 this.stopEvent(e);
16168                 return this.handleMouseUp(e);
16169             }
16170
16171             if (!this.dragThreshMet) {
16172                 var diffX = Math.abs(this.startX - e.getPageX());
16173                 var diffY = Math.abs(this.startY - e.getPageY());
16174                 if (diffX > this.clickPixelThresh ||
16175                             diffY > this.clickPixelThresh) {
16176                     this.startDrag(this.startX, this.startY);
16177                 }
16178             }
16179
16180             if (this.dragThreshMet) {
16181                 this.dragCurrent.b4Drag(e);
16182                 this.dragCurrent.onDrag(e);
16183                 if(!this.dragCurrent.moveOnly){
16184                     this.fireEvents(e, false);
16185                 }
16186             }
16187
16188             this.stopEvent(e);
16189
16190             return true;
16191         },
16192
16193         /**
16194          * Iterates over all of the DragDrop elements to find ones we are
16195          * hovering over or dropping on
16196          * @method fireEvents
16197          * @param {Event} e the event
16198          * @param {boolean} isDrop is this a drop op or a mouseover op?
16199          * @private
16200          * @static
16201          */
16202         fireEvents: function(e, isDrop) {
16203             var dc = this.dragCurrent;
16204
16205             // If the user did the mouse up outside of the window, we could
16206             // get here even though we have ended the drag.
16207             if (!dc || dc.isLocked()) {
16208                 return;
16209             }
16210
16211             var pt = e.getPoint();
16212
16213             // cache the previous dragOver array
16214             var oldOvers = [];
16215
16216             var outEvts   = [];
16217             var overEvts  = [];
16218             var dropEvts  = [];
16219             var enterEvts = [];
16220
16221             // Check to see if the object(s) we were hovering over is no longer
16222             // being hovered over so we can fire the onDragOut event
16223             for (var i in this.dragOvers) {
16224
16225                 var ddo = this.dragOvers[i];
16226
16227                 if (! this.isTypeOfDD(ddo)) {
16228                     continue;
16229                 }
16230
16231                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16232                     outEvts.push( ddo );
16233                 }
16234
16235                 oldOvers[i] = true;
16236                 delete this.dragOvers[i];
16237             }
16238
16239             for (var sGroup in dc.groups) {
16240
16241                 if ("string" != typeof sGroup) {
16242                     continue;
16243                 }
16244
16245                 for (i in this.ids[sGroup]) {
16246                     var oDD = this.ids[sGroup][i];
16247                     if (! this.isTypeOfDD(oDD)) {
16248                         continue;
16249                     }
16250
16251                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16252                         if (this.isOverTarget(pt, oDD, this.mode)) {
16253                             // look for drop interactions
16254                             if (isDrop) {
16255                                 dropEvts.push( oDD );
16256                             // look for drag enter and drag over interactions
16257                             } else {
16258
16259                                 // initial drag over: dragEnter fires
16260                                 if (!oldOvers[oDD.id]) {
16261                                     enterEvts.push( oDD );
16262                                 // subsequent drag overs: dragOver fires
16263                                 } else {
16264                                     overEvts.push( oDD );
16265                                 }
16266
16267                                 this.dragOvers[oDD.id] = oDD;
16268                             }
16269                         }
16270                     }
16271                 }
16272             }
16273
16274             if (this.mode) {
16275                 if (outEvts.length) {
16276                     dc.b4DragOut(e, outEvts);
16277                     dc.onDragOut(e, outEvts);
16278                 }
16279
16280                 if (enterEvts.length) {
16281                     dc.onDragEnter(e, enterEvts);
16282                 }
16283
16284                 if (overEvts.length) {
16285                     dc.b4DragOver(e, overEvts);
16286                     dc.onDragOver(e, overEvts);
16287                 }
16288
16289                 if (dropEvts.length) {
16290                     dc.b4DragDrop(e, dropEvts);
16291                     dc.onDragDrop(e, dropEvts);
16292                 }
16293
16294             } else {
16295                 // fire dragout events
16296                 var len = 0;
16297                 for (i=0, len=outEvts.length; i<len; ++i) {
16298                     dc.b4DragOut(e, outEvts[i].id);
16299                     dc.onDragOut(e, outEvts[i].id);
16300                 }
16301
16302                 // fire enter events
16303                 for (i=0,len=enterEvts.length; i<len; ++i) {
16304                     // dc.b4DragEnter(e, oDD.id);
16305                     dc.onDragEnter(e, enterEvts[i].id);
16306                 }
16307
16308                 // fire over events
16309                 for (i=0,len=overEvts.length; i<len; ++i) {
16310                     dc.b4DragOver(e, overEvts[i].id);
16311                     dc.onDragOver(e, overEvts[i].id);
16312                 }
16313
16314                 // fire drop events
16315                 for (i=0, len=dropEvts.length; i<len; ++i) {
16316                     dc.b4DragDrop(e, dropEvts[i].id);
16317                     dc.onDragDrop(e, dropEvts[i].id);
16318                 }
16319
16320             }
16321
16322             // notify about a drop that did not find a target
16323             if (isDrop && !dropEvts.length) {
16324                 dc.onInvalidDrop(e);
16325             }
16326
16327         },
16328
16329         /**
16330          * Helper function for getting the best match from the list of drag
16331          * and drop objects returned by the drag and drop events when we are
16332          * in INTERSECT mode.  It returns either the first object that the
16333          * cursor is over, or the object that has the greatest overlap with
16334          * the dragged element.
16335          * @method getBestMatch
16336          * @param  {DragDrop[]} dds The array of drag and drop objects
16337          * targeted
16338          * @return {DragDrop}       The best single match
16339          * @static
16340          */
16341         getBestMatch: function(dds) {
16342             var winner = null;
16343             // Return null if the input is not what we expect
16344             //if (!dds || !dds.length || dds.length == 0) {
16345                // winner = null;
16346             // If there is only one item, it wins
16347             //} else if (dds.length == 1) {
16348
16349             var len = dds.length;
16350
16351             if (len == 1) {
16352                 winner = dds[0];
16353             } else {
16354                 // Loop through the targeted items
16355                 for (var i=0; i<len; ++i) {
16356                     var dd = dds[i];
16357                     // If the cursor is over the object, it wins.  If the
16358                     // cursor is over multiple matches, the first one we come
16359                     // to wins.
16360                     if (dd.cursorIsOver) {
16361                         winner = dd;
16362                         break;
16363                     // Otherwise the object with the most overlap wins
16364                     } else {
16365                         if (!winner ||
16366                             winner.overlap.getArea() < dd.overlap.getArea()) {
16367                             winner = dd;
16368                         }
16369                     }
16370                 }
16371             }
16372
16373             return winner;
16374         },
16375
16376         /**
16377          * Refreshes the cache of the top-left and bottom-right points of the
16378          * drag and drop objects in the specified group(s).  This is in the
16379          * format that is stored in the drag and drop instance, so typical
16380          * usage is:
16381          * <code>
16382          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16383          * </code>
16384          * Alternatively:
16385          * <code>
16386          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16387          * </code>
16388          * @TODO this really should be an indexed array.  Alternatively this
16389          * method could accept both.
16390          * @method refreshCache
16391          * @param {Object} groups an associative array of groups to refresh
16392          * @static
16393          */
16394         refreshCache: function(groups) {
16395             for (var sGroup in groups) {
16396                 if ("string" != typeof sGroup) {
16397                     continue;
16398                 }
16399                 for (var i in this.ids[sGroup]) {
16400                     var oDD = this.ids[sGroup][i];
16401
16402                     if (this.isTypeOfDD(oDD)) {
16403                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16404                         var loc = this.getLocation(oDD);
16405                         if (loc) {
16406                             this.locationCache[oDD.id] = loc;
16407                         } else {
16408                             delete this.locationCache[oDD.id];
16409                             // this will unregister the drag and drop object if
16410                             // the element is not in a usable state
16411                             // oDD.unreg();
16412                         }
16413                     }
16414                 }
16415             }
16416         },
16417
16418         /**
16419          * This checks to make sure an element exists and is in the DOM.  The
16420          * main purpose is to handle cases where innerHTML is used to remove
16421          * drag and drop objects from the DOM.  IE provides an 'unspecified
16422          * error' when trying to access the offsetParent of such an element
16423          * @method verifyEl
16424          * @param {HTMLElement} el the element to check
16425          * @return {boolean} true if the element looks usable
16426          * @static
16427          */
16428         verifyEl: function(el) {
16429             if (el) {
16430                 var parent;
16431                 if(Roo.isIE){
16432                     try{
16433                         parent = el.offsetParent;
16434                     }catch(e){}
16435                 }else{
16436                     parent = el.offsetParent;
16437                 }
16438                 if (parent) {
16439                     return true;
16440                 }
16441             }
16442
16443             return false;
16444         },
16445
16446         /**
16447          * Returns a Region object containing the drag and drop element's position
16448          * and size, including the padding configured for it
16449          * @method getLocation
16450          * @param {DragDrop} oDD the drag and drop object to get the
16451          *                       location for
16452          * @return {Roo.lib.Region} a Region object representing the total area
16453          *                             the element occupies, including any padding
16454          *                             the instance is configured for.
16455          * @static
16456          */
16457         getLocation: function(oDD) {
16458             if (! this.isTypeOfDD(oDD)) {
16459                 return null;
16460             }
16461
16462             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16463
16464             try {
16465                 pos= Roo.lib.Dom.getXY(el);
16466             } catch (e) { }
16467
16468             if (!pos) {
16469                 return null;
16470             }
16471
16472             x1 = pos[0];
16473             x2 = x1 + el.offsetWidth;
16474             y1 = pos[1];
16475             y2 = y1 + el.offsetHeight;
16476
16477             t = y1 - oDD.padding[0];
16478             r = x2 + oDD.padding[1];
16479             b = y2 + oDD.padding[2];
16480             l = x1 - oDD.padding[3];
16481
16482             return new Roo.lib.Region( t, r, b, l );
16483         },
16484
16485         /**
16486          * Checks the cursor location to see if it over the target
16487          * @method isOverTarget
16488          * @param {Roo.lib.Point} pt The point to evaluate
16489          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16490          * @return {boolean} true if the mouse is over the target
16491          * @private
16492          * @static
16493          */
16494         isOverTarget: function(pt, oTarget, intersect) {
16495             // use cache if available
16496             var loc = this.locationCache[oTarget.id];
16497             if (!loc || !this.useCache) {
16498                 loc = this.getLocation(oTarget);
16499                 this.locationCache[oTarget.id] = loc;
16500
16501             }
16502
16503             if (!loc) {
16504                 return false;
16505             }
16506
16507             oTarget.cursorIsOver = loc.contains( pt );
16508
16509             // DragDrop is using this as a sanity check for the initial mousedown
16510             // in this case we are done.  In POINT mode, if the drag obj has no
16511             // contraints, we are also done. Otherwise we need to evaluate the
16512             // location of the target as related to the actual location of the
16513             // dragged element.
16514             var dc = this.dragCurrent;
16515             if (!dc || !dc.getTargetCoord ||
16516                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16517                 return oTarget.cursorIsOver;
16518             }
16519
16520             oTarget.overlap = null;
16521
16522             // Get the current location of the drag element, this is the
16523             // location of the mouse event less the delta that represents
16524             // where the original mousedown happened on the element.  We
16525             // need to consider constraints and ticks as well.
16526             var pos = dc.getTargetCoord(pt.x, pt.y);
16527
16528             var el = dc.getDragEl();
16529             var curRegion = new Roo.lib.Region( pos.y,
16530                                                    pos.x + el.offsetWidth,
16531                                                    pos.y + el.offsetHeight,
16532                                                    pos.x );
16533
16534             var overlap = curRegion.intersect(loc);
16535
16536             if (overlap) {
16537                 oTarget.overlap = overlap;
16538                 return (intersect) ? true : oTarget.cursorIsOver;
16539             } else {
16540                 return false;
16541             }
16542         },
16543
16544         /**
16545          * unload event handler
16546          * @method _onUnload
16547          * @private
16548          * @static
16549          */
16550         _onUnload: function(e, me) {
16551             Roo.dd.DragDropMgr.unregAll();
16552         },
16553
16554         /**
16555          * Cleans up the drag and drop events and objects.
16556          * @method unregAll
16557          * @private
16558          * @static
16559          */
16560         unregAll: function() {
16561
16562             if (this.dragCurrent) {
16563                 this.stopDrag();
16564                 this.dragCurrent = null;
16565             }
16566
16567             this._execOnAll("unreg", []);
16568
16569             for (i in this.elementCache) {
16570                 delete this.elementCache[i];
16571             }
16572
16573             this.elementCache = {};
16574             this.ids = {};
16575         },
16576
16577         /**
16578          * A cache of DOM elements
16579          * @property elementCache
16580          * @private
16581          * @static
16582          */
16583         elementCache: {},
16584
16585         /**
16586          * Get the wrapper for the DOM element specified
16587          * @method getElWrapper
16588          * @param {String} id the id of the element to get
16589          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16590          * @private
16591          * @deprecated This wrapper isn't that useful
16592          * @static
16593          */
16594         getElWrapper: function(id) {
16595             var oWrapper = this.elementCache[id];
16596             if (!oWrapper || !oWrapper.el) {
16597                 oWrapper = this.elementCache[id] =
16598                     new this.ElementWrapper(Roo.getDom(id));
16599             }
16600             return oWrapper;
16601         },
16602
16603         /**
16604          * Returns the actual DOM element
16605          * @method getElement
16606          * @param {String} id the id of the elment to get
16607          * @return {Object} The element
16608          * @deprecated use Roo.getDom instead
16609          * @static
16610          */
16611         getElement: function(id) {
16612             return Roo.getDom(id);
16613         },
16614
16615         /**
16616          * Returns the style property for the DOM element (i.e.,
16617          * document.getElById(id).style)
16618          * @method getCss
16619          * @param {String} id the id of the elment to get
16620          * @return {Object} The style property of the element
16621          * @deprecated use Roo.getDom instead
16622          * @static
16623          */
16624         getCss: function(id) {
16625             var el = Roo.getDom(id);
16626             return (el) ? el.style : null;
16627         },
16628
16629         /**
16630          * Inner class for cached elements
16631          * @class DragDropMgr.ElementWrapper
16632          * @for DragDropMgr
16633          * @private
16634          * @deprecated
16635          */
16636         ElementWrapper: function(el) {
16637                 /**
16638                  * The element
16639                  * @property el
16640                  */
16641                 this.el = el || null;
16642                 /**
16643                  * The element id
16644                  * @property id
16645                  */
16646                 this.id = this.el && el.id;
16647                 /**
16648                  * A reference to the style property
16649                  * @property css
16650                  */
16651                 this.css = this.el && el.style;
16652             },
16653
16654         /**
16655          * Returns the X position of an html element
16656          * @method getPosX
16657          * @param el the element for which to get the position
16658          * @return {int} the X coordinate
16659          * @for DragDropMgr
16660          * @deprecated use Roo.lib.Dom.getX instead
16661          * @static
16662          */
16663         getPosX: function(el) {
16664             return Roo.lib.Dom.getX(el);
16665         },
16666
16667         /**
16668          * Returns the Y position of an html element
16669          * @method getPosY
16670          * @param el the element for which to get the position
16671          * @return {int} the Y coordinate
16672          * @deprecated use Roo.lib.Dom.getY instead
16673          * @static
16674          */
16675         getPosY: function(el) {
16676             return Roo.lib.Dom.getY(el);
16677         },
16678
16679         /**
16680          * Swap two nodes.  In IE, we use the native method, for others we
16681          * emulate the IE behavior
16682          * @method swapNode
16683          * @param n1 the first node to swap
16684          * @param n2 the other node to swap
16685          * @static
16686          */
16687         swapNode: function(n1, n2) {
16688             if (n1.swapNode) {
16689                 n1.swapNode(n2);
16690             } else {
16691                 var p = n2.parentNode;
16692                 var s = n2.nextSibling;
16693
16694                 if (s == n1) {
16695                     p.insertBefore(n1, n2);
16696                 } else if (n2 == n1.nextSibling) {
16697                     p.insertBefore(n2, n1);
16698                 } else {
16699                     n1.parentNode.replaceChild(n2, n1);
16700                     p.insertBefore(n1, s);
16701                 }
16702             }
16703         },
16704
16705         /**
16706          * Returns the current scroll position
16707          * @method getScroll
16708          * @private
16709          * @static
16710          */
16711         getScroll: function () {
16712             var t, l, dde=document.documentElement, db=document.body;
16713             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16714                 t = dde.scrollTop;
16715                 l = dde.scrollLeft;
16716             } else if (db) {
16717                 t = db.scrollTop;
16718                 l = db.scrollLeft;
16719             } else {
16720
16721             }
16722             return { top: t, left: l };
16723         },
16724
16725         /**
16726          * Returns the specified element style property
16727          * @method getStyle
16728          * @param {HTMLElement} el          the element
16729          * @param {string}      styleProp   the style property
16730          * @return {string} The value of the style property
16731          * @deprecated use Roo.lib.Dom.getStyle
16732          * @static
16733          */
16734         getStyle: function(el, styleProp) {
16735             return Roo.fly(el).getStyle(styleProp);
16736         },
16737
16738         /**
16739          * Gets the scrollTop
16740          * @method getScrollTop
16741          * @return {int} the document's scrollTop
16742          * @static
16743          */
16744         getScrollTop: function () { return this.getScroll().top; },
16745
16746         /**
16747          * Gets the scrollLeft
16748          * @method getScrollLeft
16749          * @return {int} the document's scrollTop
16750          * @static
16751          */
16752         getScrollLeft: function () { return this.getScroll().left; },
16753
16754         /**
16755          * Sets the x/y position of an element to the location of the
16756          * target element.
16757          * @method moveToEl
16758          * @param {HTMLElement} moveEl      The element to move
16759          * @param {HTMLElement} targetEl    The position reference element
16760          * @static
16761          */
16762         moveToEl: function (moveEl, targetEl) {
16763             var aCoord = Roo.lib.Dom.getXY(targetEl);
16764             Roo.lib.Dom.setXY(moveEl, aCoord);
16765         },
16766
16767         /**
16768          * Numeric array sort function
16769          * @method numericSort
16770          * @static
16771          */
16772         numericSort: function(a, b) { return (a - b); },
16773
16774         /**
16775          * Internal counter
16776          * @property _timeoutCount
16777          * @private
16778          * @static
16779          */
16780         _timeoutCount: 0,
16781
16782         /**
16783          * Trying to make the load order less important.  Without this we get
16784          * an error if this file is loaded before the Event Utility.
16785          * @method _addListeners
16786          * @private
16787          * @static
16788          */
16789         _addListeners: function() {
16790             var DDM = Roo.dd.DDM;
16791             if ( Roo.lib.Event && document ) {
16792                 DDM._onLoad();
16793             } else {
16794                 if (DDM._timeoutCount > 2000) {
16795                 } else {
16796                     setTimeout(DDM._addListeners, 10);
16797                     if (document && document.body) {
16798                         DDM._timeoutCount += 1;
16799                     }
16800                 }
16801             }
16802         },
16803
16804         /**
16805          * Recursively searches the immediate parent and all child nodes for
16806          * the handle element in order to determine wheter or not it was
16807          * clicked.
16808          * @method handleWasClicked
16809          * @param node the html element to inspect
16810          * @static
16811          */
16812         handleWasClicked: function(node, id) {
16813             if (this.isHandle(id, node.id)) {
16814                 return true;
16815             } else {
16816                 // check to see if this is a text node child of the one we want
16817                 var p = node.parentNode;
16818
16819                 while (p) {
16820                     if (this.isHandle(id, p.id)) {
16821                         return true;
16822                     } else {
16823                         p = p.parentNode;
16824                     }
16825                 }
16826             }
16827
16828             return false;
16829         }
16830
16831     };
16832
16833 }();
16834
16835 // shorter alias, save a few bytes
16836 Roo.dd.DDM = Roo.dd.DragDropMgr;
16837 Roo.dd.DDM._addListeners();
16838
16839 }/*
16840  * Based on:
16841  * Ext JS Library 1.1.1
16842  * Copyright(c) 2006-2007, Ext JS, LLC.
16843  *
16844  * Originally Released Under LGPL - original licence link has changed is not relivant.
16845  *
16846  * Fork - LGPL
16847  * <script type="text/javascript">
16848  */
16849
16850 /**
16851  * @class Roo.dd.DD
16852  * A DragDrop implementation where the linked element follows the
16853  * mouse cursor during a drag.
16854  * @extends Roo.dd.DragDrop
16855  * @constructor
16856  * @param {String} id the id of the linked element
16857  * @param {String} sGroup the group of related DragDrop items
16858  * @param {object} config an object containing configurable attributes
16859  *                Valid properties for DD:
16860  *                    scroll
16861  */
16862 Roo.dd.DD = function(id, sGroup, config) {
16863     if (id) {
16864         this.init(id, sGroup, config);
16865     }
16866 };
16867
16868 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16869
16870     /**
16871      * When set to true, the utility automatically tries to scroll the browser
16872      * window wehn a drag and drop element is dragged near the viewport boundary.
16873      * Defaults to true.
16874      * @property scroll
16875      * @type boolean
16876      */
16877     scroll: true,
16878
16879     /**
16880      * Sets the pointer offset to the distance between the linked element's top
16881      * left corner and the location the element was clicked
16882      * @method autoOffset
16883      * @param {int} iPageX the X coordinate of the click
16884      * @param {int} iPageY the Y coordinate of the click
16885      */
16886     autoOffset: function(iPageX, iPageY) {
16887         var x = iPageX - this.startPageX;
16888         var y = iPageY - this.startPageY;
16889         this.setDelta(x, y);
16890     },
16891
16892     /**
16893      * Sets the pointer offset.  You can call this directly to force the
16894      * offset to be in a particular location (e.g., pass in 0,0 to set it
16895      * to the center of the object)
16896      * @method setDelta
16897      * @param {int} iDeltaX the distance from the left
16898      * @param {int} iDeltaY the distance from the top
16899      */
16900     setDelta: function(iDeltaX, iDeltaY) {
16901         this.deltaX = iDeltaX;
16902         this.deltaY = iDeltaY;
16903     },
16904
16905     /**
16906      * Sets the drag element to the location of the mousedown or click event,
16907      * maintaining the cursor location relative to the location on the element
16908      * that was clicked.  Override this if you want to place the element in a
16909      * location other than where the cursor is.
16910      * @method setDragElPos
16911      * @param {int} iPageX the X coordinate of the mousedown or drag event
16912      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16913      */
16914     setDragElPos: function(iPageX, iPageY) {
16915         // the first time we do this, we are going to check to make sure
16916         // the element has css positioning
16917
16918         var el = this.getDragEl();
16919         this.alignElWithMouse(el, iPageX, iPageY);
16920     },
16921
16922     /**
16923      * Sets the element to the location of the mousedown or click event,
16924      * maintaining the cursor location relative to the location on the element
16925      * that was clicked.  Override this if you want to place the element in a
16926      * location other than where the cursor is.
16927      * @method alignElWithMouse
16928      * @param {HTMLElement} el the element to move
16929      * @param {int} iPageX the X coordinate of the mousedown or drag event
16930      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16931      */
16932     alignElWithMouse: function(el, iPageX, iPageY) {
16933         var oCoord = this.getTargetCoord(iPageX, iPageY);
16934         var fly = el.dom ? el : Roo.fly(el);
16935         if (!this.deltaSetXY) {
16936             var aCoord = [oCoord.x, oCoord.y];
16937             fly.setXY(aCoord);
16938             var newLeft = fly.getLeft(true);
16939             var newTop  = fly.getTop(true);
16940             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16941         } else {
16942             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16943         }
16944
16945         this.cachePosition(oCoord.x, oCoord.y);
16946         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16947         return oCoord;
16948     },
16949
16950     /**
16951      * Saves the most recent position so that we can reset the constraints and
16952      * tick marks on-demand.  We need to know this so that we can calculate the
16953      * number of pixels the element is offset from its original position.
16954      * @method cachePosition
16955      * @param iPageX the current x position (optional, this just makes it so we
16956      * don't have to look it up again)
16957      * @param iPageY the current y position (optional, this just makes it so we
16958      * don't have to look it up again)
16959      */
16960     cachePosition: function(iPageX, iPageY) {
16961         if (iPageX) {
16962             this.lastPageX = iPageX;
16963             this.lastPageY = iPageY;
16964         } else {
16965             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16966             this.lastPageX = aCoord[0];
16967             this.lastPageY = aCoord[1];
16968         }
16969     },
16970
16971     /**
16972      * Auto-scroll the window if the dragged object has been moved beyond the
16973      * visible window boundary.
16974      * @method autoScroll
16975      * @param {int} x the drag element's x position
16976      * @param {int} y the drag element's y position
16977      * @param {int} h the height of the drag element
16978      * @param {int} w the width of the drag element
16979      * @private
16980      */
16981     autoScroll: function(x, y, h, w) {
16982
16983         if (this.scroll) {
16984             // The client height
16985             var clientH = Roo.lib.Dom.getViewWidth();
16986
16987             // The client width
16988             var clientW = Roo.lib.Dom.getViewHeight();
16989
16990             // The amt scrolled down
16991             var st = this.DDM.getScrollTop();
16992
16993             // The amt scrolled right
16994             var sl = this.DDM.getScrollLeft();
16995
16996             // Location of the bottom of the element
16997             var bot = h + y;
16998
16999             // Location of the right of the element
17000             var right = w + x;
17001
17002             // The distance from the cursor to the bottom of the visible area,
17003             // adjusted so that we don't scroll if the cursor is beyond the
17004             // element drag constraints
17005             var toBot = (clientH + st - y - this.deltaY);
17006
17007             // The distance from the cursor to the right of the visible area
17008             var toRight = (clientW + sl - x - this.deltaX);
17009
17010
17011             // How close to the edge the cursor must be before we scroll
17012             // var thresh = (document.all) ? 100 : 40;
17013             var thresh = 40;
17014
17015             // How many pixels to scroll per autoscroll op.  This helps to reduce
17016             // clunky scrolling. IE is more sensitive about this ... it needs this
17017             // value to be higher.
17018             var scrAmt = (document.all) ? 80 : 30;
17019
17020             // Scroll down if we are near the bottom of the visible page and the
17021             // obj extends below the crease
17022             if ( bot > clientH && toBot < thresh ) {
17023                 window.scrollTo(sl, st + scrAmt);
17024             }
17025
17026             // Scroll up if the window is scrolled down and the top of the object
17027             // goes above the top border
17028             if ( y < st && st > 0 && y - st < thresh ) {
17029                 window.scrollTo(sl, st - scrAmt);
17030             }
17031
17032             // Scroll right if the obj is beyond the right border and the cursor is
17033             // near the border.
17034             if ( right > clientW && toRight < thresh ) {
17035                 window.scrollTo(sl + scrAmt, st);
17036             }
17037
17038             // Scroll left if the window has been scrolled to the right and the obj
17039             // extends past the left border
17040             if ( x < sl && sl > 0 && x - sl < thresh ) {
17041                 window.scrollTo(sl - scrAmt, st);
17042             }
17043         }
17044     },
17045
17046     /**
17047      * Finds the location the element should be placed if we want to move
17048      * it to where the mouse location less the click offset would place us.
17049      * @method getTargetCoord
17050      * @param {int} iPageX the X coordinate of the click
17051      * @param {int} iPageY the Y coordinate of the click
17052      * @return an object that contains the coordinates (Object.x and Object.y)
17053      * @private
17054      */
17055     getTargetCoord: function(iPageX, iPageY) {
17056
17057
17058         var x = iPageX - this.deltaX;
17059         var y = iPageY - this.deltaY;
17060
17061         if (this.constrainX) {
17062             if (x < this.minX) { x = this.minX; }
17063             if (x > this.maxX) { x = this.maxX; }
17064         }
17065
17066         if (this.constrainY) {
17067             if (y < this.minY) { y = this.minY; }
17068             if (y > this.maxY) { y = this.maxY; }
17069         }
17070
17071         x = this.getTick(x, this.xTicks);
17072         y = this.getTick(y, this.yTicks);
17073
17074
17075         return {x:x, y:y};
17076     },
17077
17078     /*
17079      * Sets up config options specific to this class. Overrides
17080      * Roo.dd.DragDrop, but all versions of this method through the
17081      * inheritance chain are called
17082      */
17083     applyConfig: function() {
17084         Roo.dd.DD.superclass.applyConfig.call(this);
17085         this.scroll = (this.config.scroll !== false);
17086     },
17087
17088     /*
17089      * Event that fires prior to the onMouseDown event.  Overrides
17090      * Roo.dd.DragDrop.
17091      */
17092     b4MouseDown: function(e) {
17093         // this.resetConstraints();
17094         this.autoOffset(e.getPageX(),
17095                             e.getPageY());
17096     },
17097
17098     /*
17099      * Event that fires prior to the onDrag event.  Overrides
17100      * Roo.dd.DragDrop.
17101      */
17102     b4Drag: function(e) {
17103         this.setDragElPos(e.getPageX(),
17104                             e.getPageY());
17105     },
17106
17107     toString: function() {
17108         return ("DD " + this.id);
17109     }
17110
17111     //////////////////////////////////////////////////////////////////////////
17112     // Debugging ygDragDrop events that can be overridden
17113     //////////////////////////////////////////////////////////////////////////
17114     /*
17115     startDrag: function(x, y) {
17116     },
17117
17118     onDrag: function(e) {
17119     },
17120
17121     onDragEnter: function(e, id) {
17122     },
17123
17124     onDragOver: function(e, id) {
17125     },
17126
17127     onDragOut: function(e, id) {
17128     },
17129
17130     onDragDrop: function(e, id) {
17131     },
17132
17133     endDrag: function(e) {
17134     }
17135
17136     */
17137
17138 });/*
17139  * Based on:
17140  * Ext JS Library 1.1.1
17141  * Copyright(c) 2006-2007, Ext JS, LLC.
17142  *
17143  * Originally Released Under LGPL - original licence link has changed is not relivant.
17144  *
17145  * Fork - LGPL
17146  * <script type="text/javascript">
17147  */
17148
17149 /**
17150  * @class Roo.dd.DDProxy
17151  * A DragDrop implementation that inserts an empty, bordered div into
17152  * the document that follows the cursor during drag operations.  At the time of
17153  * the click, the frame div is resized to the dimensions of the linked html
17154  * element, and moved to the exact location of the linked element.
17155  *
17156  * References to the "frame" element refer to the single proxy element that
17157  * was created to be dragged in place of all DDProxy elements on the
17158  * page.
17159  *
17160  * @extends Roo.dd.DD
17161  * @constructor
17162  * @param {String} id the id of the linked html element
17163  * @param {String} sGroup the group of related DragDrop objects
17164  * @param {object} config an object containing configurable attributes
17165  *                Valid properties for DDProxy in addition to those in DragDrop:
17166  *                   resizeFrame, centerFrame, dragElId
17167  */
17168 Roo.dd.DDProxy = function(id, sGroup, config) {
17169     if (id) {
17170         this.init(id, sGroup, config);
17171         this.initFrame();
17172     }
17173 };
17174
17175 /**
17176  * The default drag frame div id
17177  * @property Roo.dd.DDProxy.dragElId
17178  * @type String
17179  * @static
17180  */
17181 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17182
17183 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17184
17185     /**
17186      * By default we resize the drag frame to be the same size as the element
17187      * we want to drag (this is to get the frame effect).  We can turn it off
17188      * if we want a different behavior.
17189      * @property resizeFrame
17190      * @type boolean
17191      */
17192     resizeFrame: true,
17193
17194     /**
17195      * By default the frame is positioned exactly where the drag element is, so
17196      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17197      * you do not have constraints on the obj is to have the drag frame centered
17198      * around the cursor.  Set centerFrame to true for this effect.
17199      * @property centerFrame
17200      * @type boolean
17201      */
17202     centerFrame: false,
17203
17204     /**
17205      * Creates the proxy element if it does not yet exist
17206      * @method createFrame
17207      */
17208     createFrame: function() {
17209         var self = this;
17210         var body = document.body;
17211
17212         if (!body || !body.firstChild) {
17213             setTimeout( function() { self.createFrame(); }, 50 );
17214             return;
17215         }
17216
17217         var div = this.getDragEl();
17218
17219         if (!div) {
17220             div    = document.createElement("div");
17221             div.id = this.dragElId;
17222             var s  = div.style;
17223
17224             s.position   = "absolute";
17225             s.visibility = "hidden";
17226             s.cursor     = "move";
17227             s.border     = "2px solid #aaa";
17228             s.zIndex     = 999;
17229
17230             // appendChild can blow up IE if invoked prior to the window load event
17231             // while rendering a table.  It is possible there are other scenarios
17232             // that would cause this to happen as well.
17233             body.insertBefore(div, body.firstChild);
17234         }
17235     },
17236
17237     /**
17238      * Initialization for the drag frame element.  Must be called in the
17239      * constructor of all subclasses
17240      * @method initFrame
17241      */
17242     initFrame: function() {
17243         this.createFrame();
17244     },
17245
17246     applyConfig: function() {
17247         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17248
17249         this.resizeFrame = (this.config.resizeFrame !== false);
17250         this.centerFrame = (this.config.centerFrame);
17251         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17252     },
17253
17254     /**
17255      * Resizes the drag frame to the dimensions of the clicked object, positions
17256      * it over the object, and finally displays it
17257      * @method showFrame
17258      * @param {int} iPageX X click position
17259      * @param {int} iPageY Y click position
17260      * @private
17261      */
17262     showFrame: function(iPageX, iPageY) {
17263         var el = this.getEl();
17264         var dragEl = this.getDragEl();
17265         var s = dragEl.style;
17266
17267         this._resizeProxy();
17268
17269         if (this.centerFrame) {
17270             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17271                            Math.round(parseInt(s.height, 10)/2) );
17272         }
17273
17274         this.setDragElPos(iPageX, iPageY);
17275
17276         Roo.fly(dragEl).show();
17277     },
17278
17279     /**
17280      * The proxy is automatically resized to the dimensions of the linked
17281      * element when a drag is initiated, unless resizeFrame is set to false
17282      * @method _resizeProxy
17283      * @private
17284      */
17285     _resizeProxy: function() {
17286         if (this.resizeFrame) {
17287             var el = this.getEl();
17288             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17289         }
17290     },
17291
17292     // overrides Roo.dd.DragDrop
17293     b4MouseDown: function(e) {
17294         var x = e.getPageX();
17295         var y = e.getPageY();
17296         this.autoOffset(x, y);
17297         this.setDragElPos(x, y);
17298     },
17299
17300     // overrides Roo.dd.DragDrop
17301     b4StartDrag: function(x, y) {
17302         // show the drag frame
17303         this.showFrame(x, y);
17304     },
17305
17306     // overrides Roo.dd.DragDrop
17307     b4EndDrag: function(e) {
17308         Roo.fly(this.getDragEl()).hide();
17309     },
17310
17311     // overrides Roo.dd.DragDrop
17312     // By default we try to move the element to the last location of the frame.
17313     // This is so that the default behavior mirrors that of Roo.dd.DD.
17314     endDrag: function(e) {
17315
17316         var lel = this.getEl();
17317         var del = this.getDragEl();
17318
17319         // Show the drag frame briefly so we can get its position
17320         del.style.visibility = "";
17321
17322         this.beforeMove();
17323         // Hide the linked element before the move to get around a Safari
17324         // rendering bug.
17325         lel.style.visibility = "hidden";
17326         Roo.dd.DDM.moveToEl(lel, del);
17327         del.style.visibility = "hidden";
17328         lel.style.visibility = "";
17329
17330         this.afterDrag();
17331     },
17332
17333     beforeMove : function(){
17334
17335     },
17336
17337     afterDrag : function(){
17338
17339     },
17340
17341     toString: function() {
17342         return ("DDProxy " + this.id);
17343     }
17344
17345 });
17346 /*
17347  * Based on:
17348  * Ext JS Library 1.1.1
17349  * Copyright(c) 2006-2007, Ext JS, LLC.
17350  *
17351  * Originally Released Under LGPL - original licence link has changed is not relivant.
17352  *
17353  * Fork - LGPL
17354  * <script type="text/javascript">
17355  */
17356
17357  /**
17358  * @class Roo.dd.DDTarget
17359  * A DragDrop implementation that does not move, but can be a drop
17360  * target.  You would get the same result by simply omitting implementation
17361  * for the event callbacks, but this way we reduce the processing cost of the
17362  * event listener and the callbacks.
17363  * @extends Roo.dd.DragDrop
17364  * @constructor
17365  * @param {String} id the id of the element that is a drop target
17366  * @param {String} sGroup the group of related DragDrop objects
17367  * @param {object} config an object containing configurable attributes
17368  *                 Valid properties for DDTarget in addition to those in
17369  *                 DragDrop:
17370  *                    none
17371  */
17372 Roo.dd.DDTarget = function(id, sGroup, config) {
17373     if (id) {
17374         this.initTarget(id, sGroup, config);
17375     }
17376 };
17377
17378 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17379 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17380     toString: function() {
17381         return ("DDTarget " + this.id);
17382     }
17383 });
17384 /*
17385  * Based on:
17386  * Ext JS Library 1.1.1
17387  * Copyright(c) 2006-2007, Ext JS, LLC.
17388  *
17389  * Originally Released Under LGPL - original licence link has changed is not relivant.
17390  *
17391  * Fork - LGPL
17392  * <script type="text/javascript">
17393  */
17394  
17395
17396 /**
17397  * @class Roo.dd.ScrollManager
17398  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17399  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17400  * @singleton
17401  */
17402 Roo.dd.ScrollManager = function(){
17403     var ddm = Roo.dd.DragDropMgr;
17404     var els = {};
17405     var dragEl = null;
17406     var proc = {};
17407     
17408     var onStop = function(e){
17409         dragEl = null;
17410         clearProc();
17411     };
17412     
17413     var triggerRefresh = function(){
17414         if(ddm.dragCurrent){
17415              ddm.refreshCache(ddm.dragCurrent.groups);
17416         }
17417     };
17418     
17419     var doScroll = function(){
17420         if(ddm.dragCurrent){
17421             var dds = Roo.dd.ScrollManager;
17422             if(!dds.animate){
17423                 if(proc.el.scroll(proc.dir, dds.increment)){
17424                     triggerRefresh();
17425                 }
17426             }else{
17427                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17428             }
17429         }
17430     };
17431     
17432     var clearProc = function(){
17433         if(proc.id){
17434             clearInterval(proc.id);
17435         }
17436         proc.id = 0;
17437         proc.el = null;
17438         proc.dir = "";
17439     };
17440     
17441     var startProc = function(el, dir){
17442         clearProc();
17443         proc.el = el;
17444         proc.dir = dir;
17445         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17446     };
17447     
17448     var onFire = function(e, isDrop){
17449         if(isDrop || !ddm.dragCurrent){ return; }
17450         var dds = Roo.dd.ScrollManager;
17451         if(!dragEl || dragEl != ddm.dragCurrent){
17452             dragEl = ddm.dragCurrent;
17453             // refresh regions on drag start
17454             dds.refreshCache();
17455         }
17456         
17457         var xy = Roo.lib.Event.getXY(e);
17458         var pt = new Roo.lib.Point(xy[0], xy[1]);
17459         for(var id in els){
17460             var el = els[id], r = el._region;
17461             if(r && r.contains(pt) && el.isScrollable()){
17462                 if(r.bottom - pt.y <= dds.thresh){
17463                     if(proc.el != el){
17464                         startProc(el, "down");
17465                     }
17466                     return;
17467                 }else if(r.right - pt.x <= dds.thresh){
17468                     if(proc.el != el){
17469                         startProc(el, "left");
17470                     }
17471                     return;
17472                 }else if(pt.y - r.top <= dds.thresh){
17473                     if(proc.el != el){
17474                         startProc(el, "up");
17475                     }
17476                     return;
17477                 }else if(pt.x - r.left <= dds.thresh){
17478                     if(proc.el != el){
17479                         startProc(el, "right");
17480                     }
17481                     return;
17482                 }
17483             }
17484         }
17485         clearProc();
17486     };
17487     
17488     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17489     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17490     
17491     return {
17492         /**
17493          * Registers new overflow element(s) to auto scroll
17494          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17495          */
17496         register : function(el){
17497             if(el instanceof Array){
17498                 for(var i = 0, len = el.length; i < len; i++) {
17499                         this.register(el[i]);
17500                 }
17501             }else{
17502                 el = Roo.get(el);
17503                 els[el.id] = el;
17504             }
17505         },
17506         
17507         /**
17508          * Unregisters overflow element(s) so they are no longer scrolled
17509          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17510          */
17511         unregister : function(el){
17512             if(el instanceof Array){
17513                 for(var i = 0, len = el.length; i < len; i++) {
17514                         this.unregister(el[i]);
17515                 }
17516             }else{
17517                 el = Roo.get(el);
17518                 delete els[el.id];
17519             }
17520         },
17521         
17522         /**
17523          * The number of pixels from the edge of a container the pointer needs to be to 
17524          * trigger scrolling (defaults to 25)
17525          * @type Number
17526          */
17527         thresh : 25,
17528         
17529         /**
17530          * The number of pixels to scroll in each scroll increment (defaults to 50)
17531          * @type Number
17532          */
17533         increment : 100,
17534         
17535         /**
17536          * The frequency of scrolls in milliseconds (defaults to 500)
17537          * @type Number
17538          */
17539         frequency : 500,
17540         
17541         /**
17542          * True to animate the scroll (defaults to true)
17543          * @type Boolean
17544          */
17545         animate: true,
17546         
17547         /**
17548          * The animation duration in seconds - 
17549          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17550          * @type Number
17551          */
17552         animDuration: .4,
17553         
17554         /**
17555          * Manually trigger a cache refresh.
17556          */
17557         refreshCache : function(){
17558             for(var id in els){
17559                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17560                     els[id]._region = els[id].getRegion();
17561                 }
17562             }
17563         }
17564     };
17565 }();/*
17566  * Based on:
17567  * Ext JS Library 1.1.1
17568  * Copyright(c) 2006-2007, Ext JS, LLC.
17569  *
17570  * Originally Released Under LGPL - original licence link has changed is not relivant.
17571  *
17572  * Fork - LGPL
17573  * <script type="text/javascript">
17574  */
17575  
17576
17577 /**
17578  * @class Roo.dd.Registry
17579  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17580  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17581  * @singleton
17582  */
17583 Roo.dd.Registry = function(){
17584     var elements = {}; 
17585     var handles = {}; 
17586     var autoIdSeed = 0;
17587
17588     var getId = function(el, autogen){
17589         if(typeof el == "string"){
17590             return el;
17591         }
17592         var id = el.id;
17593         if(!id && autogen !== false){
17594             id = "roodd-" + (++autoIdSeed);
17595             el.id = id;
17596         }
17597         return id;
17598     };
17599     
17600     return {
17601     /**
17602      * Register a drag drop element
17603      * @param {String|HTMLElement} element The id or DOM node to register
17604      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17605      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17606      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17607      * populated in the data object (if applicable):
17608      * <pre>
17609 Value      Description<br />
17610 ---------  ------------------------------------------<br />
17611 handles    Array of DOM nodes that trigger dragging<br />
17612            for the element being registered<br />
17613 isHandle   True if the element passed in triggers<br />
17614            dragging itself, else false
17615 </pre>
17616      */
17617         register : function(el, data){
17618             data = data || {};
17619             if(typeof el == "string"){
17620                 el = document.getElementById(el);
17621             }
17622             data.ddel = el;
17623             elements[getId(el)] = data;
17624             if(data.isHandle !== false){
17625                 handles[data.ddel.id] = data;
17626             }
17627             if(data.handles){
17628                 var hs = data.handles;
17629                 for(var i = 0, len = hs.length; i < len; i++){
17630                         handles[getId(hs[i])] = data;
17631                 }
17632             }
17633         },
17634
17635     /**
17636      * Unregister a drag drop element
17637      * @param {String|HTMLElement}  element The id or DOM node to unregister
17638      */
17639         unregister : function(el){
17640             var id = getId(el, false);
17641             var data = elements[id];
17642             if(data){
17643                 delete elements[id];
17644                 if(data.handles){
17645                     var hs = data.handles;
17646                     for(var i = 0, len = hs.length; i < len; i++){
17647                         delete handles[getId(hs[i], false)];
17648                     }
17649                 }
17650             }
17651         },
17652
17653     /**
17654      * Returns the handle registered for a DOM Node by id
17655      * @param {String|HTMLElement} id The DOM node or id to look up
17656      * @return {Object} handle The custom handle data
17657      */
17658         getHandle : function(id){
17659             if(typeof id != "string"){ // must be element?
17660                 id = id.id;
17661             }
17662             return handles[id];
17663         },
17664
17665     /**
17666      * Returns the handle that is registered for the DOM node that is the target of the event
17667      * @param {Event} e The event
17668      * @return {Object} handle The custom handle data
17669      */
17670         getHandleFromEvent : function(e){
17671             var t = Roo.lib.Event.getTarget(e);
17672             return t ? handles[t.id] : null;
17673         },
17674
17675     /**
17676      * Returns a custom data object that is registered for a DOM node by id
17677      * @param {String|HTMLElement} id The DOM node or id to look up
17678      * @return {Object} data The custom data
17679      */
17680         getTarget : function(id){
17681             if(typeof id != "string"){ // must be element?
17682                 id = id.id;
17683             }
17684             return elements[id];
17685         },
17686
17687     /**
17688      * Returns a custom data object that is registered for the DOM node that is the target of the event
17689      * @param {Event} e The event
17690      * @return {Object} data The custom data
17691      */
17692         getTargetFromEvent : function(e){
17693             var t = Roo.lib.Event.getTarget(e);
17694             return t ? elements[t.id] || handles[t.id] : null;
17695         }
17696     };
17697 }();/*
17698  * Based on:
17699  * Ext JS Library 1.1.1
17700  * Copyright(c) 2006-2007, Ext JS, LLC.
17701  *
17702  * Originally Released Under LGPL - original licence link has changed is not relivant.
17703  *
17704  * Fork - LGPL
17705  * <script type="text/javascript">
17706  */
17707  
17708
17709 /**
17710  * @class Roo.dd.StatusProxy
17711  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17712  * default drag proxy used by all Roo.dd components.
17713  * @constructor
17714  * @param {Object} config
17715  */
17716 Roo.dd.StatusProxy = function(config){
17717     Roo.apply(this, config);
17718     this.id = this.id || Roo.id();
17719     this.el = new Roo.Layer({
17720         dh: {
17721             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17722                 {tag: "div", cls: "x-dd-drop-icon"},
17723                 {tag: "div", cls: "x-dd-drag-ghost"}
17724             ]
17725         }, 
17726         shadow: !config || config.shadow !== false
17727     });
17728     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17729     this.dropStatus = this.dropNotAllowed;
17730 };
17731
17732 Roo.dd.StatusProxy.prototype = {
17733     /**
17734      * @cfg {String} dropAllowed
17735      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17736      */
17737     dropAllowed : "x-dd-drop-ok",
17738     /**
17739      * @cfg {String} dropNotAllowed
17740      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17741      */
17742     dropNotAllowed : "x-dd-drop-nodrop",
17743
17744     /**
17745      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17746      * over the current target element.
17747      * @param {String} cssClass The css class for the new drop status indicator image
17748      */
17749     setStatus : function(cssClass){
17750         cssClass = cssClass || this.dropNotAllowed;
17751         if(this.dropStatus != cssClass){
17752             this.el.replaceClass(this.dropStatus, cssClass);
17753             this.dropStatus = cssClass;
17754         }
17755     },
17756
17757     /**
17758      * Resets the status indicator to the default dropNotAllowed value
17759      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17760      */
17761     reset : function(clearGhost){
17762         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17763         this.dropStatus = this.dropNotAllowed;
17764         if(clearGhost){
17765             this.ghost.update("");
17766         }
17767     },
17768
17769     /**
17770      * Updates the contents of the ghost element
17771      * @param {String} html The html that will replace the current innerHTML of the ghost element
17772      */
17773     update : function(html){
17774         if(typeof html == "string"){
17775             this.ghost.update(html);
17776         }else{
17777             this.ghost.update("");
17778             html.style.margin = "0";
17779             this.ghost.dom.appendChild(html);
17780         }
17781         // ensure float = none set?? cant remember why though.
17782         var el = this.ghost.dom.firstChild;
17783                 if(el){
17784                         Roo.fly(el).setStyle('float', 'none');
17785                 }
17786     },
17787     
17788     /**
17789      * Returns the underlying proxy {@link Roo.Layer}
17790      * @return {Roo.Layer} el
17791     */
17792     getEl : function(){
17793         return this.el;
17794     },
17795
17796     /**
17797      * Returns the ghost element
17798      * @return {Roo.Element} el
17799      */
17800     getGhost : function(){
17801         return this.ghost;
17802     },
17803
17804     /**
17805      * Hides the proxy
17806      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17807      */
17808     hide : function(clear){
17809         this.el.hide();
17810         if(clear){
17811             this.reset(true);
17812         }
17813     },
17814
17815     /**
17816      * Stops the repair animation if it's currently running
17817      */
17818     stop : function(){
17819         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17820             this.anim.stop();
17821         }
17822     },
17823
17824     /**
17825      * Displays this proxy
17826      */
17827     show : function(){
17828         this.el.show();
17829     },
17830
17831     /**
17832      * Force the Layer to sync its shadow and shim positions to the element
17833      */
17834     sync : function(){
17835         this.el.sync();
17836     },
17837
17838     /**
17839      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17840      * invalid drop operation by the item being dragged.
17841      * @param {Array} xy The XY position of the element ([x, y])
17842      * @param {Function} callback The function to call after the repair is complete
17843      * @param {Object} scope The scope in which to execute the callback
17844      */
17845     repair : function(xy, callback, scope){
17846         this.callback = callback;
17847         this.scope = scope;
17848         if(xy && this.animRepair !== false){
17849             this.el.addClass("x-dd-drag-repair");
17850             this.el.hideUnders(true);
17851             this.anim = this.el.shift({
17852                 duration: this.repairDuration || .5,
17853                 easing: 'easeOut',
17854                 xy: xy,
17855                 stopFx: true,
17856                 callback: this.afterRepair,
17857                 scope: this
17858             });
17859         }else{
17860             this.afterRepair();
17861         }
17862     },
17863
17864     // private
17865     afterRepair : function(){
17866         this.hide(true);
17867         if(typeof this.callback == "function"){
17868             this.callback.call(this.scope || this);
17869         }
17870         this.callback = null;
17871         this.scope = null;
17872     }
17873 };/*
17874  * Based on:
17875  * Ext JS Library 1.1.1
17876  * Copyright(c) 2006-2007, Ext JS, LLC.
17877  *
17878  * Originally Released Under LGPL - original licence link has changed is not relivant.
17879  *
17880  * Fork - LGPL
17881  * <script type="text/javascript">
17882  */
17883
17884 /**
17885  * @class Roo.dd.DragSource
17886  * @extends Roo.dd.DDProxy
17887  * A simple class that provides the basic implementation needed to make any element draggable.
17888  * @constructor
17889  * @param {String/HTMLElement/Element} el The container element
17890  * @param {Object} config
17891  */
17892 Roo.dd.DragSource = function(el, config){
17893     this.el = Roo.get(el);
17894     this.dragData = {};
17895     
17896     Roo.apply(this, config);
17897     
17898     if(!this.proxy){
17899         this.proxy = new Roo.dd.StatusProxy();
17900     }
17901
17902     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17903           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17904     
17905     this.dragging = false;
17906 };
17907
17908 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17909     /**
17910      * @cfg {String} dropAllowed
17911      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17912      */
17913     dropAllowed : "x-dd-drop-ok",
17914     /**
17915      * @cfg {String} dropNotAllowed
17916      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17917      */
17918     dropNotAllowed : "x-dd-drop-nodrop",
17919
17920     /**
17921      * Returns the data object associated with this drag source
17922      * @return {Object} data An object containing arbitrary data
17923      */
17924     getDragData : function(e){
17925         return this.dragData;
17926     },
17927
17928     // private
17929     onDragEnter : function(e, id){
17930         var target = Roo.dd.DragDropMgr.getDDById(id);
17931         this.cachedTarget = target;
17932         if(this.beforeDragEnter(target, e, id) !== false){
17933             if(target.isNotifyTarget){
17934                 var status = target.notifyEnter(this, e, this.dragData);
17935                 this.proxy.setStatus(status);
17936             }else{
17937                 this.proxy.setStatus(this.dropAllowed);
17938             }
17939             
17940             if(this.afterDragEnter){
17941                 /**
17942                  * An empty function by default, but provided so that you can perform a custom action
17943                  * when the dragged item enters the drop target by providing an implementation.
17944                  * @param {Roo.dd.DragDrop} target The drop target
17945                  * @param {Event} e The event object
17946                  * @param {String} id The id of the dragged element
17947                  * @method afterDragEnter
17948                  */
17949                 this.afterDragEnter(target, e, id);
17950             }
17951         }
17952     },
17953
17954     /**
17955      * An empty function by default, but provided so that you can perform a custom action
17956      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17957      * @param {Roo.dd.DragDrop} target The drop target
17958      * @param {Event} e The event object
17959      * @param {String} id The id of the dragged element
17960      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17961      */
17962     beforeDragEnter : function(target, e, id){
17963         return true;
17964     },
17965
17966     // private
17967     alignElWithMouse: function() {
17968         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17969         this.proxy.sync();
17970     },
17971
17972     // private
17973     onDragOver : function(e, id){
17974         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17975         if(this.beforeDragOver(target, e, id) !== false){
17976             if(target.isNotifyTarget){
17977                 var status = target.notifyOver(this, e, this.dragData);
17978                 this.proxy.setStatus(status);
17979             }
17980
17981             if(this.afterDragOver){
17982                 /**
17983                  * An empty function by default, but provided so that you can perform a custom action
17984                  * while the dragged item is over the drop target by providing an implementation.
17985                  * @param {Roo.dd.DragDrop} target The drop target
17986                  * @param {Event} e The event object
17987                  * @param {String} id The id of the dragged element
17988                  * @method afterDragOver
17989                  */
17990                 this.afterDragOver(target, e, id);
17991             }
17992         }
17993     },
17994
17995     /**
17996      * An empty function by default, but provided so that you can perform a custom action
17997      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17998      * @param {Roo.dd.DragDrop} target The drop target
17999      * @param {Event} e The event object
18000      * @param {String} id The id of the dragged element
18001      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18002      */
18003     beforeDragOver : function(target, e, id){
18004         return true;
18005     },
18006
18007     // private
18008     onDragOut : function(e, id){
18009         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18010         if(this.beforeDragOut(target, e, id) !== false){
18011             if(target.isNotifyTarget){
18012                 target.notifyOut(this, e, this.dragData);
18013             }
18014             this.proxy.reset();
18015             if(this.afterDragOut){
18016                 /**
18017                  * An empty function by default, but provided so that you can perform a custom action
18018                  * after the dragged item is dragged out of the target without dropping.
18019                  * @param {Roo.dd.DragDrop} target The drop target
18020                  * @param {Event} e The event object
18021                  * @param {String} id The id of the dragged element
18022                  * @method afterDragOut
18023                  */
18024                 this.afterDragOut(target, e, id);
18025             }
18026         }
18027         this.cachedTarget = null;
18028     },
18029
18030     /**
18031      * An empty function by default, but provided so that you can perform a custom action before the dragged
18032      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18033      * @param {Roo.dd.DragDrop} target The drop target
18034      * @param {Event} e The event object
18035      * @param {String} id The id of the dragged element
18036      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18037      */
18038     beforeDragOut : function(target, e, id){
18039         return true;
18040     },
18041     
18042     // private
18043     onDragDrop : function(e, id){
18044         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18045         if(this.beforeDragDrop(target, e, id) !== false){
18046             if(target.isNotifyTarget){
18047                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18048                     this.onValidDrop(target, e, id);
18049                 }else{
18050                     this.onInvalidDrop(target, e, id);
18051                 }
18052             }else{
18053                 this.onValidDrop(target, e, id);
18054             }
18055             
18056             if(this.afterDragDrop){
18057                 /**
18058                  * An empty function by default, but provided so that you can perform a custom action
18059                  * after a valid drag drop has occurred by providing an implementation.
18060                  * @param {Roo.dd.DragDrop} target The drop target
18061                  * @param {Event} e The event object
18062                  * @param {String} id The id of the dropped element
18063                  * @method afterDragDrop
18064                  */
18065                 this.afterDragDrop(target, e, id);
18066             }
18067         }
18068         delete this.cachedTarget;
18069     },
18070
18071     /**
18072      * An empty function by default, but provided so that you can perform a custom action before the dragged
18073      * item is dropped onto the target and optionally cancel the onDragDrop.
18074      * @param {Roo.dd.DragDrop} target The drop target
18075      * @param {Event} e The event object
18076      * @param {String} id The id of the dragged element
18077      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18078      */
18079     beforeDragDrop : function(target, e, id){
18080         return true;
18081     },
18082
18083     // private
18084     onValidDrop : function(target, e, id){
18085         this.hideProxy();
18086         if(this.afterValidDrop){
18087             /**
18088              * An empty function by default, but provided so that you can perform a custom action
18089              * after a valid drop has occurred by providing an implementation.
18090              * @param {Object} target The target DD 
18091              * @param {Event} e The event object
18092              * @param {String} id The id of the dropped element
18093              * @method afterInvalidDrop
18094              */
18095             this.afterValidDrop(target, e, id);
18096         }
18097     },
18098
18099     // private
18100     getRepairXY : function(e, data){
18101         return this.el.getXY();  
18102     },
18103
18104     // private
18105     onInvalidDrop : function(target, e, id){
18106         this.beforeInvalidDrop(target, e, id);
18107         if(this.cachedTarget){
18108             if(this.cachedTarget.isNotifyTarget){
18109                 this.cachedTarget.notifyOut(this, e, this.dragData);
18110             }
18111             this.cacheTarget = null;
18112         }
18113         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18114
18115         if(this.afterInvalidDrop){
18116             /**
18117              * An empty function by default, but provided so that you can perform a custom action
18118              * after an invalid drop has occurred by providing an implementation.
18119              * @param {Event} e The event object
18120              * @param {String} id The id of the dropped element
18121              * @method afterInvalidDrop
18122              */
18123             this.afterInvalidDrop(e, id);
18124         }
18125     },
18126
18127     // private
18128     afterRepair : function(){
18129         if(Roo.enableFx){
18130             this.el.highlight(this.hlColor || "c3daf9");
18131         }
18132         this.dragging = false;
18133     },
18134
18135     /**
18136      * An empty function by default, but provided so that you can perform a custom action after an invalid
18137      * drop has occurred.
18138      * @param {Roo.dd.DragDrop} target The drop target
18139      * @param {Event} e The event object
18140      * @param {String} id The id of the dragged element
18141      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18142      */
18143     beforeInvalidDrop : function(target, e, id){
18144         return true;
18145     },
18146
18147     // private
18148     handleMouseDown : function(e){
18149         if(this.dragging) {
18150             return;
18151         }
18152         var data = this.getDragData(e);
18153         if(data && this.onBeforeDrag(data, e) !== false){
18154             this.dragData = data;
18155             this.proxy.stop();
18156             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18157         } 
18158     },
18159
18160     /**
18161      * An empty function by default, but provided so that you can perform a custom action before the initial
18162      * drag event begins and optionally cancel it.
18163      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18164      * @param {Event} e The event object
18165      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18166      */
18167     onBeforeDrag : function(data, e){
18168         return true;
18169     },
18170
18171     /**
18172      * An empty function by default, but provided so that you can perform a custom action once the initial
18173      * drag event has begun.  The drag cannot be canceled from this function.
18174      * @param {Number} x The x position of the click on the dragged object
18175      * @param {Number} y The y position of the click on the dragged object
18176      */
18177     onStartDrag : Roo.emptyFn,
18178
18179     // private - YUI override
18180     startDrag : function(x, y){
18181         this.proxy.reset();
18182         this.dragging = true;
18183         this.proxy.update("");
18184         this.onInitDrag(x, y);
18185         this.proxy.show();
18186     },
18187
18188     // private
18189     onInitDrag : function(x, y){
18190         var clone = this.el.dom.cloneNode(true);
18191         clone.id = Roo.id(); // prevent duplicate ids
18192         this.proxy.update(clone);
18193         this.onStartDrag(x, y);
18194         return true;
18195     },
18196
18197     /**
18198      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18199      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18200      */
18201     getProxy : function(){
18202         return this.proxy;  
18203     },
18204
18205     /**
18206      * Hides the drag source's {@link Roo.dd.StatusProxy}
18207      */
18208     hideProxy : function(){
18209         this.proxy.hide();  
18210         this.proxy.reset(true);
18211         this.dragging = false;
18212     },
18213
18214     // private
18215     triggerCacheRefresh : function(){
18216         Roo.dd.DDM.refreshCache(this.groups);
18217     },
18218
18219     // private - override to prevent hiding
18220     b4EndDrag: function(e) {
18221     },
18222
18223     // private - override to prevent moving
18224     endDrag : function(e){
18225         this.onEndDrag(this.dragData, e);
18226     },
18227
18228     // private
18229     onEndDrag : function(data, e){
18230     },
18231     
18232     // private - pin to cursor
18233     autoOffset : function(x, y) {
18234         this.setDelta(-12, -20);
18235     }    
18236 });/*
18237  * Based on:
18238  * Ext JS Library 1.1.1
18239  * Copyright(c) 2006-2007, Ext JS, LLC.
18240  *
18241  * Originally Released Under LGPL - original licence link has changed is not relivant.
18242  *
18243  * Fork - LGPL
18244  * <script type="text/javascript">
18245  */
18246
18247
18248 /**
18249  * @class Roo.dd.DropTarget
18250  * @extends Roo.dd.DDTarget
18251  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18252  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18253  * @constructor
18254  * @param {String/HTMLElement/Element} el The container element
18255  * @param {Object} config
18256  */
18257 Roo.dd.DropTarget = function(el, config){
18258     this.el = Roo.get(el);
18259     
18260     Roo.apply(this, config);
18261     
18262     if(this.containerScroll){
18263         Roo.dd.ScrollManager.register(this.el);
18264     }
18265     
18266     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18267           {isTarget: true});
18268
18269 };
18270
18271 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18272     /**
18273      * @cfg {String} overClass
18274      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18275      */
18276     /**
18277      * @cfg {String} dropAllowed
18278      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18279      */
18280     dropAllowed : "x-dd-drop-ok",
18281     /**
18282      * @cfg {String} dropNotAllowed
18283      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18284      */
18285     dropNotAllowed : "x-dd-drop-nodrop",
18286
18287     // private
18288     isTarget : true,
18289
18290     // private
18291     isNotifyTarget : true,
18292
18293     /**
18294      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18295      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18296      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18297      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18298      * @param {Event} e The event
18299      * @param {Object} data An object containing arbitrary data supplied by the drag source
18300      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18301      * underlying {@link Roo.dd.StatusProxy} can be updated
18302      */
18303     notifyEnter : function(dd, e, data){
18304         if(this.overClass){
18305             this.el.addClass(this.overClass);
18306         }
18307         return this.dropAllowed;
18308     },
18309
18310     /**
18311      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18312      * This method will be called on every mouse movement while the drag source is over the drop target.
18313      * This default implementation simply returns the dropAllowed config value.
18314      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18315      * @param {Event} e The event
18316      * @param {Object} data An object containing arbitrary data supplied by the drag source
18317      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18318      * underlying {@link Roo.dd.StatusProxy} can be updated
18319      */
18320     notifyOver : function(dd, e, data){
18321         return this.dropAllowed;
18322     },
18323
18324     /**
18325      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18326      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18327      * overClass (if any) from the drop element.
18328      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18329      * @param {Event} e The event
18330      * @param {Object} data An object containing arbitrary data supplied by the drag source
18331      */
18332     notifyOut : function(dd, e, data){
18333         if(this.overClass){
18334             this.el.removeClass(this.overClass);
18335         }
18336     },
18337
18338     /**
18339      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18340      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18341      * implementation that does something to process the drop event and returns true so that the drag source's
18342      * repair action does not run.
18343      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18344      * @param {Event} e The event
18345      * @param {Object} data An object containing arbitrary data supplied by the drag source
18346      * @return {Boolean} True if the drop was valid, else false
18347      */
18348     notifyDrop : function(dd, e, data){
18349         return false;
18350     }
18351 });/*
18352  * Based on:
18353  * Ext JS Library 1.1.1
18354  * Copyright(c) 2006-2007, Ext JS, LLC.
18355  *
18356  * Originally Released Under LGPL - original licence link has changed is not relivant.
18357  *
18358  * Fork - LGPL
18359  * <script type="text/javascript">
18360  */
18361
18362
18363 /**
18364  * @class Roo.dd.DragZone
18365  * @extends Roo.dd.DragSource
18366  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18367  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18368  * @constructor
18369  * @param {String/HTMLElement/Element} el The container element
18370  * @param {Object} config
18371  */
18372 Roo.dd.DragZone = function(el, config){
18373     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18374     if(this.containerScroll){
18375         Roo.dd.ScrollManager.register(this.el);
18376     }
18377 };
18378
18379 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18380     /**
18381      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18382      * for auto scrolling during drag operations.
18383      */
18384     /**
18385      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18386      * method after a failed drop (defaults to "c3daf9" - light blue)
18387      */
18388
18389     /**
18390      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18391      * for a valid target to drag based on the mouse down. Override this method
18392      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18393      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18394      * @param {EventObject} e The mouse down event
18395      * @return {Object} The dragData
18396      */
18397     getDragData : function(e){
18398         return Roo.dd.Registry.getHandleFromEvent(e);
18399     },
18400     
18401     /**
18402      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18403      * this.dragData.ddel
18404      * @param {Number} x The x position of the click on the dragged object
18405      * @param {Number} y The y position of the click on the dragged object
18406      * @return {Boolean} true to continue the drag, false to cancel
18407      */
18408     onInitDrag : function(x, y){
18409         this.proxy.update(this.dragData.ddel.cloneNode(true));
18410         this.onStartDrag(x, y);
18411         return true;
18412     },
18413     
18414     /**
18415      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18416      */
18417     afterRepair : function(){
18418         if(Roo.enableFx){
18419             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18420         }
18421         this.dragging = false;
18422     },
18423
18424     /**
18425      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18426      * the XY of this.dragData.ddel
18427      * @param {EventObject} e The mouse up event
18428      * @return {Array} The xy location (e.g. [100, 200])
18429      */
18430     getRepairXY : function(e){
18431         return Roo.Element.fly(this.dragData.ddel).getXY();  
18432     }
18433 });/*
18434  * Based on:
18435  * Ext JS Library 1.1.1
18436  * Copyright(c) 2006-2007, Ext JS, LLC.
18437  *
18438  * Originally Released Under LGPL - original licence link has changed is not relivant.
18439  *
18440  * Fork - LGPL
18441  * <script type="text/javascript">
18442  */
18443 /**
18444  * @class Roo.dd.DropZone
18445  * @extends Roo.dd.DropTarget
18446  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18447  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18448  * @constructor
18449  * @param {String/HTMLElement/Element} el The container element
18450  * @param {Object} config
18451  */
18452 Roo.dd.DropZone = function(el, config){
18453     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18454 };
18455
18456 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18457     /**
18458      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18459      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18460      * provide your own custom lookup.
18461      * @param {Event} e The event
18462      * @return {Object} data The custom data
18463      */
18464     getTargetFromEvent : function(e){
18465         return Roo.dd.Registry.getTargetFromEvent(e);
18466     },
18467
18468     /**
18469      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18470      * that it has registered.  This method has no default implementation and should be overridden to provide
18471      * node-specific processing if necessary.
18472      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18473      * {@link #getTargetFromEvent} for this node)
18474      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18475      * @param {Event} e The event
18476      * @param {Object} data An object containing arbitrary data supplied by the drag source
18477      */
18478     onNodeEnter : function(n, dd, e, data){
18479         
18480     },
18481
18482     /**
18483      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18484      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18485      * overridden to provide the proper feedback.
18486      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18487      * {@link #getTargetFromEvent} for this node)
18488      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18489      * @param {Event} e The event
18490      * @param {Object} data An object containing arbitrary data supplied by the drag source
18491      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18492      * underlying {@link Roo.dd.StatusProxy} can be updated
18493      */
18494     onNodeOver : function(n, dd, e, data){
18495         return this.dropAllowed;
18496     },
18497
18498     /**
18499      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18500      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18501      * node-specific processing if necessary.
18502      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18503      * {@link #getTargetFromEvent} for this node)
18504      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18505      * @param {Event} e The event
18506      * @param {Object} data An object containing arbitrary data supplied by the drag source
18507      */
18508     onNodeOut : function(n, dd, e, data){
18509         
18510     },
18511
18512     /**
18513      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18514      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18515      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18516      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18517      * {@link #getTargetFromEvent} for this node)
18518      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18519      * @param {Event} e The event
18520      * @param {Object} data An object containing arbitrary data supplied by the drag source
18521      * @return {Boolean} True if the drop was valid, else false
18522      */
18523     onNodeDrop : function(n, dd, e, data){
18524         return false;
18525     },
18526
18527     /**
18528      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18529      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18530      * it should be overridden to provide the proper feedback if necessary.
18531      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18532      * @param {Event} e The event
18533      * @param {Object} data An object containing arbitrary data supplied by the drag source
18534      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18535      * underlying {@link Roo.dd.StatusProxy} can be updated
18536      */
18537     onContainerOver : function(dd, e, data){
18538         return this.dropNotAllowed;
18539     },
18540
18541     /**
18542      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18543      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18544      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18545      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18546      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18547      * @param {Event} e The event
18548      * @param {Object} data An object containing arbitrary data supplied by the drag source
18549      * @return {Boolean} True if the drop was valid, else false
18550      */
18551     onContainerDrop : function(dd, e, data){
18552         return false;
18553     },
18554
18555     /**
18556      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18557      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18558      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18559      * you should override this method and provide a custom implementation.
18560      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18561      * @param {Event} e The event
18562      * @param {Object} data An object containing arbitrary data supplied by the drag source
18563      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18564      * underlying {@link Roo.dd.StatusProxy} can be updated
18565      */
18566     notifyEnter : function(dd, e, data){
18567         return this.dropNotAllowed;
18568     },
18569
18570     /**
18571      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18572      * This method will be called on every mouse movement while the drag source is over the drop zone.
18573      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18574      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18575      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18576      * registered node, it will call {@link #onContainerOver}.
18577      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18578      * @param {Event} e The event
18579      * @param {Object} data An object containing arbitrary data supplied by the drag source
18580      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18581      * underlying {@link Roo.dd.StatusProxy} can be updated
18582      */
18583     notifyOver : function(dd, e, data){
18584         var n = this.getTargetFromEvent(e);
18585         if(!n){ // not over valid drop target
18586             if(this.lastOverNode){
18587                 this.onNodeOut(this.lastOverNode, dd, e, data);
18588                 this.lastOverNode = null;
18589             }
18590             return this.onContainerOver(dd, e, data);
18591         }
18592         if(this.lastOverNode != n){
18593             if(this.lastOverNode){
18594                 this.onNodeOut(this.lastOverNode, dd, e, data);
18595             }
18596             this.onNodeEnter(n, dd, e, data);
18597             this.lastOverNode = n;
18598         }
18599         return this.onNodeOver(n, dd, e, data);
18600     },
18601
18602     /**
18603      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18604      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18605      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18606      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18607      * @param {Event} e The event
18608      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18609      */
18610     notifyOut : function(dd, e, data){
18611         if(this.lastOverNode){
18612             this.onNodeOut(this.lastOverNode, dd, e, data);
18613             this.lastOverNode = null;
18614         }
18615     },
18616
18617     /**
18618      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18619      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18620      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18621      * otherwise it will call {@link #onContainerDrop}.
18622      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18623      * @param {Event} e The event
18624      * @param {Object} data An object containing arbitrary data supplied by the drag source
18625      * @return {Boolean} True if the drop was valid, else false
18626      */
18627     notifyDrop : function(dd, e, data){
18628         if(this.lastOverNode){
18629             this.onNodeOut(this.lastOverNode, dd, e, data);
18630             this.lastOverNode = null;
18631         }
18632         var n = this.getTargetFromEvent(e);
18633         return n ?
18634             this.onNodeDrop(n, dd, e, data) :
18635             this.onContainerDrop(dd, e, data);
18636     },
18637
18638     // private
18639     triggerCacheRefresh : function(){
18640         Roo.dd.DDM.refreshCache(this.groups);
18641     }  
18642 });/*
18643  * Based on:
18644  * Ext JS Library 1.1.1
18645  * Copyright(c) 2006-2007, Ext JS, LLC.
18646  *
18647  * Originally Released Under LGPL - original licence link has changed is not relivant.
18648  *
18649  * Fork - LGPL
18650  * <script type="text/javascript">
18651  */
18652
18653
18654 /**
18655  * @class Roo.data.SortTypes
18656  * @singleton
18657  * Defines the default sorting (casting?) comparison functions used when sorting data.
18658  */
18659 Roo.data.SortTypes = {
18660     /**
18661      * Default sort that does nothing
18662      * @param {Mixed} s The value being converted
18663      * @return {Mixed} The comparison value
18664      */
18665     none : function(s){
18666         return s;
18667     },
18668     
18669     /**
18670      * The regular expression used to strip tags
18671      * @type {RegExp}
18672      * @property
18673      */
18674     stripTagsRE : /<\/?[^>]+>/gi,
18675     
18676     /**
18677      * Strips all HTML tags to sort on text only
18678      * @param {Mixed} s The value being converted
18679      * @return {String} The comparison value
18680      */
18681     asText : function(s){
18682         return String(s).replace(this.stripTagsRE, "");
18683     },
18684     
18685     /**
18686      * Strips all HTML tags to sort on text only - Case insensitive
18687      * @param {Mixed} s The value being converted
18688      * @return {String} The comparison value
18689      */
18690     asUCText : function(s){
18691         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18692     },
18693     
18694     /**
18695      * Case insensitive string
18696      * @param {Mixed} s The value being converted
18697      * @return {String} The comparison value
18698      */
18699     asUCString : function(s) {
18700         return String(s).toUpperCase();
18701     },
18702     
18703     /**
18704      * Date sorting
18705      * @param {Mixed} s The value being converted
18706      * @return {Number} The comparison value
18707      */
18708     asDate : function(s) {
18709         if(!s){
18710             return 0;
18711         }
18712         if(s instanceof Date){
18713             return s.getTime();
18714         }
18715         return Date.parse(String(s));
18716     },
18717     
18718     /**
18719      * Float sorting
18720      * @param {Mixed} s The value being converted
18721      * @return {Float} The comparison value
18722      */
18723     asFloat : function(s) {
18724         var val = parseFloat(String(s).replace(/,/g, ""));
18725         if(isNaN(val)) val = 0;
18726         return val;
18727     },
18728     
18729     /**
18730      * Integer sorting
18731      * @param {Mixed} s The value being converted
18732      * @return {Number} The comparison value
18733      */
18734     asInt : function(s) {
18735         var val = parseInt(String(s).replace(/,/g, ""));
18736         if(isNaN(val)) val = 0;
18737         return val;
18738     }
18739 };/*
18740  * Based on:
18741  * Ext JS Library 1.1.1
18742  * Copyright(c) 2006-2007, Ext JS, LLC.
18743  *
18744  * Originally Released Under LGPL - original licence link has changed is not relivant.
18745  *
18746  * Fork - LGPL
18747  * <script type="text/javascript">
18748  */
18749
18750 /**
18751 * @class Roo.data.Record
18752  * Instances of this class encapsulate both record <em>definition</em> information, and record
18753  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18754  * to access Records cached in an {@link Roo.data.Store} object.<br>
18755  * <p>
18756  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18757  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18758  * objects.<br>
18759  * <p>
18760  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18761  * @constructor
18762  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18763  * {@link #create}. The parameters are the same.
18764  * @param {Array} data An associative Array of data values keyed by the field name.
18765  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18766  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18767  * not specified an integer id is generated.
18768  */
18769 Roo.data.Record = function(data, id){
18770     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18771     this.data = data;
18772 };
18773
18774 /**
18775  * Generate a constructor for a specific record layout.
18776  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18777  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18778  * Each field definition object may contain the following properties: <ul>
18779  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
18780  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18781  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18782  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18783  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18784  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18785  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18786  * this may be omitted.</p></li>
18787  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18788  * <ul><li>auto (Default, implies no conversion)</li>
18789  * <li>string</li>
18790  * <li>int</li>
18791  * <li>float</li>
18792  * <li>boolean</li>
18793  * <li>date</li></ul></p></li>
18794  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18795  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18796  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18797  * by the Reader into an object that will be stored in the Record. It is passed the
18798  * following parameters:<ul>
18799  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18800  * </ul></p></li>
18801  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18802  * </ul>
18803  * <br>usage:<br><pre><code>
18804 var TopicRecord = Roo.data.Record.create(
18805     {name: 'title', mapping: 'topic_title'},
18806     {name: 'author', mapping: 'username'},
18807     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18808     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18809     {name: 'lastPoster', mapping: 'user2'},
18810     {name: 'excerpt', mapping: 'post_text'}
18811 );
18812
18813 var myNewRecord = new TopicRecord({
18814     title: 'Do my job please',
18815     author: 'noobie',
18816     totalPosts: 1,
18817     lastPost: new Date(),
18818     lastPoster: 'Animal',
18819     excerpt: 'No way dude!'
18820 });
18821 myStore.add(myNewRecord);
18822 </code></pre>
18823  * @method create
18824  * @static
18825  */
18826 Roo.data.Record.create = function(o){
18827     var f = function(){
18828         f.superclass.constructor.apply(this, arguments);
18829     };
18830     Roo.extend(f, Roo.data.Record);
18831     var p = f.prototype;
18832     p.fields = new Roo.util.MixedCollection(false, function(field){
18833         return field.name;
18834     });
18835     for(var i = 0, len = o.length; i < len; i++){
18836         p.fields.add(new Roo.data.Field(o[i]));
18837     }
18838     f.getField = function(name){
18839         return p.fields.get(name);  
18840     };
18841     return f;
18842 };
18843
18844 Roo.data.Record.AUTO_ID = 1000;
18845 Roo.data.Record.EDIT = 'edit';
18846 Roo.data.Record.REJECT = 'reject';
18847 Roo.data.Record.COMMIT = 'commit';
18848
18849 Roo.data.Record.prototype = {
18850     /**
18851      * Readonly flag - true if this record has been modified.
18852      * @type Boolean
18853      */
18854     dirty : false,
18855     editing : false,
18856     error: null,
18857     modified: null,
18858
18859     // private
18860     join : function(store){
18861         this.store = store;
18862     },
18863
18864     /**
18865      * Set the named field to the specified value.
18866      * @param {String} name The name of the field to set.
18867      * @param {Object} value The value to set the field to.
18868      */
18869     set : function(name, value){
18870         if(this.data[name] == value){
18871             return;
18872         }
18873         this.dirty = true;
18874         if(!this.modified){
18875             this.modified = {};
18876         }
18877         if(typeof this.modified[name] == 'undefined'){
18878             this.modified[name] = this.data[name];
18879         }
18880         this.data[name] = value;
18881         if(!this.editing){
18882             this.store.afterEdit(this);
18883         }       
18884     },
18885
18886     /**
18887      * Get the value of the named field.
18888      * @param {String} name The name of the field to get the value of.
18889      * @return {Object} The value of the field.
18890      */
18891     get : function(name){
18892         return this.data[name]; 
18893     },
18894
18895     // private
18896     beginEdit : function(){
18897         this.editing = true;
18898         this.modified = {}; 
18899     },
18900
18901     // private
18902     cancelEdit : function(){
18903         this.editing = false;
18904         delete this.modified;
18905     },
18906
18907     // private
18908     endEdit : function(){
18909         this.editing = false;
18910         if(this.dirty && this.store){
18911             this.store.afterEdit(this);
18912         }
18913     },
18914
18915     /**
18916      * Usually called by the {@link Roo.data.Store} which owns the Record.
18917      * Rejects all changes made to the Record since either creation, or the last commit operation.
18918      * Modified fields are reverted to their original values.
18919      * <p>
18920      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18921      * of reject operations.
18922      */
18923     reject : function(){
18924         var m = this.modified;
18925         for(var n in m){
18926             if(typeof m[n] != "function"){
18927                 this.data[n] = m[n];
18928             }
18929         }
18930         this.dirty = false;
18931         delete this.modified;
18932         this.editing = false;
18933         if(this.store){
18934             this.store.afterReject(this);
18935         }
18936     },
18937
18938     /**
18939      * Usually called by the {@link Roo.data.Store} which owns the Record.
18940      * Commits all changes made to the Record since either creation, or the last commit operation.
18941      * <p>
18942      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18943      * of commit operations.
18944      */
18945     commit : function(){
18946         this.dirty = false;
18947         delete this.modified;
18948         this.editing = false;
18949         if(this.store){
18950             this.store.afterCommit(this);
18951         }
18952     },
18953
18954     // private
18955     hasError : function(){
18956         return this.error != null;
18957     },
18958
18959     // private
18960     clearError : function(){
18961         this.error = null;
18962     },
18963
18964     /**
18965      * Creates a copy of this record.
18966      * @param {String} id (optional) A new record id if you don't want to use this record's id
18967      * @return {Record}
18968      */
18969     copy : function(newId) {
18970         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18971     }
18972 };/*
18973  * Based on:
18974  * Ext JS Library 1.1.1
18975  * Copyright(c) 2006-2007, Ext JS, LLC.
18976  *
18977  * Originally Released Under LGPL - original licence link has changed is not relivant.
18978  *
18979  * Fork - LGPL
18980  * <script type="text/javascript">
18981  */
18982
18983
18984
18985 /**
18986  * @class Roo.data.Store
18987  * @extends Roo.util.Observable
18988  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18989  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18990  * <p>
18991  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
18992  * has no knowledge of the format of the data returned by the Proxy.<br>
18993  * <p>
18994  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18995  * instances from the data object. These records are cached and made available through accessor functions.
18996  * @constructor
18997  * Creates a new Store.
18998  * @param {Object} config A config object containing the objects needed for the Store to access data,
18999  * and read the data into Records.
19000  */
19001 Roo.data.Store = function(config){
19002     this.data = new Roo.util.MixedCollection(false);
19003     this.data.getKey = function(o){
19004         return o.id;
19005     };
19006     this.baseParams = {};
19007     // private
19008     this.paramNames = {
19009         "start" : "start",
19010         "limit" : "limit",
19011         "sort" : "sort",
19012         "dir" : "dir"
19013     };
19014
19015     if(config && config.data){
19016         this.inlineData = config.data;
19017         delete config.data;
19018     }
19019
19020     Roo.apply(this, config);
19021     
19022     if(this.reader){ // reader passed
19023         this.reader = Roo.factory(this.reader, Roo.data);
19024         this.reader.xmodule = this.xmodule || false;
19025         if(!this.recordType){
19026             this.recordType = this.reader.recordType;
19027         }
19028         if(this.reader.onMetaChange){
19029             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19030         }
19031     }
19032
19033     if(this.recordType){
19034         this.fields = this.recordType.prototype.fields;
19035     }
19036     this.modified = [];
19037
19038     this.addEvents({
19039         /**
19040          * @event datachanged
19041          * Fires when the data cache has changed, and a widget which is using this Store
19042          * as a Record cache should refresh its view.
19043          * @param {Store} this
19044          */
19045         datachanged : true,
19046         /**
19047          * @event metachange
19048          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19049          * @param {Store} this
19050          * @param {Object} meta The JSON metadata
19051          */
19052         metachange : true,
19053         /**
19054          * @event add
19055          * Fires when Records have been added to the Store
19056          * @param {Store} this
19057          * @param {Roo.data.Record[]} records The array of Records added
19058          * @param {Number} index The index at which the record(s) were added
19059          */
19060         add : true,
19061         /**
19062          * @event remove
19063          * Fires when a Record has been removed from the Store
19064          * @param {Store} this
19065          * @param {Roo.data.Record} record The Record that was removed
19066          * @param {Number} index The index at which the record was removed
19067          */
19068         remove : true,
19069         /**
19070          * @event update
19071          * Fires when a Record has been updated
19072          * @param {Store} this
19073          * @param {Roo.data.Record} record The Record that was updated
19074          * @param {String} operation The update operation being performed.  Value may be one of:
19075          * <pre><code>
19076  Roo.data.Record.EDIT
19077  Roo.data.Record.REJECT
19078  Roo.data.Record.COMMIT
19079          * </code></pre>
19080          */
19081         update : true,
19082         /**
19083          * @event clear
19084          * Fires when the data cache has been cleared.
19085          * @param {Store} this
19086          */
19087         clear : true,
19088         /**
19089          * @event beforeload
19090          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19091          * the load action will be canceled.
19092          * @param {Store} this
19093          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19094          */
19095         beforeload : true,
19096         /**
19097          * @event load
19098          * Fires after a new set of Records has been loaded.
19099          * @param {Store} this
19100          * @param {Roo.data.Record[]} records The Records that were loaded
19101          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19102          */
19103         load : true,
19104         /**
19105          * @event loadexception
19106          * Fires if an exception occurs in the Proxy during loading.
19107          * Called with the signature of the Proxy's "loadexception" event.
19108          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19109          * 
19110          * @param {Proxy} 
19111          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19112          * @param {Object} load options 
19113          * @param {Object} jsonData from your request (normally this contains the Exception)
19114          */
19115         loadexception : true
19116     });
19117     
19118     if(this.proxy){
19119         this.proxy = Roo.factory(this.proxy, Roo.data);
19120         this.proxy.xmodule = this.xmodule || false;
19121         this.relayEvents(this.proxy,  ["loadexception"]);
19122     }
19123     this.sortToggle = {};
19124
19125     Roo.data.Store.superclass.constructor.call(this);
19126
19127     if(this.inlineData){
19128         this.loadData(this.inlineData);
19129         delete this.inlineData;
19130     }
19131 };
19132 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19133      /**
19134     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19135     * without a remote query - used by combo/forms at present.
19136     */
19137     
19138     /**
19139     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19140     */
19141     /**
19142     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19143     */
19144     /**
19145     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19146     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19147     */
19148     /**
19149     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19150     * on any HTTP request
19151     */
19152     /**
19153     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19154     */
19155     /**
19156     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19157     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19158     */
19159     remoteSort : false,
19160
19161     /**
19162     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19163      * loaded or when a record is removed. (defaults to false).
19164     */
19165     pruneModifiedRecords : false,
19166
19167     // private
19168     lastOptions : null,
19169
19170     /**
19171      * Add Records to the Store and fires the add event.
19172      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19173      */
19174     add : function(records){
19175         records = [].concat(records);
19176         for(var i = 0, len = records.length; i < len; i++){
19177             records[i].join(this);
19178         }
19179         var index = this.data.length;
19180         this.data.addAll(records);
19181         this.fireEvent("add", this, records, index);
19182     },
19183
19184     /**
19185      * Remove a Record from the Store and fires the remove event.
19186      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19187      */
19188     remove : function(record){
19189         var index = this.data.indexOf(record);
19190         this.data.removeAt(index);
19191         if(this.pruneModifiedRecords){
19192             this.modified.remove(record);
19193         }
19194         this.fireEvent("remove", this, record, index);
19195     },
19196
19197     /**
19198      * Remove all Records from the Store and fires the clear event.
19199      */
19200     removeAll : function(){
19201         this.data.clear();
19202         if(this.pruneModifiedRecords){
19203             this.modified = [];
19204         }
19205         this.fireEvent("clear", this);
19206     },
19207
19208     /**
19209      * Inserts Records to the Store at the given index and fires the add event.
19210      * @param {Number} index The start index at which to insert the passed Records.
19211      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19212      */
19213     insert : function(index, records){
19214         records = [].concat(records);
19215         for(var i = 0, len = records.length; i < len; i++){
19216             this.data.insert(index, records[i]);
19217             records[i].join(this);
19218         }
19219         this.fireEvent("add", this, records, index);
19220     },
19221
19222     /**
19223      * Get the index within the cache of the passed Record.
19224      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19225      * @return {Number} The index of the passed Record. Returns -1 if not found.
19226      */
19227     indexOf : function(record){
19228         return this.data.indexOf(record);
19229     },
19230
19231     /**
19232      * Get the index within the cache of the Record with the passed id.
19233      * @param {String} id The id of the Record to find.
19234      * @return {Number} The index of the Record. Returns -1 if not found.
19235      */
19236     indexOfId : function(id){
19237         return this.data.indexOfKey(id);
19238     },
19239
19240     /**
19241      * Get the Record with the specified id.
19242      * @param {String} id The id of the Record to find.
19243      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19244      */
19245     getById : function(id){
19246         return this.data.key(id);
19247     },
19248
19249     /**
19250      * Get the Record at the specified index.
19251      * @param {Number} index The index of the Record to find.
19252      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19253      */
19254     getAt : function(index){
19255         return this.data.itemAt(index);
19256     },
19257
19258     /**
19259      * Returns a range of Records between specified indices.
19260      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19261      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19262      * @return {Roo.data.Record[]} An array of Records
19263      */
19264     getRange : function(start, end){
19265         return this.data.getRange(start, end);
19266     },
19267
19268     // private
19269     storeOptions : function(o){
19270         o = Roo.apply({}, o);
19271         delete o.callback;
19272         delete o.scope;
19273         this.lastOptions = o;
19274     },
19275
19276     /**
19277      * Loads the Record cache from the configured Proxy using the configured Reader.
19278      * <p>
19279      * If using remote paging, then the first load call must specify the <em>start</em>
19280      * and <em>limit</em> properties in the options.params property to establish the initial
19281      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19282      * <p>
19283      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19284      * and this call will return before the new data has been loaded. Perform any post-processing
19285      * in a callback function, or in a "load" event handler.</strong>
19286      * <p>
19287      * @param {Object} options An object containing properties which control loading options:<ul>
19288      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19289      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19290      * passed the following arguments:<ul>
19291      * <li>r : Roo.data.Record[]</li>
19292      * <li>options: Options object from the load call</li>
19293      * <li>success: Boolean success indicator</li></ul></li>
19294      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19295      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19296      * </ul>
19297      */
19298     load : function(options){
19299         options = options || {};
19300         if(this.fireEvent("beforeload", this, options) !== false){
19301             this.storeOptions(options);
19302             var p = Roo.apply(options.params || {}, this.baseParams);
19303             // if meta was not loaded from remote source.. try requesting it.
19304             if (!this.reader.metaFromRemote) {
19305                 p._requestMeta = 1;
19306             }
19307             if(this.sortInfo && this.remoteSort){
19308                 var pn = this.paramNames;
19309                 p[pn["sort"]] = this.sortInfo.field;
19310                 p[pn["dir"]] = this.sortInfo.direction;
19311             }
19312             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19313         }
19314     },
19315
19316     /**
19317      * Reloads the Record cache from the configured Proxy using the configured Reader and
19318      * the options from the last load operation performed.
19319      * @param {Object} options (optional) An object containing properties which may override the options
19320      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19321      * the most recently used options are reused).
19322      */
19323     reload : function(options){
19324         this.load(Roo.applyIf(options||{}, this.lastOptions));
19325     },
19326
19327     // private
19328     // Called as a callback by the Reader during a load operation.
19329     loadRecords : function(o, options, success){
19330         if(!o || success === false){
19331             if(success !== false){
19332                 this.fireEvent("load", this, [], options);
19333             }
19334             if(options.callback){
19335                 options.callback.call(options.scope || this, [], options, false);
19336             }
19337             return;
19338         }
19339         // if data returned failure - throw an exception.
19340         if (o.success === false) {
19341             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19342             return;
19343         }
19344         var r = o.records, t = o.totalRecords || r.length;
19345         if(!options || options.add !== true){
19346             if(this.pruneModifiedRecords){
19347                 this.modified = [];
19348             }
19349             for(var i = 0, len = r.length; i < len; i++){
19350                 r[i].join(this);
19351             }
19352             if(this.snapshot){
19353                 this.data = this.snapshot;
19354                 delete this.snapshot;
19355             }
19356             this.data.clear();
19357             this.data.addAll(r);
19358             this.totalLength = t;
19359             this.applySort();
19360             this.fireEvent("datachanged", this);
19361         }else{
19362             this.totalLength = Math.max(t, this.data.length+r.length);
19363             this.add(r);
19364         }
19365         this.fireEvent("load", this, r, options);
19366         if(options.callback){
19367             options.callback.call(options.scope || this, r, options, true);
19368         }
19369     },
19370
19371     /**
19372      * Loads data from a passed data block. A Reader which understands the format of the data
19373      * must have been configured in the constructor.
19374      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19375      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19376      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19377      */
19378     loadData : function(o, append){
19379         var r = this.reader.readRecords(o);
19380         this.loadRecords(r, {add: append}, true);
19381     },
19382
19383     /**
19384      * Gets the number of cached records.
19385      * <p>
19386      * <em>If using paging, this may not be the total size of the dataset. If the data object
19387      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19388      * the data set size</em>
19389      */
19390     getCount : function(){
19391         return this.data.length || 0;
19392     },
19393
19394     /**
19395      * Gets the total number of records in the dataset as returned by the server.
19396      * <p>
19397      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19398      * the dataset size</em>
19399      */
19400     getTotalCount : function(){
19401         return this.totalLength || 0;
19402     },
19403
19404     /**
19405      * Returns the sort state of the Store as an object with two properties:
19406      * <pre><code>
19407  field {String} The name of the field by which the Records are sorted
19408  direction {String} The sort order, "ASC" or "DESC"
19409      * </code></pre>
19410      */
19411     getSortState : function(){
19412         return this.sortInfo;
19413     },
19414
19415     // private
19416     applySort : function(){
19417         if(this.sortInfo && !this.remoteSort){
19418             var s = this.sortInfo, f = s.field;
19419             var st = this.fields.get(f).sortType;
19420             var fn = function(r1, r2){
19421                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19422                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19423             };
19424             this.data.sort(s.direction, fn);
19425             if(this.snapshot && this.snapshot != this.data){
19426                 this.snapshot.sort(s.direction, fn);
19427             }
19428         }
19429     },
19430
19431     /**
19432      * Sets the default sort column and order to be used by the next load operation.
19433      * @param {String} fieldName The name of the field to sort by.
19434      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19435      */
19436     setDefaultSort : function(field, dir){
19437         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19438     },
19439
19440     /**
19441      * Sort the Records.
19442      * If remote sorting is used, the sort is performed on the server, and the cache is
19443      * reloaded. If local sorting is used, the cache is sorted internally.
19444      * @param {String} fieldName The name of the field to sort by.
19445      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19446      */
19447     sort : function(fieldName, dir){
19448         var f = this.fields.get(fieldName);
19449         if(!dir){
19450             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19451                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19452             }else{
19453                 dir = f.sortDir;
19454             }
19455         }
19456         this.sortToggle[f.name] = dir;
19457         this.sortInfo = {field: f.name, direction: dir};
19458         if(!this.remoteSort){
19459             this.applySort();
19460             this.fireEvent("datachanged", this);
19461         }else{
19462             this.load(this.lastOptions);
19463         }
19464     },
19465
19466     /**
19467      * Calls the specified function for each of the Records in the cache.
19468      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19469      * Returning <em>false</em> aborts and exits the iteration.
19470      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19471      */
19472     each : function(fn, scope){
19473         this.data.each(fn, scope);
19474     },
19475
19476     /**
19477      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19478      * (e.g., during paging).
19479      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19480      */
19481     getModifiedRecords : function(){
19482         return this.modified;
19483     },
19484
19485     // private
19486     createFilterFn : function(property, value, anyMatch){
19487         if(!value.exec){ // not a regex
19488             value = String(value);
19489             if(value.length == 0){
19490                 return false;
19491             }
19492             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19493         }
19494         return function(r){
19495             return value.test(r.data[property]);
19496         };
19497     },
19498
19499     /**
19500      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19501      * @param {String} property A field on your records
19502      * @param {Number} start The record index to start at (defaults to 0)
19503      * @param {Number} end The last record index to include (defaults to length - 1)
19504      * @return {Number} The sum
19505      */
19506     sum : function(property, start, end){
19507         var rs = this.data.items, v = 0;
19508         start = start || 0;
19509         end = (end || end === 0) ? end : rs.length-1;
19510
19511         for(var i = start; i <= end; i++){
19512             v += (rs[i].data[property] || 0);
19513         }
19514         return v;
19515     },
19516
19517     /**
19518      * Filter the records by a specified property.
19519      * @param {String} field A field on your records
19520      * @param {String/RegExp} value Either a string that the field
19521      * should start with or a RegExp to test against the field
19522      * @param {Boolean} anyMatch True to match any part not just the beginning
19523      */
19524     filter : function(property, value, anyMatch){
19525         var fn = this.createFilterFn(property, value, anyMatch);
19526         return fn ? this.filterBy(fn) : this.clearFilter();
19527     },
19528
19529     /**
19530      * Filter by a function. The specified function will be called with each
19531      * record in this data source. If the function returns true the record is included,
19532      * otherwise it is filtered.
19533      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19534      * @param {Object} scope (optional) The scope of the function (defaults to this)
19535      */
19536     filterBy : function(fn, scope){
19537         this.snapshot = this.snapshot || this.data;
19538         this.data = this.queryBy(fn, scope||this);
19539         this.fireEvent("datachanged", this);
19540     },
19541
19542     /**
19543      * Query the records by a specified property.
19544      * @param {String} field A field on your records
19545      * @param {String/RegExp} value Either a string that the field
19546      * should start with or a RegExp to test against the field
19547      * @param {Boolean} anyMatch True to match any part not just the beginning
19548      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19549      */
19550     query : function(property, value, anyMatch){
19551         var fn = this.createFilterFn(property, value, anyMatch);
19552         return fn ? this.queryBy(fn) : this.data.clone();
19553     },
19554
19555     /**
19556      * Query by a function. The specified function will be called with each
19557      * record in this data source. If the function returns true the record is included
19558      * in the results.
19559      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19560      * @param {Object} scope (optional) The scope of the function (defaults to this)
19561       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19562      **/
19563     queryBy : function(fn, scope){
19564         var data = this.snapshot || this.data;
19565         return data.filterBy(fn, scope||this);
19566     },
19567
19568     /**
19569      * Collects unique values for a particular dataIndex from this store.
19570      * @param {String} dataIndex The property to collect
19571      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19572      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19573      * @return {Array} An array of the unique values
19574      **/
19575     collect : function(dataIndex, allowNull, bypassFilter){
19576         var d = (bypassFilter === true && this.snapshot) ?
19577                 this.snapshot.items : this.data.items;
19578         var v, sv, r = [], l = {};
19579         for(var i = 0, len = d.length; i < len; i++){
19580             v = d[i].data[dataIndex];
19581             sv = String(v);
19582             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19583                 l[sv] = true;
19584                 r[r.length] = v;
19585             }
19586         }
19587         return r;
19588     },
19589
19590     /**
19591      * Revert to a view of the Record cache with no filtering applied.
19592      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19593      */
19594     clearFilter : function(suppressEvent){
19595         if(this.snapshot && this.snapshot != this.data){
19596             this.data = this.snapshot;
19597             delete this.snapshot;
19598             if(suppressEvent !== true){
19599                 this.fireEvent("datachanged", this);
19600             }
19601         }
19602     },
19603
19604     // private
19605     afterEdit : function(record){
19606         if(this.modified.indexOf(record) == -1){
19607             this.modified.push(record);
19608         }
19609         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19610     },
19611
19612     // private
19613     afterReject : function(record){
19614         this.modified.remove(record);
19615         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19616     },
19617
19618     // private
19619     afterCommit : function(record){
19620         this.modified.remove(record);
19621         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19622     },
19623
19624     /**
19625      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19626      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19627      */
19628     commitChanges : function(){
19629         var m = this.modified.slice(0);
19630         this.modified = [];
19631         for(var i = 0, len = m.length; i < len; i++){
19632             m[i].commit();
19633         }
19634     },
19635
19636     /**
19637      * Cancel outstanding changes on all changed records.
19638      */
19639     rejectChanges : function(){
19640         var m = this.modified.slice(0);
19641         this.modified = [];
19642         for(var i = 0, len = m.length; i < len; i++){
19643             m[i].reject();
19644         }
19645     },
19646
19647     onMetaChange : function(meta, rtype, o){
19648         this.recordType = rtype;
19649         this.fields = rtype.prototype.fields;
19650         delete this.snapshot;
19651         this.sortInfo = meta.sortInfo || this.sortInfo;
19652         this.modified = [];
19653         this.fireEvent('metachange', this, this.reader.meta);
19654     }
19655 });/*
19656  * Based on:
19657  * Ext JS Library 1.1.1
19658  * Copyright(c) 2006-2007, Ext JS, LLC.
19659  *
19660  * Originally Released Under LGPL - original licence link has changed is not relivant.
19661  *
19662  * Fork - LGPL
19663  * <script type="text/javascript">
19664  */
19665
19666 /**
19667  * @class Roo.data.SimpleStore
19668  * @extends Roo.data.Store
19669  * Small helper class to make creating Stores from Array data easier.
19670  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19671  * @cfg {Array} fields An array of field definition objects, or field name strings.
19672  * @cfg {Array} data The multi-dimensional array of data
19673  * @constructor
19674  * @param {Object} config
19675  */
19676 Roo.data.SimpleStore = function(config){
19677     Roo.data.SimpleStore.superclass.constructor.call(this, {
19678         isLocal : true,
19679         reader: new Roo.data.ArrayReader({
19680                 id: config.id
19681             },
19682             Roo.data.Record.create(config.fields)
19683         ),
19684         proxy : new Roo.data.MemoryProxy(config.data)
19685     });
19686     this.load();
19687 };
19688 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19689  * Based on:
19690  * Ext JS Library 1.1.1
19691  * Copyright(c) 2006-2007, Ext JS, LLC.
19692  *
19693  * Originally Released Under LGPL - original licence link has changed is not relivant.
19694  *
19695  * Fork - LGPL
19696  * <script type="text/javascript">
19697  */
19698
19699 /**
19700 /**
19701  * @extends Roo.data.Store
19702  * @class Roo.data.JsonStore
19703  * Small helper class to make creating Stores for JSON data easier. <br/>
19704 <pre><code>
19705 var store = new Roo.data.JsonStore({
19706     url: 'get-images.php',
19707     root: 'images',
19708     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19709 });
19710 </code></pre>
19711  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19712  * JsonReader and HttpProxy (unless inline data is provided).</b>
19713  * @cfg {Array} fields An array of field definition objects, or field name strings.
19714  * @constructor
19715  * @param {Object} config
19716  */
19717 Roo.data.JsonStore = function(c){
19718     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19719         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19720         reader: new Roo.data.JsonReader(c, c.fields)
19721     }));
19722 };
19723 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19724  * Based on:
19725  * Ext JS Library 1.1.1
19726  * Copyright(c) 2006-2007, Ext JS, LLC.
19727  *
19728  * Originally Released Under LGPL - original licence link has changed is not relivant.
19729  *
19730  * Fork - LGPL
19731  * <script type="text/javascript">
19732  */
19733
19734  
19735 Roo.data.Field = function(config){
19736     if(typeof config == "string"){
19737         config = {name: config};
19738     }
19739     Roo.apply(this, config);
19740     
19741     if(!this.type){
19742         this.type = "auto";
19743     }
19744     
19745     var st = Roo.data.SortTypes;
19746     // named sortTypes are supported, here we look them up
19747     if(typeof this.sortType == "string"){
19748         this.sortType = st[this.sortType];
19749     }
19750     
19751     // set default sortType for strings and dates
19752     if(!this.sortType){
19753         switch(this.type){
19754             case "string":
19755                 this.sortType = st.asUCString;
19756                 break;
19757             case "date":
19758                 this.sortType = st.asDate;
19759                 break;
19760             default:
19761                 this.sortType = st.none;
19762         }
19763     }
19764
19765     // define once
19766     var stripRe = /[\$,%]/g;
19767
19768     // prebuilt conversion function for this field, instead of
19769     // switching every time we're reading a value
19770     if(!this.convert){
19771         var cv, dateFormat = this.dateFormat;
19772         switch(this.type){
19773             case "":
19774             case "auto":
19775             case undefined:
19776                 cv = function(v){ return v; };
19777                 break;
19778             case "string":
19779                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19780                 break;
19781             case "int":
19782                 cv = function(v){
19783                     return v !== undefined && v !== null && v !== '' ?
19784                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19785                     };
19786                 break;
19787             case "float":
19788                 cv = function(v){
19789                     return v !== undefined && v !== null && v !== '' ?
19790                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19791                     };
19792                 break;
19793             case "bool":
19794             case "boolean":
19795                 cv = function(v){ return v === true || v === "true" || v == 1; };
19796                 break;
19797             case "date":
19798                 cv = function(v){
19799                     if(!v){
19800                         return '';
19801                     }
19802                     if(v instanceof Date){
19803                         return v;
19804                     }
19805                     if(dateFormat){
19806                         if(dateFormat == "timestamp"){
19807                             return new Date(v*1000);
19808                         }
19809                         return Date.parseDate(v, dateFormat);
19810                     }
19811                     var parsed = Date.parse(v);
19812                     return parsed ? new Date(parsed) : null;
19813                 };
19814              break;
19815             
19816         }
19817         this.convert = cv;
19818     }
19819 };
19820
19821 Roo.data.Field.prototype = {
19822     dateFormat: null,
19823     defaultValue: "",
19824     mapping: null,
19825     sortType : null,
19826     sortDir : "ASC"
19827 };/*
19828  * Based on:
19829  * Ext JS Library 1.1.1
19830  * Copyright(c) 2006-2007, Ext JS, LLC.
19831  *
19832  * Originally Released Under LGPL - original licence link has changed is not relivant.
19833  *
19834  * Fork - LGPL
19835  * <script type="text/javascript">
19836  */
19837  
19838 // Base class for reading structured data from a data source.  This class is intended to be
19839 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19840
19841 /**
19842  * @class Roo.data.DataReader
19843  * Base class for reading structured data from a data source.  This class is intended to be
19844  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19845  */
19846
19847 Roo.data.DataReader = function(meta, recordType){
19848     
19849     this.meta = meta;
19850     
19851     this.recordType = recordType instanceof Array ? 
19852         Roo.data.Record.create(recordType) : recordType;
19853 };
19854
19855 Roo.data.DataReader.prototype = {
19856      /**
19857      * Create an empty record
19858      * @param {Object} data (optional) - overlay some values
19859      * @return {Roo.data.Record} record created.
19860      */
19861     newRow :  function(d) {
19862         var da =  {};
19863         this.recordType.prototype.fields.each(function(c) {
19864             switch( c.type) {
19865                 case 'int' : da[c.name] = 0; break;
19866                 case 'date' : da[c.name] = new Date(); break;
19867                 case 'float' : da[c.name] = 0.0; break;
19868                 case 'boolean' : da[c.name] = false; break;
19869                 default : da[c.name] = ""; break;
19870             }
19871             
19872         });
19873         return new this.recordType(Roo.apply(da, d));
19874     }
19875     
19876 };/*
19877  * Based on:
19878  * Ext JS Library 1.1.1
19879  * Copyright(c) 2006-2007, Ext JS, LLC.
19880  *
19881  * Originally Released Under LGPL - original licence link has changed is not relivant.
19882  *
19883  * Fork - LGPL
19884  * <script type="text/javascript">
19885  */
19886
19887 /**
19888  * @class Roo.data.DataProxy
19889  * @extends Roo.data.Observable
19890  * This class is an abstract base class for implementations which provide retrieval of
19891  * unformatted data objects.<br>
19892  * <p>
19893  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19894  * (of the appropriate type which knows how to parse the data object) to provide a block of
19895  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19896  * <p>
19897  * Custom implementations must implement the load method as described in
19898  * {@link Roo.data.HttpProxy#load}.
19899  */
19900 Roo.data.DataProxy = function(){
19901     this.addEvents({
19902         /**
19903          * @event beforeload
19904          * Fires before a network request is made to retrieve a data object.
19905          * @param {Object} This DataProxy object.
19906          * @param {Object} params The params parameter to the load function.
19907          */
19908         beforeload : true,
19909         /**
19910          * @event load
19911          * Fires before the load method's callback is called.
19912          * @param {Object} This DataProxy object.
19913          * @param {Object} o The data object.
19914          * @param {Object} arg The callback argument object passed to the load function.
19915          */
19916         load : true,
19917         /**
19918          * @event loadexception
19919          * Fires if an Exception occurs during data retrieval.
19920          * @param {Object} This DataProxy object.
19921          * @param {Object} o The data object.
19922          * @param {Object} arg The callback argument object passed to the load function.
19923          * @param {Object} e The Exception.
19924          */
19925         loadexception : true
19926     });
19927     Roo.data.DataProxy.superclass.constructor.call(this);
19928 };
19929
19930 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19931
19932     /**
19933      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19934      */
19935 /*
19936  * Based on:
19937  * Ext JS Library 1.1.1
19938  * Copyright(c) 2006-2007, Ext JS, LLC.
19939  *
19940  * Originally Released Under LGPL - original licence link has changed is not relivant.
19941  *
19942  * Fork - LGPL
19943  * <script type="text/javascript">
19944  */
19945 /**
19946  * @class Roo.data.MemoryProxy
19947  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19948  * to the Reader when its load method is called.
19949  * @constructor
19950  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19951  */
19952 Roo.data.MemoryProxy = function(data){
19953     if (data.data) {
19954         data = data.data;
19955     }
19956     Roo.data.MemoryProxy.superclass.constructor.call(this);
19957     this.data = data;
19958 };
19959
19960 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19961     /**
19962      * Load data from the requested source (in this case an in-memory
19963      * data object passed to the constructor), read the data object into
19964      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19965      * process that block using the passed callback.
19966      * @param {Object} params This parameter is not used by the MemoryProxy class.
19967      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19968      * object into a block of Roo.data.Records.
19969      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19970      * The function must be passed <ul>
19971      * <li>The Record block object</li>
19972      * <li>The "arg" argument from the load function</li>
19973      * <li>A boolean success indicator</li>
19974      * </ul>
19975      * @param {Object} scope The scope in which to call the callback
19976      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19977      */
19978     load : function(params, reader, callback, scope, arg){
19979         params = params || {};
19980         var result;
19981         try {
19982             result = reader.readRecords(this.data);
19983         }catch(e){
19984             this.fireEvent("loadexception", this, arg, null, e);
19985             callback.call(scope, null, arg, false);
19986             return;
19987         }
19988         callback.call(scope, result, arg, true);
19989     },
19990     
19991     // private
19992     update : function(params, records){
19993         
19994     }
19995 });/*
19996  * Based on:
19997  * Ext JS Library 1.1.1
19998  * Copyright(c) 2006-2007, Ext JS, LLC.
19999  *
20000  * Originally Released Under LGPL - original licence link has changed is not relivant.
20001  *
20002  * Fork - LGPL
20003  * <script type="text/javascript">
20004  */
20005 /**
20006  * @class Roo.data.HttpProxy
20007  * @extends Roo.data.DataProxy
20008  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20009  * configured to reference a certain URL.<br><br>
20010  * <p>
20011  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20012  * from which the running page was served.<br><br>
20013  * <p>
20014  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20015  * <p>
20016  * Be aware that to enable the browser to parse an XML document, the server must set
20017  * the Content-Type header in the HTTP response to "text/xml".
20018  * @constructor
20019  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20020  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20021  * will be used to make the request.
20022  */
20023 Roo.data.HttpProxy = function(conn){
20024     Roo.data.HttpProxy.superclass.constructor.call(this);
20025     // is conn a conn config or a real conn?
20026     this.conn = conn;
20027     this.useAjax = !conn || !conn.events;
20028   
20029 };
20030
20031 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20032     // thse are take from connection...
20033     
20034     /**
20035      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20036      */
20037     /**
20038      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20039      * extra parameters to each request made by this object. (defaults to undefined)
20040      */
20041     /**
20042      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20043      *  to each request made by this object. (defaults to undefined)
20044      */
20045     /**
20046      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
20047      */
20048     /**
20049      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20050      */
20051      /**
20052      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20053      * @type Boolean
20054      */
20055   
20056
20057     /**
20058      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20059      * @type Boolean
20060      */
20061     /**
20062      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20063      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20064      * a finer-grained basis than the DataProxy events.
20065      */
20066     getConnection : function(){
20067         return this.useAjax ? Roo.Ajax : this.conn;
20068     },
20069
20070     /**
20071      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20072      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20073      * process that block using the passed callback.
20074      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20075      * for the request to the remote server.
20076      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20077      * object into a block of Roo.data.Records.
20078      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20079      * The function must be passed <ul>
20080      * <li>The Record block object</li>
20081      * <li>The "arg" argument from the load function</li>
20082      * <li>A boolean success indicator</li>
20083      * </ul>
20084      * @param {Object} scope The scope in which to call the callback
20085      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20086      */
20087     load : function(params, reader, callback, scope, arg){
20088         if(this.fireEvent("beforeload", this, params) !== false){
20089             var  o = {
20090                 params : params || {},
20091                 request: {
20092                     callback : callback,
20093                     scope : scope,
20094                     arg : arg
20095                 },
20096                 reader: reader,
20097                 callback : this.loadResponse,
20098                 scope: this
20099             };
20100             if(this.useAjax){
20101                 Roo.applyIf(o, this.conn);
20102                 if(this.activeRequest){
20103                     Roo.Ajax.abort(this.activeRequest);
20104                 }
20105                 this.activeRequest = Roo.Ajax.request(o);
20106             }else{
20107                 this.conn.request(o);
20108             }
20109         }else{
20110             callback.call(scope||this, null, arg, false);
20111         }
20112     },
20113
20114     // private
20115     loadResponse : function(o, success, response){
20116         delete this.activeRequest;
20117         if(!success){
20118             this.fireEvent("loadexception", this, o, response);
20119             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20120             return;
20121         }
20122         var result;
20123         try {
20124             result = o.reader.read(response);
20125         }catch(e){
20126             this.fireEvent("loadexception", this, o, response, e);
20127             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20128             return;
20129         }
20130         
20131         this.fireEvent("load", this, o, o.request.arg);
20132         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20133     },
20134
20135     // private
20136     update : function(dataSet){
20137
20138     },
20139
20140     // private
20141     updateResponse : function(dataSet){
20142
20143     }
20144 });/*
20145  * Based on:
20146  * Ext JS Library 1.1.1
20147  * Copyright(c) 2006-2007, Ext JS, LLC.
20148  *
20149  * Originally Released Under LGPL - original licence link has changed is not relivant.
20150  *
20151  * Fork - LGPL
20152  * <script type="text/javascript">
20153  */
20154
20155 /**
20156  * @class Roo.data.ScriptTagProxy
20157  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20158  * other than the originating domain of the running page.<br><br>
20159  * <p>
20160  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
20161  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20162  * <p>
20163  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20164  * source code that is used as the source inside a &lt;script> tag.<br><br>
20165  * <p>
20166  * In order for the browser to process the returned data, the server must wrap the data object
20167  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20168  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20169  * depending on whether the callback name was passed:
20170  * <p>
20171  * <pre><code>
20172 boolean scriptTag = false;
20173 String cb = request.getParameter("callback");
20174 if (cb != null) {
20175     scriptTag = true;
20176     response.setContentType("text/javascript");
20177 } else {
20178     response.setContentType("application/x-json");
20179 }
20180 Writer out = response.getWriter();
20181 if (scriptTag) {
20182     out.write(cb + "(");
20183 }
20184 out.print(dataBlock.toJsonString());
20185 if (scriptTag) {
20186     out.write(");");
20187 }
20188 </pre></code>
20189  *
20190  * @constructor
20191  * @param {Object} config A configuration object.
20192  */
20193 Roo.data.ScriptTagProxy = function(config){
20194     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20195     Roo.apply(this, config);
20196     this.head = document.getElementsByTagName("head")[0];
20197 };
20198
20199 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20200
20201 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20202     /**
20203      * @cfg {String} url The URL from which to request the data object.
20204      */
20205     /**
20206      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20207      */
20208     timeout : 30000,
20209     /**
20210      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20211      * the server the name of the callback function set up by the load call to process the returned data object.
20212      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20213      * javascript output which calls this named function passing the data object as its only parameter.
20214      */
20215     callbackParam : "callback",
20216     /**
20217      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20218      * name to the request.
20219      */
20220     nocache : true,
20221
20222     /**
20223      * Load data from the configured URL, read the data object into
20224      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20225      * process that block using the passed callback.
20226      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20227      * for the request to the remote server.
20228      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20229      * object into a block of Roo.data.Records.
20230      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20231      * The function must be passed <ul>
20232      * <li>The Record block object</li>
20233      * <li>The "arg" argument from the load function</li>
20234      * <li>A boolean success indicator</li>
20235      * </ul>
20236      * @param {Object} scope The scope in which to call the callback
20237      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20238      */
20239     load : function(params, reader, callback, scope, arg){
20240         if(this.fireEvent("beforeload", this, params) !== false){
20241
20242             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20243
20244             var url = this.url;
20245             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20246             if(this.nocache){
20247                 url += "&_dc=" + (new Date().getTime());
20248             }
20249             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20250             var trans = {
20251                 id : transId,
20252                 cb : "stcCallback"+transId,
20253                 scriptId : "stcScript"+transId,
20254                 params : params,
20255                 arg : arg,
20256                 url : url,
20257                 callback : callback,
20258                 scope : scope,
20259                 reader : reader
20260             };
20261             var conn = this;
20262
20263             window[trans.cb] = function(o){
20264                 conn.handleResponse(o, trans);
20265             };
20266
20267             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20268
20269             if(this.autoAbort !== false){
20270                 this.abort();
20271             }
20272
20273             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20274
20275             var script = document.createElement("script");
20276             script.setAttribute("src", url);
20277             script.setAttribute("type", "text/javascript");
20278             script.setAttribute("id", trans.scriptId);
20279             this.head.appendChild(script);
20280
20281             this.trans = trans;
20282         }else{
20283             callback.call(scope||this, null, arg, false);
20284         }
20285     },
20286
20287     // private
20288     isLoading : function(){
20289         return this.trans ? true : false;
20290     },
20291
20292     /**
20293      * Abort the current server request.
20294      */
20295     abort : function(){
20296         if(this.isLoading()){
20297             this.destroyTrans(this.trans);
20298         }
20299     },
20300
20301     // private
20302     destroyTrans : function(trans, isLoaded){
20303         this.head.removeChild(document.getElementById(trans.scriptId));
20304         clearTimeout(trans.timeoutId);
20305         if(isLoaded){
20306             window[trans.cb] = undefined;
20307             try{
20308                 delete window[trans.cb];
20309             }catch(e){}
20310         }else{
20311             // if hasn't been loaded, wait for load to remove it to prevent script error
20312             window[trans.cb] = function(){
20313                 window[trans.cb] = undefined;
20314                 try{
20315                     delete window[trans.cb];
20316                 }catch(e){}
20317             };
20318         }
20319     },
20320
20321     // private
20322     handleResponse : function(o, trans){
20323         this.trans = false;
20324         this.destroyTrans(trans, true);
20325         var result;
20326         try {
20327             result = trans.reader.readRecords(o);
20328         }catch(e){
20329             this.fireEvent("loadexception", this, o, trans.arg, e);
20330             trans.callback.call(trans.scope||window, null, trans.arg, false);
20331             return;
20332         }
20333         this.fireEvent("load", this, o, trans.arg);
20334         trans.callback.call(trans.scope||window, result, trans.arg, true);
20335     },
20336
20337     // private
20338     handleFailure : function(trans){
20339         this.trans = false;
20340         this.destroyTrans(trans, false);
20341         this.fireEvent("loadexception", this, null, trans.arg);
20342         trans.callback.call(trans.scope||window, null, trans.arg, false);
20343     }
20344 });/*
20345  * Based on:
20346  * Ext JS Library 1.1.1
20347  * Copyright(c) 2006-2007, Ext JS, LLC.
20348  *
20349  * Originally Released Under LGPL - original licence link has changed is not relivant.
20350  *
20351  * Fork - LGPL
20352  * <script type="text/javascript">
20353  */
20354
20355 /**
20356  * @class Roo.data.JsonReader
20357  * @extends Roo.data.DataReader
20358  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20359  * based on mappings in a provided Roo.data.Record constructor.
20360  * 
20361  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20362  * in the reply previously. 
20363  * 
20364  * <p>
20365  * Example code:
20366  * <pre><code>
20367 var RecordDef = Roo.data.Record.create([
20368     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20369     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20370 ]);
20371 var myReader = new Roo.data.JsonReader({
20372     totalProperty: "results",    // The property which contains the total dataset size (optional)
20373     root: "rows",                // The property which contains an Array of row objects
20374     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20375 }, RecordDef);
20376 </code></pre>
20377  * <p>
20378  * This would consume a JSON file like this:
20379  * <pre><code>
20380 { 'results': 2, 'rows': [
20381     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20382     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20383 }
20384 </code></pre>
20385  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20386  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20387  * paged from the remote server.
20388  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20389  * @cfg {String} root name of the property which contains the Array of row objects.
20390  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20391  * @constructor
20392  * Create a new JsonReader
20393  * @param {Object} meta Metadata configuration options
20394  * @param {Object} recordType Either an Array of field definition objects,
20395  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20396  */
20397 Roo.data.JsonReader = function(meta, recordType){
20398     
20399     meta = meta || {};
20400     // set some defaults:
20401     Roo.applyIf(meta, {
20402         totalProperty: 'total',
20403         successProperty : 'success',
20404         root : 'data',
20405         id : 'id'
20406     });
20407     
20408     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20409 };
20410 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20411     
20412     /**
20413      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20414      * Used by Store query builder to append _requestMeta to params.
20415      * 
20416      */
20417     metaFromRemote : false,
20418     /**
20419      * This method is only used by a DataProxy which has retrieved data from a remote server.
20420      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20421      * @return {Object} data A data block which is used by an Roo.data.Store object as
20422      * a cache of Roo.data.Records.
20423      */
20424     read : function(response){
20425         var json = response.responseText;
20426        
20427         var o = /* eval:var:o */ eval("("+json+")");
20428         if(!o) {
20429             throw {message: "JsonReader.read: Json object not found"};
20430         }
20431         
20432         if(o.metaData){
20433             
20434             delete this.ef;
20435             this.metaFromRemote = true;
20436             this.meta = o.metaData;
20437             this.recordType = Roo.data.Record.create(o.metaData.fields);
20438             this.onMetaChange(this.meta, this.recordType, o);
20439         }
20440         return this.readRecords(o);
20441     },
20442
20443     // private function a store will implement
20444     onMetaChange : function(meta, recordType, o){
20445
20446     },
20447
20448     /**
20449          * @ignore
20450          */
20451     simpleAccess: function(obj, subsc) {
20452         return obj[subsc];
20453     },
20454
20455         /**
20456          * @ignore
20457          */
20458     getJsonAccessor: function(){
20459         var re = /[\[\.]/;
20460         return function(expr) {
20461             try {
20462                 return(re.test(expr))
20463                     ? new Function("obj", "return obj." + expr)
20464                     : function(obj){
20465                         return obj[expr];
20466                     };
20467             } catch(e){}
20468             return Roo.emptyFn;
20469         };
20470     }(),
20471
20472     /**
20473      * Create a data block containing Roo.data.Records from an XML document.
20474      * @param {Object} o An object which contains an Array of row objects in the property specified
20475      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20476      * which contains the total size of the dataset.
20477      * @return {Object} data A data block which is used by an Roo.data.Store object as
20478      * a cache of Roo.data.Records.
20479      */
20480     readRecords : function(o){
20481         /**
20482          * After any data loads, the raw JSON data is available for further custom processing.
20483          * @type Object
20484          */
20485         this.jsonData = o;
20486         var s = this.meta, Record = this.recordType,
20487             f = Record.prototype.fields, fi = f.items, fl = f.length;
20488
20489 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20490         if (!this.ef) {
20491             if(s.totalProperty) {
20492                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20493                 }
20494                 if(s.successProperty) {
20495                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20496                 }
20497                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20498                 if (s.id) {
20499                         var g = this.getJsonAccessor(s.id);
20500                         this.getId = function(rec) {
20501                                 var r = g(rec);
20502                                 return (r === undefined || r === "") ? null : r;
20503                         };
20504                 } else {
20505                         this.getId = function(){return null;};
20506                 }
20507             this.ef = [];
20508             for(var jj = 0; jj < fl; jj++){
20509                 f = fi[jj];
20510                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20511                 this.ef[jj] = this.getJsonAccessor(map);
20512             }
20513         }
20514
20515         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20516         if(s.totalProperty){
20517             var vt = parseInt(this.getTotal(o), 10);
20518             if(!isNaN(vt)){
20519                 totalRecords = vt;
20520             }
20521         }
20522         if(s.successProperty){
20523             var vs = this.getSuccess(o);
20524             if(vs === false || vs === 'false'){
20525                 success = false;
20526             }
20527         }
20528         var records = [];
20529             for(var i = 0; i < c; i++){
20530                     var n = root[i];
20531                 var values = {};
20532                 var id = this.getId(n);
20533                 for(var j = 0; j < fl; j++){
20534                     f = fi[j];
20535                 var v = this.ef[j](n);
20536                 if (!f.convert) {
20537                     Roo.log('missing convert for ' + f.name);
20538                     Roo.log(f);
20539                     continue;
20540                 }
20541                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20542                 }
20543                 var record = new Record(values, id);
20544                 record.json = n;
20545                 records[i] = record;
20546             }
20547             return {
20548                 success : success,
20549                 records : records,
20550                 totalRecords : totalRecords
20551             };
20552     }
20553 });/*
20554  * Based on:
20555  * Ext JS Library 1.1.1
20556  * Copyright(c) 2006-2007, Ext JS, LLC.
20557  *
20558  * Originally Released Under LGPL - original licence link has changed is not relivant.
20559  *
20560  * Fork - LGPL
20561  * <script type="text/javascript">
20562  */
20563
20564 /**
20565  * @class Roo.data.XmlReader
20566  * @extends Roo.data.DataReader
20567  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20568  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20569  * <p>
20570  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20571  * header in the HTTP response must be set to "text/xml".</em>
20572  * <p>
20573  * Example code:
20574  * <pre><code>
20575 var RecordDef = Roo.data.Record.create([
20576    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20577    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20578 ]);
20579 var myReader = new Roo.data.XmlReader({
20580    totalRecords: "results", // The element which contains the total dataset size (optional)
20581    record: "row",           // The repeated element which contains row information
20582    id: "id"                 // The element within the row that provides an ID for the record (optional)
20583 }, RecordDef);
20584 </code></pre>
20585  * <p>
20586  * This would consume an XML file like this:
20587  * <pre><code>
20588 &lt;?xml?>
20589 &lt;dataset>
20590  &lt;results>2&lt;/results>
20591  &lt;row>
20592    &lt;id>1&lt;/id>
20593    &lt;name>Bill&lt;/name>
20594    &lt;occupation>Gardener&lt;/occupation>
20595  &lt;/row>
20596  &lt;row>
20597    &lt;id>2&lt;/id>
20598    &lt;name>Ben&lt;/name>
20599    &lt;occupation>Horticulturalist&lt;/occupation>
20600  &lt;/row>
20601 &lt;/dataset>
20602 </code></pre>
20603  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20604  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20605  * paged from the remote server.
20606  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20607  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20608  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20609  * a record identifier value.
20610  * @constructor
20611  * Create a new XmlReader
20612  * @param {Object} meta Metadata configuration options
20613  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20614  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20615  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20616  */
20617 Roo.data.XmlReader = function(meta, recordType){
20618     meta = meta || {};
20619     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20620 };
20621 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20622     /**
20623      * This method is only used by a DataProxy which has retrieved data from a remote server.
20624          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20625          * to contain a method called 'responseXML' that returns an XML document object.
20626      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20627      * a cache of Roo.data.Records.
20628      */
20629     read : function(response){
20630         var doc = response.responseXML;
20631         if(!doc) {
20632             throw {message: "XmlReader.read: XML Document not available"};
20633         }
20634         return this.readRecords(doc);
20635     },
20636
20637     /**
20638      * Create a data block containing Roo.data.Records from an XML document.
20639          * @param {Object} doc A parsed XML document.
20640      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20641      * a cache of Roo.data.Records.
20642      */
20643     readRecords : function(doc){
20644         /**
20645          * After any data loads/reads, the raw XML Document is available for further custom processing.
20646          * @type XMLDocument
20647          */
20648         this.xmlData = doc;
20649         var root = doc.documentElement || doc;
20650         var q = Roo.DomQuery;
20651         var recordType = this.recordType, fields = recordType.prototype.fields;
20652         var sid = this.meta.id;
20653         var totalRecords = 0, success = true;
20654         if(this.meta.totalRecords){
20655             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20656         }
20657         
20658         if(this.meta.success){
20659             var sv = q.selectValue(this.meta.success, root, true);
20660             success = sv !== false && sv !== 'false';
20661         }
20662         var records = [];
20663         var ns = q.select(this.meta.record, root);
20664         for(var i = 0, len = ns.length; i < len; i++) {
20665                 var n = ns[i];
20666                 var values = {};
20667                 var id = sid ? q.selectValue(sid, n) : undefined;
20668                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20669                     var f = fields.items[j];
20670                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20671                     v = f.convert(v);
20672                     values[f.name] = v;
20673                 }
20674                 var record = new recordType(values, id);
20675                 record.node = n;
20676                 records[records.length] = record;
20677             }
20678
20679             return {
20680                 success : success,
20681                 records : records,
20682                 totalRecords : totalRecords || records.length
20683             };
20684     }
20685 });/*
20686  * Based on:
20687  * Ext JS Library 1.1.1
20688  * Copyright(c) 2006-2007, Ext JS, LLC.
20689  *
20690  * Originally Released Under LGPL - original licence link has changed is not relivant.
20691  *
20692  * Fork - LGPL
20693  * <script type="text/javascript">
20694  */
20695
20696 /**
20697  * @class Roo.data.ArrayReader
20698  * @extends Roo.data.DataReader
20699  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20700  * Each element of that Array represents a row of data fields. The
20701  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20702  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20703  * <p>
20704  * Example code:.
20705  * <pre><code>
20706 var RecordDef = Roo.data.Record.create([
20707     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20708     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20709 ]);
20710 var myReader = new Roo.data.ArrayReader({
20711     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20712 }, RecordDef);
20713 </code></pre>
20714  * <p>
20715  * This would consume an Array like this:
20716  * <pre><code>
20717 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20718   </code></pre>
20719  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20720  * @constructor
20721  * Create a new JsonReader
20722  * @param {Object} meta Metadata configuration options.
20723  * @param {Object} recordType Either an Array of field definition objects
20724  * as specified to {@link Roo.data.Record#create},
20725  * or an {@link Roo.data.Record} object
20726  * created using {@link Roo.data.Record#create}.
20727  */
20728 Roo.data.ArrayReader = function(meta, recordType){
20729     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20730 };
20731
20732 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20733     /**
20734      * Create a data block containing Roo.data.Records from an XML document.
20735      * @param {Object} o An Array of row objects which represents the dataset.
20736      * @return {Object} data A data block which is used by an Roo.data.Store object as
20737      * a cache of Roo.data.Records.
20738      */
20739     readRecords : function(o){
20740         var sid = this.meta ? this.meta.id : null;
20741         var recordType = this.recordType, fields = recordType.prototype.fields;
20742         var records = [];
20743         var root = o;
20744             for(var i = 0; i < root.length; i++){
20745                     var n = root[i];
20746                 var values = {};
20747                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20748                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20749                 var f = fields.items[j];
20750                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20751                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20752                 v = f.convert(v);
20753                 values[f.name] = v;
20754             }
20755                 var record = new recordType(values, id);
20756                 record.json = n;
20757                 records[records.length] = record;
20758             }
20759             return {
20760                 records : records,
20761                 totalRecords : records.length
20762             };
20763     }
20764 });/*
20765  * Based on:
20766  * Ext JS Library 1.1.1
20767  * Copyright(c) 2006-2007, Ext JS, LLC.
20768  *
20769  * Originally Released Under LGPL - original licence link has changed is not relivant.
20770  *
20771  * Fork - LGPL
20772  * <script type="text/javascript">
20773  */
20774
20775
20776 /**
20777  * @class Roo.data.Tree
20778  * @extends Roo.util.Observable
20779  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20780  * in the tree have most standard DOM functionality.
20781  * @constructor
20782  * @param {Node} root (optional) The root node
20783  */
20784 Roo.data.Tree = function(root){
20785    this.nodeHash = {};
20786    /**
20787     * The root node for this tree
20788     * @type Node
20789     */
20790    this.root = null;
20791    if(root){
20792        this.setRootNode(root);
20793    }
20794    this.addEvents({
20795        /**
20796         * @event append
20797         * Fires when a new child node is appended to a node in this tree.
20798         * @param {Tree} tree The owner tree
20799         * @param {Node} parent The parent node
20800         * @param {Node} node The newly appended node
20801         * @param {Number} index The index of the newly appended node
20802         */
20803        "append" : true,
20804        /**
20805         * @event remove
20806         * Fires when a child node is removed from a node in this tree.
20807         * @param {Tree} tree The owner tree
20808         * @param {Node} parent The parent node
20809         * @param {Node} node The child node removed
20810         */
20811        "remove" : true,
20812        /**
20813         * @event move
20814         * Fires when a node is moved to a new location in the tree
20815         * @param {Tree} tree The owner tree
20816         * @param {Node} node The node moved
20817         * @param {Node} oldParent The old parent of this node
20818         * @param {Node} newParent The new parent of this node
20819         * @param {Number} index The index it was moved to
20820         */
20821        "move" : true,
20822        /**
20823         * @event insert
20824         * Fires when a new child node is inserted in a node in this tree.
20825         * @param {Tree} tree The owner tree
20826         * @param {Node} parent The parent node
20827         * @param {Node} node The child node inserted
20828         * @param {Node} refNode The child node the node was inserted before
20829         */
20830        "insert" : true,
20831        /**
20832         * @event beforeappend
20833         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20834         * @param {Tree} tree The owner tree
20835         * @param {Node} parent The parent node
20836         * @param {Node} node The child node to be appended
20837         */
20838        "beforeappend" : true,
20839        /**
20840         * @event beforeremove
20841         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20842         * @param {Tree} tree The owner tree
20843         * @param {Node} parent The parent node
20844         * @param {Node} node The child node to be removed
20845         */
20846        "beforeremove" : true,
20847        /**
20848         * @event beforemove
20849         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20850         * @param {Tree} tree The owner tree
20851         * @param {Node} node The node being moved
20852         * @param {Node} oldParent The parent of the node
20853         * @param {Node} newParent The new parent the node is moving to
20854         * @param {Number} index The index it is being moved to
20855         */
20856        "beforemove" : true,
20857        /**
20858         * @event beforeinsert
20859         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20860         * @param {Tree} tree The owner tree
20861         * @param {Node} parent The parent node
20862         * @param {Node} node The child node to be inserted
20863         * @param {Node} refNode The child node the node is being inserted before
20864         */
20865        "beforeinsert" : true
20866    });
20867
20868     Roo.data.Tree.superclass.constructor.call(this);
20869 };
20870
20871 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20872     pathSeparator: "/",
20873
20874     proxyNodeEvent : function(){
20875         return this.fireEvent.apply(this, arguments);
20876     },
20877
20878     /**
20879      * Returns the root node for this tree.
20880      * @return {Node}
20881      */
20882     getRootNode : function(){
20883         return this.root;
20884     },
20885
20886     /**
20887      * Sets the root node for this tree.
20888      * @param {Node} node
20889      * @return {Node}
20890      */
20891     setRootNode : function(node){
20892         this.root = node;
20893         node.ownerTree = this;
20894         node.isRoot = true;
20895         this.registerNode(node);
20896         return node;
20897     },
20898
20899     /**
20900      * Gets a node in this tree by its id.
20901      * @param {String} id
20902      * @return {Node}
20903      */
20904     getNodeById : function(id){
20905         return this.nodeHash[id];
20906     },
20907
20908     registerNode : function(node){
20909         this.nodeHash[node.id] = node;
20910     },
20911
20912     unregisterNode : function(node){
20913         delete this.nodeHash[node.id];
20914     },
20915
20916     toString : function(){
20917         return "[Tree"+(this.id?" "+this.id:"")+"]";
20918     }
20919 });
20920
20921 /**
20922  * @class Roo.data.Node
20923  * @extends Roo.util.Observable
20924  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20925  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20926  * @constructor
20927  * @param {Object} attributes The attributes/config for the node
20928  */
20929 Roo.data.Node = function(attributes){
20930     /**
20931      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20932      * @type {Object}
20933      */
20934     this.attributes = attributes || {};
20935     this.leaf = this.attributes.leaf;
20936     /**
20937      * The node id. @type String
20938      */
20939     this.id = this.attributes.id;
20940     if(!this.id){
20941         this.id = Roo.id(null, "ynode-");
20942         this.attributes.id = this.id;
20943     }
20944     /**
20945      * All child nodes of this node. @type Array
20946      */
20947     this.childNodes = [];
20948     if(!this.childNodes.indexOf){ // indexOf is a must
20949         this.childNodes.indexOf = function(o){
20950             for(var i = 0, len = this.length; i < len; i++){
20951                 if(this[i] == o) {
20952                     return i;
20953                 }
20954             }
20955             return -1;
20956         };
20957     }
20958     /**
20959      * The parent node for this node. @type Node
20960      */
20961     this.parentNode = null;
20962     /**
20963      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20964      */
20965     this.firstChild = null;
20966     /**
20967      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20968      */
20969     this.lastChild = null;
20970     /**
20971      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20972      */
20973     this.previousSibling = null;
20974     /**
20975      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20976      */
20977     this.nextSibling = null;
20978
20979     this.addEvents({
20980        /**
20981         * @event append
20982         * Fires when a new child node is appended
20983         * @param {Tree} tree The owner tree
20984         * @param {Node} this This node
20985         * @param {Node} node The newly appended node
20986         * @param {Number} index The index of the newly appended node
20987         */
20988        "append" : true,
20989        /**
20990         * @event remove
20991         * Fires when a child node is removed
20992         * @param {Tree} tree The owner tree
20993         * @param {Node} this This node
20994         * @param {Node} node The removed node
20995         */
20996        "remove" : true,
20997        /**
20998         * @event move
20999         * Fires when this node is moved to a new location in the tree
21000         * @param {Tree} tree The owner tree
21001         * @param {Node} this This node
21002         * @param {Node} oldParent The old parent of this node
21003         * @param {Node} newParent The new parent of this node
21004         * @param {Number} index The index it was moved to
21005         */
21006        "move" : true,
21007        /**
21008         * @event insert
21009         * Fires when a new child node is inserted.
21010         * @param {Tree} tree The owner tree
21011         * @param {Node} this This node
21012         * @param {Node} node The child node inserted
21013         * @param {Node} refNode The child node the node was inserted before
21014         */
21015        "insert" : true,
21016        /**
21017         * @event beforeappend
21018         * Fires before a new child is appended, return false to cancel the append.
21019         * @param {Tree} tree The owner tree
21020         * @param {Node} this This node
21021         * @param {Node} node The child node to be appended
21022         */
21023        "beforeappend" : true,
21024        /**
21025         * @event beforeremove
21026         * Fires before a child is removed, return false to cancel the remove.
21027         * @param {Tree} tree The owner tree
21028         * @param {Node} this This node
21029         * @param {Node} node The child node to be removed
21030         */
21031        "beforeremove" : true,
21032        /**
21033         * @event beforemove
21034         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21035         * @param {Tree} tree The owner tree
21036         * @param {Node} this This node
21037         * @param {Node} oldParent The parent of this node
21038         * @param {Node} newParent The new parent this node is moving to
21039         * @param {Number} index The index it is being moved to
21040         */
21041        "beforemove" : true,
21042        /**
21043         * @event beforeinsert
21044         * Fires before a new child is inserted, return false to cancel the insert.
21045         * @param {Tree} tree The owner tree
21046         * @param {Node} this This node
21047         * @param {Node} node The child node to be inserted
21048         * @param {Node} refNode The child node the node is being inserted before
21049         */
21050        "beforeinsert" : true
21051    });
21052     this.listeners = this.attributes.listeners;
21053     Roo.data.Node.superclass.constructor.call(this);
21054 };
21055
21056 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21057     fireEvent : function(evtName){
21058         // first do standard event for this node
21059         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21060             return false;
21061         }
21062         // then bubble it up to the tree if the event wasn't cancelled
21063         var ot = this.getOwnerTree();
21064         if(ot){
21065             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21066                 return false;
21067             }
21068         }
21069         return true;
21070     },
21071
21072     /**
21073      * Returns true if this node is a leaf
21074      * @return {Boolean}
21075      */
21076     isLeaf : function(){
21077         return this.leaf === true;
21078     },
21079
21080     // private
21081     setFirstChild : function(node){
21082         this.firstChild = node;
21083     },
21084
21085     //private
21086     setLastChild : function(node){
21087         this.lastChild = node;
21088     },
21089
21090
21091     /**
21092      * Returns true if this node is the last child of its parent
21093      * @return {Boolean}
21094      */
21095     isLast : function(){
21096        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21097     },
21098
21099     /**
21100      * Returns true if this node is the first child of its parent
21101      * @return {Boolean}
21102      */
21103     isFirst : function(){
21104        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21105     },
21106
21107     hasChildNodes : function(){
21108         return !this.isLeaf() && this.childNodes.length > 0;
21109     },
21110
21111     /**
21112      * Insert node(s) as the last child node of this node.
21113      * @param {Node/Array} node The node or Array of nodes to append
21114      * @return {Node} The appended node if single append, or null if an array was passed
21115      */
21116     appendChild : function(node){
21117         var multi = false;
21118         if(node instanceof Array){
21119             multi = node;
21120         }else if(arguments.length > 1){
21121             multi = arguments;
21122         }
21123         // if passed an array or multiple args do them one by one
21124         if(multi){
21125             for(var i = 0, len = multi.length; i < len; i++) {
21126                 this.appendChild(multi[i]);
21127             }
21128         }else{
21129             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21130                 return false;
21131             }
21132             var index = this.childNodes.length;
21133             var oldParent = node.parentNode;
21134             // it's a move, make sure we move it cleanly
21135             if(oldParent){
21136                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21137                     return false;
21138                 }
21139                 oldParent.removeChild(node);
21140             }
21141             index = this.childNodes.length;
21142             if(index == 0){
21143                 this.setFirstChild(node);
21144             }
21145             this.childNodes.push(node);
21146             node.parentNode = this;
21147             var ps = this.childNodes[index-1];
21148             if(ps){
21149                 node.previousSibling = ps;
21150                 ps.nextSibling = node;
21151             }else{
21152                 node.previousSibling = null;
21153             }
21154             node.nextSibling = null;
21155             this.setLastChild(node);
21156             node.setOwnerTree(this.getOwnerTree());
21157             this.fireEvent("append", this.ownerTree, this, node, index);
21158             if(oldParent){
21159                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21160             }
21161             return node;
21162         }
21163     },
21164
21165     /**
21166      * Removes a child node from this node.
21167      * @param {Node} node The node to remove
21168      * @return {Node} The removed node
21169      */
21170     removeChild : function(node){
21171         var index = this.childNodes.indexOf(node);
21172         if(index == -1){
21173             return false;
21174         }
21175         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21176             return false;
21177         }
21178
21179         // remove it from childNodes collection
21180         this.childNodes.splice(index, 1);
21181
21182         // update siblings
21183         if(node.previousSibling){
21184             node.previousSibling.nextSibling = node.nextSibling;
21185         }
21186         if(node.nextSibling){
21187             node.nextSibling.previousSibling = node.previousSibling;
21188         }
21189
21190         // update child refs
21191         if(this.firstChild == node){
21192             this.setFirstChild(node.nextSibling);
21193         }
21194         if(this.lastChild == node){
21195             this.setLastChild(node.previousSibling);
21196         }
21197
21198         node.setOwnerTree(null);
21199         // clear any references from the node
21200         node.parentNode = null;
21201         node.previousSibling = null;
21202         node.nextSibling = null;
21203         this.fireEvent("remove", this.ownerTree, this, node);
21204         return node;
21205     },
21206
21207     /**
21208      * Inserts the first node before the second node in this nodes childNodes collection.
21209      * @param {Node} node The node to insert
21210      * @param {Node} refNode The node to insert before (if null the node is appended)
21211      * @return {Node} The inserted node
21212      */
21213     insertBefore : function(node, refNode){
21214         if(!refNode){ // like standard Dom, refNode can be null for append
21215             return this.appendChild(node);
21216         }
21217         // nothing to do
21218         if(node == refNode){
21219             return false;
21220         }
21221
21222         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21223             return false;
21224         }
21225         var index = this.childNodes.indexOf(refNode);
21226         var oldParent = node.parentNode;
21227         var refIndex = index;
21228
21229         // when moving internally, indexes will change after remove
21230         if(oldParent == this && this.childNodes.indexOf(node) < index){
21231             refIndex--;
21232         }
21233
21234         // it's a move, make sure we move it cleanly
21235         if(oldParent){
21236             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21237                 return false;
21238             }
21239             oldParent.removeChild(node);
21240         }
21241         if(refIndex == 0){
21242             this.setFirstChild(node);
21243         }
21244         this.childNodes.splice(refIndex, 0, node);
21245         node.parentNode = this;
21246         var ps = this.childNodes[refIndex-1];
21247         if(ps){
21248             node.previousSibling = ps;
21249             ps.nextSibling = node;
21250         }else{
21251             node.previousSibling = null;
21252         }
21253         node.nextSibling = refNode;
21254         refNode.previousSibling = node;
21255         node.setOwnerTree(this.getOwnerTree());
21256         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21257         if(oldParent){
21258             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21259         }
21260         return node;
21261     },
21262
21263     /**
21264      * Returns the child node at the specified index.
21265      * @param {Number} index
21266      * @return {Node}
21267      */
21268     item : function(index){
21269         return this.childNodes[index];
21270     },
21271
21272     /**
21273      * Replaces one child node in this node with another.
21274      * @param {Node} newChild The replacement node
21275      * @param {Node} oldChild The node to replace
21276      * @return {Node} The replaced node
21277      */
21278     replaceChild : function(newChild, oldChild){
21279         this.insertBefore(newChild, oldChild);
21280         this.removeChild(oldChild);
21281         return oldChild;
21282     },
21283
21284     /**
21285      * Returns the index of a child node
21286      * @param {Node} node
21287      * @return {Number} The index of the node or -1 if it was not found
21288      */
21289     indexOf : function(child){
21290         return this.childNodes.indexOf(child);
21291     },
21292
21293     /**
21294      * Returns the tree this node is in.
21295      * @return {Tree}
21296      */
21297     getOwnerTree : function(){
21298         // if it doesn't have one, look for one
21299         if(!this.ownerTree){
21300             var p = this;
21301             while(p){
21302                 if(p.ownerTree){
21303                     this.ownerTree = p.ownerTree;
21304                     break;
21305                 }
21306                 p = p.parentNode;
21307             }
21308         }
21309         return this.ownerTree;
21310     },
21311
21312     /**
21313      * Returns depth of this node (the root node has a depth of 0)
21314      * @return {Number}
21315      */
21316     getDepth : function(){
21317         var depth = 0;
21318         var p = this;
21319         while(p.parentNode){
21320             ++depth;
21321             p = p.parentNode;
21322         }
21323         return depth;
21324     },
21325
21326     // private
21327     setOwnerTree : function(tree){
21328         // if it's move, we need to update everyone
21329         if(tree != this.ownerTree){
21330             if(this.ownerTree){
21331                 this.ownerTree.unregisterNode(this);
21332             }
21333             this.ownerTree = tree;
21334             var cs = this.childNodes;
21335             for(var i = 0, len = cs.length; i < len; i++) {
21336                 cs[i].setOwnerTree(tree);
21337             }
21338             if(tree){
21339                 tree.registerNode(this);
21340             }
21341         }
21342     },
21343
21344     /**
21345      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21346      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21347      * @return {String} The path
21348      */
21349     getPath : function(attr){
21350         attr = attr || "id";
21351         var p = this.parentNode;
21352         var b = [this.attributes[attr]];
21353         while(p){
21354             b.unshift(p.attributes[attr]);
21355             p = p.parentNode;
21356         }
21357         var sep = this.getOwnerTree().pathSeparator;
21358         return sep + b.join(sep);
21359     },
21360
21361     /**
21362      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21363      * function call will be the scope provided or the current node. The arguments to the function
21364      * will be the args provided or the current node. If the function returns false at any point,
21365      * the bubble is stopped.
21366      * @param {Function} fn The function to call
21367      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21368      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21369      */
21370     bubble : function(fn, scope, args){
21371         var p = this;
21372         while(p){
21373             if(fn.call(scope || p, args || p) === false){
21374                 break;
21375             }
21376             p = p.parentNode;
21377         }
21378     },
21379
21380     /**
21381      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21382      * function call will be the scope provided or the current node. The arguments to the function
21383      * will be the args provided or the current node. If the function returns false at any point,
21384      * the cascade is stopped on that branch.
21385      * @param {Function} fn The function to call
21386      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21387      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21388      */
21389     cascade : function(fn, scope, args){
21390         if(fn.call(scope || this, args || this) !== false){
21391             var cs = this.childNodes;
21392             for(var i = 0, len = cs.length; i < len; i++) {
21393                 cs[i].cascade(fn, scope, args);
21394             }
21395         }
21396     },
21397
21398     /**
21399      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21400      * function call will be the scope provided or the current node. The arguments to the function
21401      * will be the args provided or the current node. If the function returns false at any point,
21402      * the iteration stops.
21403      * @param {Function} fn The function to call
21404      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21405      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21406      */
21407     eachChild : function(fn, scope, args){
21408         var cs = this.childNodes;
21409         for(var i = 0, len = cs.length; i < len; i++) {
21410                 if(fn.call(scope || this, args || cs[i]) === false){
21411                     break;
21412                 }
21413         }
21414     },
21415
21416     /**
21417      * Finds the first child that has the attribute with the specified value.
21418      * @param {String} attribute The attribute name
21419      * @param {Mixed} value The value to search for
21420      * @return {Node} The found child or null if none was found
21421      */
21422     findChild : function(attribute, value){
21423         var cs = this.childNodes;
21424         for(var i = 0, len = cs.length; i < len; i++) {
21425                 if(cs[i].attributes[attribute] == value){
21426                     return cs[i];
21427                 }
21428         }
21429         return null;
21430     },
21431
21432     /**
21433      * Finds the first child by a custom function. The child matches if the function passed
21434      * returns true.
21435      * @param {Function} fn
21436      * @param {Object} scope (optional)
21437      * @return {Node} The found child or null if none was found
21438      */
21439     findChildBy : function(fn, scope){
21440         var cs = this.childNodes;
21441         for(var i = 0, len = cs.length; i < len; i++) {
21442                 if(fn.call(scope||cs[i], cs[i]) === true){
21443                     return cs[i];
21444                 }
21445         }
21446         return null;
21447     },
21448
21449     /**
21450      * Sorts this nodes children using the supplied sort function
21451      * @param {Function} fn
21452      * @param {Object} scope (optional)
21453      */
21454     sort : function(fn, scope){
21455         var cs = this.childNodes;
21456         var len = cs.length;
21457         if(len > 0){
21458             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21459             cs.sort(sortFn);
21460             for(var i = 0; i < len; i++){
21461                 var n = cs[i];
21462                 n.previousSibling = cs[i-1];
21463                 n.nextSibling = cs[i+1];
21464                 if(i == 0){
21465                     this.setFirstChild(n);
21466                 }
21467                 if(i == len-1){
21468                     this.setLastChild(n);
21469                 }
21470             }
21471         }
21472     },
21473
21474     /**
21475      * Returns true if this node is an ancestor (at any point) of the passed node.
21476      * @param {Node} node
21477      * @return {Boolean}
21478      */
21479     contains : function(node){
21480         return node.isAncestor(this);
21481     },
21482
21483     /**
21484      * Returns true if the passed node is an ancestor (at any point) of this node.
21485      * @param {Node} node
21486      * @return {Boolean}
21487      */
21488     isAncestor : function(node){
21489         var p = this.parentNode;
21490         while(p){
21491             if(p == node){
21492                 return true;
21493             }
21494             p = p.parentNode;
21495         }
21496         return false;
21497     },
21498
21499     toString : function(){
21500         return "[Node"+(this.id?" "+this.id:"")+"]";
21501     }
21502 });/*
21503  * Based on:
21504  * Ext JS Library 1.1.1
21505  * Copyright(c) 2006-2007, Ext JS, LLC.
21506  *
21507  * Originally Released Under LGPL - original licence link has changed is not relivant.
21508  *
21509  * Fork - LGPL
21510  * <script type="text/javascript">
21511  */
21512  
21513
21514 /**
21515  * @class Roo.ComponentMgr
21516  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21517  * @singleton
21518  */
21519 Roo.ComponentMgr = function(){
21520     var all = new Roo.util.MixedCollection();
21521
21522     return {
21523         /**
21524          * Registers a component.
21525          * @param {Roo.Component} c The component
21526          */
21527         register : function(c){
21528             all.add(c);
21529         },
21530
21531         /**
21532          * Unregisters a component.
21533          * @param {Roo.Component} c The component
21534          */
21535         unregister : function(c){
21536             all.remove(c);
21537         },
21538
21539         /**
21540          * Returns a component by id
21541          * @param {String} id The component id
21542          */
21543         get : function(id){
21544             return all.get(id);
21545         },
21546
21547         /**
21548          * Registers a function that will be called when a specified component is added to ComponentMgr
21549          * @param {String} id The component id
21550          * @param {Funtction} fn The callback function
21551          * @param {Object} scope The scope of the callback
21552          */
21553         onAvailable : function(id, fn, scope){
21554             all.on("add", function(index, o){
21555                 if(o.id == id){
21556                     fn.call(scope || o, o);
21557                     all.un("add", fn, scope);
21558                 }
21559             });
21560         }
21561     };
21562 }();/*
21563  * Based on:
21564  * Ext JS Library 1.1.1
21565  * Copyright(c) 2006-2007, Ext JS, LLC.
21566  *
21567  * Originally Released Under LGPL - original licence link has changed is not relivant.
21568  *
21569  * Fork - LGPL
21570  * <script type="text/javascript">
21571  */
21572  
21573 /**
21574  * @class Roo.Component
21575  * @extends Roo.util.Observable
21576  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21577  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21578  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21579  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21580  * All visual components (widgets) that require rendering into a layout should subclass Component.
21581  * @constructor
21582  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21583  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
21584  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21585  */
21586 Roo.Component = function(config){
21587     config = config || {};
21588     if(config.tagName || config.dom || typeof config == "string"){ // element object
21589         config = {el: config, id: config.id || config};
21590     }
21591     this.initialConfig = config;
21592
21593     Roo.apply(this, config);
21594     this.addEvents({
21595         /**
21596          * @event disable
21597          * Fires after the component is disabled.
21598              * @param {Roo.Component} this
21599              */
21600         disable : true,
21601         /**
21602          * @event enable
21603          * Fires after the component is enabled.
21604              * @param {Roo.Component} this
21605              */
21606         enable : true,
21607         /**
21608          * @event beforeshow
21609          * Fires before the component is shown.  Return false to stop the show.
21610              * @param {Roo.Component} this
21611              */
21612         beforeshow : true,
21613         /**
21614          * @event show
21615          * Fires after the component is shown.
21616              * @param {Roo.Component} this
21617              */
21618         show : true,
21619         /**
21620          * @event beforehide
21621          * Fires before the component is hidden. Return false to stop the hide.
21622              * @param {Roo.Component} this
21623              */
21624         beforehide : true,
21625         /**
21626          * @event hide
21627          * Fires after the component is hidden.
21628              * @param {Roo.Component} this
21629              */
21630         hide : true,
21631         /**
21632          * @event beforerender
21633          * Fires before the component is rendered. Return false to stop the render.
21634              * @param {Roo.Component} this
21635              */
21636         beforerender : true,
21637         /**
21638          * @event render
21639          * Fires after the component is rendered.
21640              * @param {Roo.Component} this
21641              */
21642         render : true,
21643         /**
21644          * @event beforedestroy
21645          * Fires before the component is destroyed. Return false to stop the destroy.
21646              * @param {Roo.Component} this
21647              */
21648         beforedestroy : true,
21649         /**
21650          * @event destroy
21651          * Fires after the component is destroyed.
21652              * @param {Roo.Component} this
21653              */
21654         destroy : true
21655     });
21656     if(!this.id){
21657         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21658     }
21659     Roo.ComponentMgr.register(this);
21660     Roo.Component.superclass.constructor.call(this);
21661     this.initComponent();
21662     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21663         this.render(this.renderTo);
21664         delete this.renderTo;
21665     }
21666 };
21667
21668 // private
21669 Roo.Component.AUTO_ID = 1000;
21670
21671 Roo.extend(Roo.Component, Roo.util.Observable, {
21672     /**
21673      * @property {Boolean} hidden
21674      * true if this component is hidden. Read-only.
21675      */
21676     hidden : false,
21677     /**
21678      * true if this component is disabled. Read-only.
21679      */
21680     disabled : false,
21681     /**
21682      * true if this component has been rendered. Read-only.
21683      */
21684     rendered : false,
21685     
21686     /** @cfg {String} disableClass
21687      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21688      */
21689     disabledClass : "x-item-disabled",
21690         /** @cfg {Boolean} allowDomMove
21691          * Whether the component can move the Dom node when rendering (defaults to true).
21692          */
21693     allowDomMove : true,
21694     /** @cfg {String} hideMode
21695      * How this component should hidden. Supported values are
21696      * "visibility" (css visibility), "offsets" (negative offset position) and
21697      * "display" (css display) - defaults to "display".
21698      */
21699     hideMode: 'display',
21700
21701     // private
21702     ctype : "Roo.Component",
21703
21704     /** @cfg {String} actionMode 
21705      * which property holds the element that used for  hide() / show() / disable() / enable()
21706      * default is 'el' 
21707      */
21708     actionMode : "el",
21709
21710     // private
21711     getActionEl : function(){
21712         return this[this.actionMode];
21713     },
21714
21715     initComponent : Roo.emptyFn,
21716     /**
21717      * If this is a lazy rendering component, render it to its container element.
21718      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
21719      */
21720     render : function(container, position){
21721         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21722             if(!container && this.el){
21723                 this.el = Roo.get(this.el);
21724                 container = this.el.dom.parentNode;
21725                 this.allowDomMove = false;
21726             }
21727             this.container = Roo.get(container);
21728             this.rendered = true;
21729             if(position !== undefined){
21730                 if(typeof position == 'number'){
21731                     position = this.container.dom.childNodes[position];
21732                 }else{
21733                     position = Roo.getDom(position);
21734                 }
21735             }
21736             this.onRender(this.container, position || null);
21737             if(this.cls){
21738                 this.el.addClass(this.cls);
21739                 delete this.cls;
21740             }
21741             if(this.style){
21742                 this.el.applyStyles(this.style);
21743                 delete this.style;
21744             }
21745             this.fireEvent("render", this);
21746             this.afterRender(this.container);
21747             if(this.hidden){
21748                 this.hide();
21749             }
21750             if(this.disabled){
21751                 this.disable();
21752             }
21753         }
21754         return this;
21755     },
21756
21757     // private
21758     // default function is not really useful
21759     onRender : function(ct, position){
21760         if(this.el){
21761             this.el = Roo.get(this.el);
21762             if(this.allowDomMove !== false){
21763                 ct.dom.insertBefore(this.el.dom, position);
21764             }
21765         }
21766     },
21767
21768     // private
21769     getAutoCreate : function(){
21770         var cfg = typeof this.autoCreate == "object" ?
21771                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21772         if(this.id && !cfg.id){
21773             cfg.id = this.id;
21774         }
21775         return cfg;
21776     },
21777
21778     // private
21779     afterRender : Roo.emptyFn,
21780
21781     /**
21782      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21783      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21784      */
21785     destroy : function(){
21786         if(this.fireEvent("beforedestroy", this) !== false){
21787             this.purgeListeners();
21788             this.beforeDestroy();
21789             if(this.rendered){
21790                 this.el.removeAllListeners();
21791                 this.el.remove();
21792                 if(this.actionMode == "container"){
21793                     this.container.remove();
21794                 }
21795             }
21796             this.onDestroy();
21797             Roo.ComponentMgr.unregister(this);
21798             this.fireEvent("destroy", this);
21799         }
21800     },
21801
21802         // private
21803     beforeDestroy : function(){
21804
21805     },
21806
21807         // private
21808         onDestroy : function(){
21809
21810     },
21811
21812     /**
21813      * Returns the underlying {@link Roo.Element}.
21814      * @return {Roo.Element} The element
21815      */
21816     getEl : function(){
21817         return this.el;
21818     },
21819
21820     /**
21821      * Returns the id of this component.
21822      * @return {String}
21823      */
21824     getId : function(){
21825         return this.id;
21826     },
21827
21828     /**
21829      * Try to focus this component.
21830      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21831      * @return {Roo.Component} this
21832      */
21833     focus : function(selectText){
21834         if(this.rendered){
21835             this.el.focus();
21836             if(selectText === true){
21837                 this.el.dom.select();
21838             }
21839         }
21840         return this;
21841     },
21842
21843     // private
21844     blur : function(){
21845         if(this.rendered){
21846             this.el.blur();
21847         }
21848         return this;
21849     },
21850
21851     /**
21852      * Disable this component.
21853      * @return {Roo.Component} this
21854      */
21855     disable : function(){
21856         if(this.rendered){
21857             this.onDisable();
21858         }
21859         this.disabled = true;
21860         this.fireEvent("disable", this);
21861         return this;
21862     },
21863
21864         // private
21865     onDisable : function(){
21866         this.getActionEl().addClass(this.disabledClass);
21867         this.el.dom.disabled = true;
21868     },
21869
21870     /**
21871      * Enable this component.
21872      * @return {Roo.Component} this
21873      */
21874     enable : function(){
21875         if(this.rendered){
21876             this.onEnable();
21877         }
21878         this.disabled = false;
21879         this.fireEvent("enable", this);
21880         return this;
21881     },
21882
21883         // private
21884     onEnable : function(){
21885         this.getActionEl().removeClass(this.disabledClass);
21886         this.el.dom.disabled = false;
21887     },
21888
21889     /**
21890      * Convenience function for setting disabled/enabled by boolean.
21891      * @param {Boolean} disabled
21892      */
21893     setDisabled : function(disabled){
21894         this[disabled ? "disable" : "enable"]();
21895     },
21896
21897     /**
21898      * Show this component.
21899      * @return {Roo.Component} this
21900      */
21901     show: function(){
21902         if(this.fireEvent("beforeshow", this) !== false){
21903             this.hidden = false;
21904             if(this.rendered){
21905                 this.onShow();
21906             }
21907             this.fireEvent("show", this);
21908         }
21909         return this;
21910     },
21911
21912     // private
21913     onShow : function(){
21914         var ae = this.getActionEl();
21915         if(this.hideMode == 'visibility'){
21916             ae.dom.style.visibility = "visible";
21917         }else if(this.hideMode == 'offsets'){
21918             ae.removeClass('x-hidden');
21919         }else{
21920             ae.dom.style.display = "";
21921         }
21922     },
21923
21924     /**
21925      * Hide this component.
21926      * @return {Roo.Component} this
21927      */
21928     hide: function(){
21929         if(this.fireEvent("beforehide", this) !== false){
21930             this.hidden = true;
21931             if(this.rendered){
21932                 this.onHide();
21933             }
21934             this.fireEvent("hide", this);
21935         }
21936         return this;
21937     },
21938
21939     // private
21940     onHide : function(){
21941         var ae = this.getActionEl();
21942         if(this.hideMode == 'visibility'){
21943             ae.dom.style.visibility = "hidden";
21944         }else if(this.hideMode == 'offsets'){
21945             ae.addClass('x-hidden');
21946         }else{
21947             ae.dom.style.display = "none";
21948         }
21949     },
21950
21951     /**
21952      * Convenience function to hide or show this component by boolean.
21953      * @param {Boolean} visible True to show, false to hide
21954      * @return {Roo.Component} this
21955      */
21956     setVisible: function(visible){
21957         if(visible) {
21958             this.show();
21959         }else{
21960             this.hide();
21961         }
21962         return this;
21963     },
21964
21965     /**
21966      * Returns true if this component is visible.
21967      */
21968     isVisible : function(){
21969         return this.getActionEl().isVisible();
21970     },
21971
21972     cloneConfig : function(overrides){
21973         overrides = overrides || {};
21974         var id = overrides.id || Roo.id();
21975         var cfg = Roo.applyIf(overrides, this.initialConfig);
21976         cfg.id = id; // prevent dup id
21977         return new this.constructor(cfg);
21978     }
21979 });/*
21980  * Based on:
21981  * Ext JS Library 1.1.1
21982  * Copyright(c) 2006-2007, Ext JS, LLC.
21983  *
21984  * Originally Released Under LGPL - original licence link has changed is not relivant.
21985  *
21986  * Fork - LGPL
21987  * <script type="text/javascript">
21988  */
21989  (function(){ 
21990 /**
21991  * @class Roo.Layer
21992  * @extends Roo.Element
21993  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21994  * automatic maintaining of shadow/shim positions.
21995  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21996  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21997  * you can pass a string with a CSS class name. False turns off the shadow.
21998  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21999  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22000  * @cfg {String} cls CSS class to add to the element
22001  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22002  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22003  * @constructor
22004  * @param {Object} config An object with config options.
22005  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22006  */
22007
22008 Roo.Layer = function(config, existingEl){
22009     config = config || {};
22010     var dh = Roo.DomHelper;
22011     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22012     if(existingEl){
22013         this.dom = Roo.getDom(existingEl);
22014     }
22015     if(!this.dom){
22016         var o = config.dh || {tag: "div", cls: "x-layer"};
22017         this.dom = dh.append(pel, o);
22018     }
22019     if(config.cls){
22020         this.addClass(config.cls);
22021     }
22022     this.constrain = config.constrain !== false;
22023     this.visibilityMode = Roo.Element.VISIBILITY;
22024     if(config.id){
22025         this.id = this.dom.id = config.id;
22026     }else{
22027         this.id = Roo.id(this.dom);
22028     }
22029     this.zindex = config.zindex || this.getZIndex();
22030     this.position("absolute", this.zindex);
22031     if(config.shadow){
22032         this.shadowOffset = config.shadowOffset || 4;
22033         this.shadow = new Roo.Shadow({
22034             offset : this.shadowOffset,
22035             mode : config.shadow
22036         });
22037     }else{
22038         this.shadowOffset = 0;
22039     }
22040     this.useShim = config.shim !== false && Roo.useShims;
22041     this.useDisplay = config.useDisplay;
22042     this.hide();
22043 };
22044
22045 var supr = Roo.Element.prototype;
22046
22047 // shims are shared among layer to keep from having 100 iframes
22048 var shims = [];
22049
22050 Roo.extend(Roo.Layer, Roo.Element, {
22051
22052     getZIndex : function(){
22053         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22054     },
22055
22056     getShim : function(){
22057         if(!this.useShim){
22058             return null;
22059         }
22060         if(this.shim){
22061             return this.shim;
22062         }
22063         var shim = shims.shift();
22064         if(!shim){
22065             shim = this.createShim();
22066             shim.enableDisplayMode('block');
22067             shim.dom.style.display = 'none';
22068             shim.dom.style.visibility = 'visible';
22069         }
22070         var pn = this.dom.parentNode;
22071         if(shim.dom.parentNode != pn){
22072             pn.insertBefore(shim.dom, this.dom);
22073         }
22074         shim.setStyle('z-index', this.getZIndex()-2);
22075         this.shim = shim;
22076         return shim;
22077     },
22078
22079     hideShim : function(){
22080         if(this.shim){
22081             this.shim.setDisplayed(false);
22082             shims.push(this.shim);
22083             delete this.shim;
22084         }
22085     },
22086
22087     disableShadow : function(){
22088         if(this.shadow){
22089             this.shadowDisabled = true;
22090             this.shadow.hide();
22091             this.lastShadowOffset = this.shadowOffset;
22092             this.shadowOffset = 0;
22093         }
22094     },
22095
22096     enableShadow : function(show){
22097         if(this.shadow){
22098             this.shadowDisabled = false;
22099             this.shadowOffset = this.lastShadowOffset;
22100             delete this.lastShadowOffset;
22101             if(show){
22102                 this.sync(true);
22103             }
22104         }
22105     },
22106
22107     // private
22108     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22109     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22110     sync : function(doShow){
22111         var sw = this.shadow;
22112         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22113             var sh = this.getShim();
22114
22115             var w = this.getWidth(),
22116                 h = this.getHeight();
22117
22118             var l = this.getLeft(true),
22119                 t = this.getTop(true);
22120
22121             if(sw && !this.shadowDisabled){
22122                 if(doShow && !sw.isVisible()){
22123                     sw.show(this);
22124                 }else{
22125                     sw.realign(l, t, w, h);
22126                 }
22127                 if(sh){
22128                     if(doShow){
22129                        sh.show();
22130                     }
22131                     // fit the shim behind the shadow, so it is shimmed too
22132                     var a = sw.adjusts, s = sh.dom.style;
22133                     s.left = (Math.min(l, l+a.l))+"px";
22134                     s.top = (Math.min(t, t+a.t))+"px";
22135                     s.width = (w+a.w)+"px";
22136                     s.height = (h+a.h)+"px";
22137                 }
22138             }else if(sh){
22139                 if(doShow){
22140                    sh.show();
22141                 }
22142                 sh.setSize(w, h);
22143                 sh.setLeftTop(l, t);
22144             }
22145             
22146         }
22147     },
22148
22149     // private
22150     destroy : function(){
22151         this.hideShim();
22152         if(this.shadow){
22153             this.shadow.hide();
22154         }
22155         this.removeAllListeners();
22156         var pn = this.dom.parentNode;
22157         if(pn){
22158             pn.removeChild(this.dom);
22159         }
22160         Roo.Element.uncache(this.id);
22161     },
22162
22163     remove : function(){
22164         this.destroy();
22165     },
22166
22167     // private
22168     beginUpdate : function(){
22169         this.updating = true;
22170     },
22171
22172     // private
22173     endUpdate : function(){
22174         this.updating = false;
22175         this.sync(true);
22176     },
22177
22178     // private
22179     hideUnders : function(negOffset){
22180         if(this.shadow){
22181             this.shadow.hide();
22182         }
22183         this.hideShim();
22184     },
22185
22186     // private
22187     constrainXY : function(){
22188         if(this.constrain){
22189             var vw = Roo.lib.Dom.getViewWidth(),
22190                 vh = Roo.lib.Dom.getViewHeight();
22191             var s = Roo.get(document).getScroll();
22192
22193             var xy = this.getXY();
22194             var x = xy[0], y = xy[1];   
22195             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22196             // only move it if it needs it
22197             var moved = false;
22198             // first validate right/bottom
22199             if((x + w) > vw+s.left){
22200                 x = vw - w - this.shadowOffset;
22201                 moved = true;
22202             }
22203             if((y + h) > vh+s.top){
22204                 y = vh - h - this.shadowOffset;
22205                 moved = true;
22206             }
22207             // then make sure top/left isn't negative
22208             if(x < s.left){
22209                 x = s.left;
22210                 moved = true;
22211             }
22212             if(y < s.top){
22213                 y = s.top;
22214                 moved = true;
22215             }
22216             if(moved){
22217                 if(this.avoidY){
22218                     var ay = this.avoidY;
22219                     if(y <= ay && (y+h) >= ay){
22220                         y = ay-h-5;   
22221                     }
22222                 }
22223                 xy = [x, y];
22224                 this.storeXY(xy);
22225                 supr.setXY.call(this, xy);
22226                 this.sync();
22227             }
22228         }
22229     },
22230
22231     isVisible : function(){
22232         return this.visible;    
22233     },
22234
22235     // private
22236     showAction : function(){
22237         this.visible = true; // track visibility to prevent getStyle calls
22238         if(this.useDisplay === true){
22239             this.setDisplayed("");
22240         }else if(this.lastXY){
22241             supr.setXY.call(this, this.lastXY);
22242         }else if(this.lastLT){
22243             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22244         }
22245     },
22246
22247     // private
22248     hideAction : function(){
22249         this.visible = false;
22250         if(this.useDisplay === true){
22251             this.setDisplayed(false);
22252         }else{
22253             this.setLeftTop(-10000,-10000);
22254         }
22255     },
22256
22257     // overridden Element method
22258     setVisible : function(v, a, d, c, e){
22259         if(v){
22260             this.showAction();
22261         }
22262         if(a && v){
22263             var cb = function(){
22264                 this.sync(true);
22265                 if(c){
22266                     c();
22267                 }
22268             }.createDelegate(this);
22269             supr.setVisible.call(this, true, true, d, cb, e);
22270         }else{
22271             if(!v){
22272                 this.hideUnders(true);
22273             }
22274             var cb = c;
22275             if(a){
22276                 cb = function(){
22277                     this.hideAction();
22278                     if(c){
22279                         c();
22280                     }
22281                 }.createDelegate(this);
22282             }
22283             supr.setVisible.call(this, v, a, d, cb, e);
22284             if(v){
22285                 this.sync(true);
22286             }else if(!a){
22287                 this.hideAction();
22288             }
22289         }
22290     },
22291
22292     storeXY : function(xy){
22293         delete this.lastLT;
22294         this.lastXY = xy;
22295     },
22296
22297     storeLeftTop : function(left, top){
22298         delete this.lastXY;
22299         this.lastLT = [left, top];
22300     },
22301
22302     // private
22303     beforeFx : function(){
22304         this.beforeAction();
22305         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22306     },
22307
22308     // private
22309     afterFx : function(){
22310         Roo.Layer.superclass.afterFx.apply(this, arguments);
22311         this.sync(this.isVisible());
22312     },
22313
22314     // private
22315     beforeAction : function(){
22316         if(!this.updating && this.shadow){
22317             this.shadow.hide();
22318         }
22319     },
22320
22321     // overridden Element method
22322     setLeft : function(left){
22323         this.storeLeftTop(left, this.getTop(true));
22324         supr.setLeft.apply(this, arguments);
22325         this.sync();
22326     },
22327
22328     setTop : function(top){
22329         this.storeLeftTop(this.getLeft(true), top);
22330         supr.setTop.apply(this, arguments);
22331         this.sync();
22332     },
22333
22334     setLeftTop : function(left, top){
22335         this.storeLeftTop(left, top);
22336         supr.setLeftTop.apply(this, arguments);
22337         this.sync();
22338     },
22339
22340     setXY : function(xy, a, d, c, e){
22341         this.fixDisplay();
22342         this.beforeAction();
22343         this.storeXY(xy);
22344         var cb = this.createCB(c);
22345         supr.setXY.call(this, xy, a, d, cb, e);
22346         if(!a){
22347             cb();
22348         }
22349     },
22350
22351     // private
22352     createCB : function(c){
22353         var el = this;
22354         return function(){
22355             el.constrainXY();
22356             el.sync(true);
22357             if(c){
22358                 c();
22359             }
22360         };
22361     },
22362
22363     // overridden Element method
22364     setX : function(x, a, d, c, e){
22365         this.setXY([x, this.getY()], a, d, c, e);
22366     },
22367
22368     // overridden Element method
22369     setY : function(y, a, d, c, e){
22370         this.setXY([this.getX(), y], a, d, c, e);
22371     },
22372
22373     // overridden Element method
22374     setSize : function(w, h, a, d, c, e){
22375         this.beforeAction();
22376         var cb = this.createCB(c);
22377         supr.setSize.call(this, w, h, a, d, cb, e);
22378         if(!a){
22379             cb();
22380         }
22381     },
22382
22383     // overridden Element method
22384     setWidth : function(w, a, d, c, e){
22385         this.beforeAction();
22386         var cb = this.createCB(c);
22387         supr.setWidth.call(this, w, a, d, cb, e);
22388         if(!a){
22389             cb();
22390         }
22391     },
22392
22393     // overridden Element method
22394     setHeight : function(h, a, d, c, e){
22395         this.beforeAction();
22396         var cb = this.createCB(c);
22397         supr.setHeight.call(this, h, a, d, cb, e);
22398         if(!a){
22399             cb();
22400         }
22401     },
22402
22403     // overridden Element method
22404     setBounds : function(x, y, w, h, a, d, c, e){
22405         this.beforeAction();
22406         var cb = this.createCB(c);
22407         if(!a){
22408             this.storeXY([x, y]);
22409             supr.setXY.call(this, [x, y]);
22410             supr.setSize.call(this, w, h, a, d, cb, e);
22411             cb();
22412         }else{
22413             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22414         }
22415         return this;
22416     },
22417     
22418     /**
22419      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22420      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22421      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22422      * @param {Number} zindex The new z-index to set
22423      * @return {this} The Layer
22424      */
22425     setZIndex : function(zindex){
22426         this.zindex = zindex;
22427         this.setStyle("z-index", zindex + 2);
22428         if(this.shadow){
22429             this.shadow.setZIndex(zindex + 1);
22430         }
22431         if(this.shim){
22432             this.shim.setStyle("z-index", zindex);
22433         }
22434     }
22435 });
22436 })();/*
22437  * Based on:
22438  * Ext JS Library 1.1.1
22439  * Copyright(c) 2006-2007, Ext JS, LLC.
22440  *
22441  * Originally Released Under LGPL - original licence link has changed is not relivant.
22442  *
22443  * Fork - LGPL
22444  * <script type="text/javascript">
22445  */
22446
22447
22448 /**
22449  * @class Roo.Shadow
22450  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22451  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22452  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22453  * @constructor
22454  * Create a new Shadow
22455  * @param {Object} config The config object
22456  */
22457 Roo.Shadow = function(config){
22458     Roo.apply(this, config);
22459     if(typeof this.mode != "string"){
22460         this.mode = this.defaultMode;
22461     }
22462     var o = this.offset, a = {h: 0};
22463     var rad = Math.floor(this.offset/2);
22464     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22465         case "drop":
22466             a.w = 0;
22467             a.l = a.t = o;
22468             a.t -= 1;
22469             if(Roo.isIE){
22470                 a.l -= this.offset + rad;
22471                 a.t -= this.offset + rad;
22472                 a.w -= rad;
22473                 a.h -= rad;
22474                 a.t += 1;
22475             }
22476         break;
22477         case "sides":
22478             a.w = (o*2);
22479             a.l = -o;
22480             a.t = o-1;
22481             if(Roo.isIE){
22482                 a.l -= (this.offset - rad);
22483                 a.t -= this.offset + rad;
22484                 a.l += 1;
22485                 a.w -= (this.offset - rad)*2;
22486                 a.w -= rad + 1;
22487                 a.h -= 1;
22488             }
22489         break;
22490         case "frame":
22491             a.w = a.h = (o*2);
22492             a.l = a.t = -o;
22493             a.t += 1;
22494             a.h -= 2;
22495             if(Roo.isIE){
22496                 a.l -= (this.offset - rad);
22497                 a.t -= (this.offset - rad);
22498                 a.l += 1;
22499                 a.w -= (this.offset + rad + 1);
22500                 a.h -= (this.offset + rad);
22501                 a.h += 1;
22502             }
22503         break;
22504     };
22505
22506     this.adjusts = a;
22507 };
22508
22509 Roo.Shadow.prototype = {
22510     /**
22511      * @cfg {String} mode
22512      * The shadow display mode.  Supports the following options:<br />
22513      * sides: Shadow displays on both sides and bottom only<br />
22514      * frame: Shadow displays equally on all four sides<br />
22515      * drop: Traditional bottom-right drop shadow (default)
22516      */
22517     /**
22518      * @cfg {String} offset
22519      * The number of pixels to offset the shadow from the element (defaults to 4)
22520      */
22521     offset: 4,
22522
22523     // private
22524     defaultMode: "drop",
22525
22526     /**
22527      * Displays the shadow under the target element
22528      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22529      */
22530     show : function(target){
22531         target = Roo.get(target);
22532         if(!this.el){
22533             this.el = Roo.Shadow.Pool.pull();
22534             if(this.el.dom.nextSibling != target.dom){
22535                 this.el.insertBefore(target);
22536             }
22537         }
22538         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22539         if(Roo.isIE){
22540             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22541         }
22542         this.realign(
22543             target.getLeft(true),
22544             target.getTop(true),
22545             target.getWidth(),
22546             target.getHeight()
22547         );
22548         this.el.dom.style.display = "block";
22549     },
22550
22551     /**
22552      * Returns true if the shadow is visible, else false
22553      */
22554     isVisible : function(){
22555         return this.el ? true : false;  
22556     },
22557
22558     /**
22559      * Direct alignment when values are already available. Show must be called at least once before
22560      * calling this method to ensure it is initialized.
22561      * @param {Number} left The target element left position
22562      * @param {Number} top The target element top position
22563      * @param {Number} width The target element width
22564      * @param {Number} height The target element height
22565      */
22566     realign : function(l, t, w, h){
22567         if(!this.el){
22568             return;
22569         }
22570         var a = this.adjusts, d = this.el.dom, s = d.style;
22571         var iea = 0;
22572         s.left = (l+a.l)+"px";
22573         s.top = (t+a.t)+"px";
22574         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22575  
22576         if(s.width != sws || s.height != shs){
22577             s.width = sws;
22578             s.height = shs;
22579             if(!Roo.isIE){
22580                 var cn = d.childNodes;
22581                 var sww = Math.max(0, (sw-12))+"px";
22582                 cn[0].childNodes[1].style.width = sww;
22583                 cn[1].childNodes[1].style.width = sww;
22584                 cn[2].childNodes[1].style.width = sww;
22585                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22586             }
22587         }
22588     },
22589
22590     /**
22591      * Hides this shadow
22592      */
22593     hide : function(){
22594         if(this.el){
22595             this.el.dom.style.display = "none";
22596             Roo.Shadow.Pool.push(this.el);
22597             delete this.el;
22598         }
22599     },
22600
22601     /**
22602      * Adjust the z-index of this shadow
22603      * @param {Number} zindex The new z-index
22604      */
22605     setZIndex : function(z){
22606         this.zIndex = z;
22607         if(this.el){
22608             this.el.setStyle("z-index", z);
22609         }
22610     }
22611 };
22612
22613 // Private utility class that manages the internal Shadow cache
22614 Roo.Shadow.Pool = function(){
22615     var p = [];
22616     var markup = Roo.isIE ?
22617                  '<div class="x-ie-shadow"></div>' :
22618                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
22619     return {
22620         pull : function(){
22621             var sh = p.shift();
22622             if(!sh){
22623                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22624                 sh.autoBoxAdjust = false;
22625             }
22626             return sh;
22627         },
22628
22629         push : function(sh){
22630             p.push(sh);
22631         }
22632     };
22633 }();/*
22634  * Based on:
22635  * Ext JS Library 1.1.1
22636  * Copyright(c) 2006-2007, Ext JS, LLC.
22637  *
22638  * Originally Released Under LGPL - original licence link has changed is not relivant.
22639  *
22640  * Fork - LGPL
22641  * <script type="text/javascript">
22642  */
22643
22644 /**
22645  * @class Roo.BoxComponent
22646  * @extends Roo.Component
22647  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22648  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22649  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22650  * layout containers.
22651  * @constructor
22652  * @param {Roo.Element/String/Object} config The configuration options.
22653  */
22654 Roo.BoxComponent = function(config){
22655     Roo.Component.call(this, config);
22656     this.addEvents({
22657         /**
22658          * @event resize
22659          * Fires after the component is resized.
22660              * @param {Roo.Component} this
22661              * @param {Number} adjWidth The box-adjusted width that was set
22662              * @param {Number} adjHeight The box-adjusted height that was set
22663              * @param {Number} rawWidth The width that was originally specified
22664              * @param {Number} rawHeight The height that was originally specified
22665              */
22666         resize : true,
22667         /**
22668          * @event move
22669          * Fires after the component is moved.
22670              * @param {Roo.Component} this
22671              * @param {Number} x The new x position
22672              * @param {Number} y The new y position
22673              */
22674         move : true
22675     });
22676 };
22677
22678 Roo.extend(Roo.BoxComponent, Roo.Component, {
22679     // private, set in afterRender to signify that the component has been rendered
22680     boxReady : false,
22681     // private, used to defer height settings to subclasses
22682     deferHeight: false,
22683     /** @cfg {Number} width
22684      * width (optional) size of component
22685      */
22686      /** @cfg {Number} height
22687      * height (optional) size of component
22688      */
22689      
22690     /**
22691      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22692      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22693      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22694      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22695      * @return {Roo.BoxComponent} this
22696      */
22697     setSize : function(w, h){
22698         // support for standard size objects
22699         if(typeof w == 'object'){
22700             h = w.height;
22701             w = w.width;
22702         }
22703         // not rendered
22704         if(!this.boxReady){
22705             this.width = w;
22706             this.height = h;
22707             return this;
22708         }
22709
22710         // prevent recalcs when not needed
22711         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22712             return this;
22713         }
22714         this.lastSize = {width: w, height: h};
22715
22716         var adj = this.adjustSize(w, h);
22717         var aw = adj.width, ah = adj.height;
22718         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22719             var rz = this.getResizeEl();
22720             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22721                 rz.setSize(aw, ah);
22722             }else if(!this.deferHeight && ah !== undefined){
22723                 rz.setHeight(ah);
22724             }else if(aw !== undefined){
22725                 rz.setWidth(aw);
22726             }
22727             this.onResize(aw, ah, w, h);
22728             this.fireEvent('resize', this, aw, ah, w, h);
22729         }
22730         return this;
22731     },
22732
22733     /**
22734      * Gets the current size of the component's underlying element.
22735      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22736      */
22737     getSize : function(){
22738         return this.el.getSize();
22739     },
22740
22741     /**
22742      * Gets the current XY position of the component's underlying element.
22743      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22744      * @return {Array} The XY position of the element (e.g., [100, 200])
22745      */
22746     getPosition : function(local){
22747         if(local === true){
22748             return [this.el.getLeft(true), this.el.getTop(true)];
22749         }
22750         return this.xy || this.el.getXY();
22751     },
22752
22753     /**
22754      * Gets the current box measurements of the component's underlying element.
22755      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22756      * @returns {Object} box An object in the format {x, y, width, height}
22757      */
22758     getBox : function(local){
22759         var s = this.el.getSize();
22760         if(local){
22761             s.x = this.el.getLeft(true);
22762             s.y = this.el.getTop(true);
22763         }else{
22764             var xy = this.xy || this.el.getXY();
22765             s.x = xy[0];
22766             s.y = xy[1];
22767         }
22768         return s;
22769     },
22770
22771     /**
22772      * Sets the current box measurements of the component's underlying element.
22773      * @param {Object} box An object in the format {x, y, width, height}
22774      * @returns {Roo.BoxComponent} this
22775      */
22776     updateBox : function(box){
22777         this.setSize(box.width, box.height);
22778         this.setPagePosition(box.x, box.y);
22779         return this;
22780     },
22781
22782     // protected
22783     getResizeEl : function(){
22784         return this.resizeEl || this.el;
22785     },
22786
22787     // protected
22788     getPositionEl : function(){
22789         return this.positionEl || this.el;
22790     },
22791
22792     /**
22793      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22794      * This method fires the move event.
22795      * @param {Number} left The new left
22796      * @param {Number} top The new top
22797      * @returns {Roo.BoxComponent} this
22798      */
22799     setPosition : function(x, y){
22800         this.x = x;
22801         this.y = y;
22802         if(!this.boxReady){
22803             return this;
22804         }
22805         var adj = this.adjustPosition(x, y);
22806         var ax = adj.x, ay = adj.y;
22807
22808         var el = this.getPositionEl();
22809         if(ax !== undefined || ay !== undefined){
22810             if(ax !== undefined && ay !== undefined){
22811                 el.setLeftTop(ax, ay);
22812             }else if(ax !== undefined){
22813                 el.setLeft(ax);
22814             }else if(ay !== undefined){
22815                 el.setTop(ay);
22816             }
22817             this.onPosition(ax, ay);
22818             this.fireEvent('move', this, ax, ay);
22819         }
22820         return this;
22821     },
22822
22823     /**
22824      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22825      * This method fires the move event.
22826      * @param {Number} x The new x position
22827      * @param {Number} y The new y position
22828      * @returns {Roo.BoxComponent} this
22829      */
22830     setPagePosition : function(x, y){
22831         this.pageX = x;
22832         this.pageY = y;
22833         if(!this.boxReady){
22834             return;
22835         }
22836         if(x === undefined || y === undefined){ // cannot translate undefined points
22837             return;
22838         }
22839         var p = this.el.translatePoints(x, y);
22840         this.setPosition(p.left, p.top);
22841         return this;
22842     },
22843
22844     // private
22845     onRender : function(ct, position){
22846         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22847         if(this.resizeEl){
22848             this.resizeEl = Roo.get(this.resizeEl);
22849         }
22850         if(this.positionEl){
22851             this.positionEl = Roo.get(this.positionEl);
22852         }
22853     },
22854
22855     // private
22856     afterRender : function(){
22857         Roo.BoxComponent.superclass.afterRender.call(this);
22858         this.boxReady = true;
22859         this.setSize(this.width, this.height);
22860         if(this.x || this.y){
22861             this.setPosition(this.x, this.y);
22862         }
22863         if(this.pageX || this.pageY){
22864             this.setPagePosition(this.pageX, this.pageY);
22865         }
22866     },
22867
22868     /**
22869      * Force the component's size to recalculate based on the underlying element's current height and width.
22870      * @returns {Roo.BoxComponent} this
22871      */
22872     syncSize : function(){
22873         delete this.lastSize;
22874         this.setSize(this.el.getWidth(), this.el.getHeight());
22875         return this;
22876     },
22877
22878     /**
22879      * Called after the component is resized, this method is empty by default but can be implemented by any
22880      * subclass that needs to perform custom logic after a resize occurs.
22881      * @param {Number} adjWidth The box-adjusted width that was set
22882      * @param {Number} adjHeight The box-adjusted height that was set
22883      * @param {Number} rawWidth The width that was originally specified
22884      * @param {Number} rawHeight The height that was originally specified
22885      */
22886     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22887
22888     },
22889
22890     /**
22891      * Called after the component is moved, this method is empty by default but can be implemented by any
22892      * subclass that needs to perform custom logic after a move occurs.
22893      * @param {Number} x The new x position
22894      * @param {Number} y The new y position
22895      */
22896     onPosition : function(x, y){
22897
22898     },
22899
22900     // private
22901     adjustSize : function(w, h){
22902         if(this.autoWidth){
22903             w = 'auto';
22904         }
22905         if(this.autoHeight){
22906             h = 'auto';
22907         }
22908         return {width : w, height: h};
22909     },
22910
22911     // private
22912     adjustPosition : function(x, y){
22913         return {x : x, y: y};
22914     }
22915 });/*
22916  * Based on:
22917  * Ext JS Library 1.1.1
22918  * Copyright(c) 2006-2007, Ext JS, LLC.
22919  *
22920  * Originally Released Under LGPL - original licence link has changed is not relivant.
22921  *
22922  * Fork - LGPL
22923  * <script type="text/javascript">
22924  */
22925
22926
22927 /**
22928  * @class Roo.SplitBar
22929  * @extends Roo.util.Observable
22930  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22931  * <br><br>
22932  * Usage:
22933  * <pre><code>
22934 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22935                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22936 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22937 split.minSize = 100;
22938 split.maxSize = 600;
22939 split.animate = true;
22940 split.on('moved', splitterMoved);
22941 </code></pre>
22942  * @constructor
22943  * Create a new SplitBar
22944  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22945  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22946  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22947  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22948                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22949                         position of the SplitBar).
22950  */
22951 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22952     
22953     /** @private */
22954     this.el = Roo.get(dragElement, true);
22955     this.el.dom.unselectable = "on";
22956     /** @private */
22957     this.resizingEl = Roo.get(resizingElement, true);
22958
22959     /**
22960      * @private
22961      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22962      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22963      * @type Number
22964      */
22965     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22966     
22967     /**
22968      * The minimum size of the resizing element. (Defaults to 0)
22969      * @type Number
22970      */
22971     this.minSize = 0;
22972     
22973     /**
22974      * The maximum size of the resizing element. (Defaults to 2000)
22975      * @type Number
22976      */
22977     this.maxSize = 2000;
22978     
22979     /**
22980      * Whether to animate the transition to the new size
22981      * @type Boolean
22982      */
22983     this.animate = false;
22984     
22985     /**
22986      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22987      * @type Boolean
22988      */
22989     this.useShim = false;
22990     
22991     /** @private */
22992     this.shim = null;
22993     
22994     if(!existingProxy){
22995         /** @private */
22996         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22997     }else{
22998         this.proxy = Roo.get(existingProxy).dom;
22999     }
23000     /** @private */
23001     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23002     
23003     /** @private */
23004     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23005     
23006     /** @private */
23007     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23008     
23009     /** @private */
23010     this.dragSpecs = {};
23011     
23012     /**
23013      * @private The adapter to use to positon and resize elements
23014      */
23015     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23016     this.adapter.init(this);
23017     
23018     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23019         /** @private */
23020         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23021         this.el.addClass("x-splitbar-h");
23022     }else{
23023         /** @private */
23024         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23025         this.el.addClass("x-splitbar-v");
23026     }
23027     
23028     this.addEvents({
23029         /**
23030          * @event resize
23031          * Fires when the splitter is moved (alias for {@link #event-moved})
23032          * @param {Roo.SplitBar} this
23033          * @param {Number} newSize the new width or height
23034          */
23035         "resize" : true,
23036         /**
23037          * @event moved
23038          * Fires when the splitter is moved
23039          * @param {Roo.SplitBar} this
23040          * @param {Number} newSize the new width or height
23041          */
23042         "moved" : true,
23043         /**
23044          * @event beforeresize
23045          * Fires before the splitter is dragged
23046          * @param {Roo.SplitBar} this
23047          */
23048         "beforeresize" : true,
23049
23050         "beforeapply" : true
23051     });
23052
23053     Roo.util.Observable.call(this);
23054 };
23055
23056 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23057     onStartProxyDrag : function(x, y){
23058         this.fireEvent("beforeresize", this);
23059         if(!this.overlay){
23060             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23061             o.unselectable();
23062             o.enableDisplayMode("block");
23063             // all splitbars share the same overlay
23064             Roo.SplitBar.prototype.overlay = o;
23065         }
23066         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23067         this.overlay.show();
23068         Roo.get(this.proxy).setDisplayed("block");
23069         var size = this.adapter.getElementSize(this);
23070         this.activeMinSize = this.getMinimumSize();;
23071         this.activeMaxSize = this.getMaximumSize();;
23072         var c1 = size - this.activeMinSize;
23073         var c2 = Math.max(this.activeMaxSize - size, 0);
23074         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23075             this.dd.resetConstraints();
23076             this.dd.setXConstraint(
23077                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23078                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23079             );
23080             this.dd.setYConstraint(0, 0);
23081         }else{
23082             this.dd.resetConstraints();
23083             this.dd.setXConstraint(0, 0);
23084             this.dd.setYConstraint(
23085                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23086                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23087             );
23088          }
23089         this.dragSpecs.startSize = size;
23090         this.dragSpecs.startPoint = [x, y];
23091         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23092     },
23093     
23094     /** 
23095      * @private Called after the drag operation by the DDProxy
23096      */
23097     onEndProxyDrag : function(e){
23098         Roo.get(this.proxy).setDisplayed(false);
23099         var endPoint = Roo.lib.Event.getXY(e);
23100         if(this.overlay){
23101             this.overlay.hide();
23102         }
23103         var newSize;
23104         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23105             newSize = this.dragSpecs.startSize + 
23106                 (this.placement == Roo.SplitBar.LEFT ?
23107                     endPoint[0] - this.dragSpecs.startPoint[0] :
23108                     this.dragSpecs.startPoint[0] - endPoint[0]
23109                 );
23110         }else{
23111             newSize = this.dragSpecs.startSize + 
23112                 (this.placement == Roo.SplitBar.TOP ?
23113                     endPoint[1] - this.dragSpecs.startPoint[1] :
23114                     this.dragSpecs.startPoint[1] - endPoint[1]
23115                 );
23116         }
23117         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23118         if(newSize != this.dragSpecs.startSize){
23119             if(this.fireEvent('beforeapply', this, newSize) !== false){
23120                 this.adapter.setElementSize(this, newSize);
23121                 this.fireEvent("moved", this, newSize);
23122                 this.fireEvent("resize", this, newSize);
23123             }
23124         }
23125     },
23126     
23127     /**
23128      * Get the adapter this SplitBar uses
23129      * @return The adapter object
23130      */
23131     getAdapter : function(){
23132         return this.adapter;
23133     },
23134     
23135     /**
23136      * Set the adapter this SplitBar uses
23137      * @param {Object} adapter A SplitBar adapter object
23138      */
23139     setAdapter : function(adapter){
23140         this.adapter = adapter;
23141         this.adapter.init(this);
23142     },
23143     
23144     /**
23145      * Gets the minimum size for the resizing element
23146      * @return {Number} The minimum size
23147      */
23148     getMinimumSize : function(){
23149         return this.minSize;
23150     },
23151     
23152     /**
23153      * Sets the minimum size for the resizing element
23154      * @param {Number} minSize The minimum size
23155      */
23156     setMinimumSize : function(minSize){
23157         this.minSize = minSize;
23158     },
23159     
23160     /**
23161      * Gets the maximum size for the resizing element
23162      * @return {Number} The maximum size
23163      */
23164     getMaximumSize : function(){
23165         return this.maxSize;
23166     },
23167     
23168     /**
23169      * Sets the maximum size for the resizing element
23170      * @param {Number} maxSize The maximum size
23171      */
23172     setMaximumSize : function(maxSize){
23173         this.maxSize = maxSize;
23174     },
23175     
23176     /**
23177      * Sets the initialize size for the resizing element
23178      * @param {Number} size The initial size
23179      */
23180     setCurrentSize : function(size){
23181         var oldAnimate = this.animate;
23182         this.animate = false;
23183         this.adapter.setElementSize(this, size);
23184         this.animate = oldAnimate;
23185     },
23186     
23187     /**
23188      * Destroy this splitbar. 
23189      * @param {Boolean} removeEl True to remove the element
23190      */
23191     destroy : function(removeEl){
23192         if(this.shim){
23193             this.shim.remove();
23194         }
23195         this.dd.unreg();
23196         this.proxy.parentNode.removeChild(this.proxy);
23197         if(removeEl){
23198             this.el.remove();
23199         }
23200     }
23201 });
23202
23203 /**
23204  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
23205  */
23206 Roo.SplitBar.createProxy = function(dir){
23207     var proxy = new Roo.Element(document.createElement("div"));
23208     proxy.unselectable();
23209     var cls = 'x-splitbar-proxy';
23210     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23211     document.body.appendChild(proxy.dom);
23212     return proxy.dom;
23213 };
23214
23215 /** 
23216  * @class Roo.SplitBar.BasicLayoutAdapter
23217  * Default Adapter. It assumes the splitter and resizing element are not positioned
23218  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23219  */
23220 Roo.SplitBar.BasicLayoutAdapter = function(){
23221 };
23222
23223 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23224     // do nothing for now
23225     init : function(s){
23226     
23227     },
23228     /**
23229      * Called before drag operations to get the current size of the resizing element. 
23230      * @param {Roo.SplitBar} s The SplitBar using this adapter
23231      */
23232      getElementSize : function(s){
23233         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23234             return s.resizingEl.getWidth();
23235         }else{
23236             return s.resizingEl.getHeight();
23237         }
23238     },
23239     
23240     /**
23241      * Called after drag operations to set the size of the resizing element.
23242      * @param {Roo.SplitBar} s The SplitBar using this adapter
23243      * @param {Number} newSize The new size to set
23244      * @param {Function} onComplete A function to be invoked when resizing is complete
23245      */
23246     setElementSize : function(s, newSize, onComplete){
23247         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23248             if(!s.animate){
23249                 s.resizingEl.setWidth(newSize);
23250                 if(onComplete){
23251                     onComplete(s, newSize);
23252                 }
23253             }else{
23254                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23255             }
23256         }else{
23257             
23258             if(!s.animate){
23259                 s.resizingEl.setHeight(newSize);
23260                 if(onComplete){
23261                     onComplete(s, newSize);
23262                 }
23263             }else{
23264                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23265             }
23266         }
23267     }
23268 };
23269
23270 /** 
23271  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23272  * @extends Roo.SplitBar.BasicLayoutAdapter
23273  * Adapter that  moves the splitter element to align with the resized sizing element. 
23274  * Used with an absolute positioned SplitBar.
23275  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23276  * document.body, make sure you assign an id to the body element.
23277  */
23278 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23279     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23280     this.container = Roo.get(container);
23281 };
23282
23283 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23284     init : function(s){
23285         this.basic.init(s);
23286     },
23287     
23288     getElementSize : function(s){
23289         return this.basic.getElementSize(s);
23290     },
23291     
23292     setElementSize : function(s, newSize, onComplete){
23293         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23294     },
23295     
23296     moveSplitter : function(s){
23297         var yes = Roo.SplitBar;
23298         switch(s.placement){
23299             case yes.LEFT:
23300                 s.el.setX(s.resizingEl.getRight());
23301                 break;
23302             case yes.RIGHT:
23303                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23304                 break;
23305             case yes.TOP:
23306                 s.el.setY(s.resizingEl.getBottom());
23307                 break;
23308             case yes.BOTTOM:
23309                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23310                 break;
23311         }
23312     }
23313 };
23314
23315 /**
23316  * Orientation constant - Create a vertical SplitBar
23317  * @static
23318  * @type Number
23319  */
23320 Roo.SplitBar.VERTICAL = 1;
23321
23322 /**
23323  * Orientation constant - Create a horizontal SplitBar
23324  * @static
23325  * @type Number
23326  */
23327 Roo.SplitBar.HORIZONTAL = 2;
23328
23329 /**
23330  * Placement constant - The resizing element is to the left of the splitter element
23331  * @static
23332  * @type Number
23333  */
23334 Roo.SplitBar.LEFT = 1;
23335
23336 /**
23337  * Placement constant - The resizing element is to the right of the splitter element
23338  * @static
23339  * @type Number
23340  */
23341 Roo.SplitBar.RIGHT = 2;
23342
23343 /**
23344  * Placement constant - The resizing element is positioned above the splitter element
23345  * @static
23346  * @type Number
23347  */
23348 Roo.SplitBar.TOP = 3;
23349
23350 /**
23351  * Placement constant - The resizing element is positioned under splitter element
23352  * @static
23353  * @type Number
23354  */
23355 Roo.SplitBar.BOTTOM = 4;
23356 /*
23357  * Based on:
23358  * Ext JS Library 1.1.1
23359  * Copyright(c) 2006-2007, Ext JS, LLC.
23360  *
23361  * Originally Released Under LGPL - original licence link has changed is not relivant.
23362  *
23363  * Fork - LGPL
23364  * <script type="text/javascript">
23365  */
23366
23367 /**
23368  * @class Roo.View
23369  * @extends Roo.util.Observable
23370  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23371  * This class also supports single and multi selection modes. <br>
23372  * Create a data model bound view:
23373  <pre><code>
23374  var store = new Roo.data.Store(...);
23375
23376  var view = new Roo.View({
23377     el : "my-element",
23378     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23379  
23380     singleSelect: true,
23381     selectedClass: "ydataview-selected",
23382     store: store
23383  });
23384
23385  // listen for node click?
23386  view.on("click", function(vw, index, node, e){
23387  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23388  });
23389
23390  // load XML data
23391  dataModel.load("foobar.xml");
23392  </code></pre>
23393  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23394  * <br><br>
23395  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23396  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23397  * 
23398  * Note: old style constructor is still suported (container, template, config)
23399  * 
23400  * @constructor
23401  * Create a new View
23402  * @param {Object} config The config object
23403  * 
23404  */
23405 Roo.View = function(config, depreciated_tpl, depreciated_config){
23406     
23407     if (typeof(depreciated_tpl) == 'undefined') {
23408         // new way.. - universal constructor.
23409         Roo.apply(this, config);
23410         this.el  = Roo.get(this.el);
23411     } else {
23412         // old format..
23413         this.el  = Roo.get(config);
23414         this.tpl = depreciated_tpl;
23415         Roo.apply(this, depreciated_config);
23416     }
23417      
23418     
23419     if(typeof(this.tpl) == "string"){
23420         this.tpl = new Roo.Template(this.tpl);
23421     } else {
23422         // support xtype ctors..
23423         this.tpl = new Roo.factory(this.tpl, Roo);
23424     }
23425     
23426     
23427     this.tpl.compile();
23428    
23429
23430      
23431     /** @private */
23432     this.addEvents({
23433     /**
23434      * @event beforeclick
23435      * Fires before a click is processed. Returns false to cancel the default action.
23436      * @param {Roo.View} this
23437      * @param {Number} index The index of the target node
23438      * @param {HTMLElement} node The target node
23439      * @param {Roo.EventObject} e The raw event object
23440      */
23441         "beforeclick" : true,
23442     /**
23443      * @event click
23444      * Fires when a template node is clicked.
23445      * @param {Roo.View} this
23446      * @param {Number} index The index of the target node
23447      * @param {HTMLElement} node The target node
23448      * @param {Roo.EventObject} e The raw event object
23449      */
23450         "click" : true,
23451     /**
23452      * @event dblclick
23453      * Fires when a template node is double clicked.
23454      * @param {Roo.View} this
23455      * @param {Number} index The index of the target node
23456      * @param {HTMLElement} node The target node
23457      * @param {Roo.EventObject} e The raw event object
23458      */
23459         "dblclick" : true,
23460     /**
23461      * @event contextmenu
23462      * Fires when a template node is right clicked.
23463      * @param {Roo.View} this
23464      * @param {Number} index The index of the target node
23465      * @param {HTMLElement} node The target node
23466      * @param {Roo.EventObject} e The raw event object
23467      */
23468         "contextmenu" : true,
23469     /**
23470      * @event selectionchange
23471      * Fires when the selected nodes change.
23472      * @param {Roo.View} this
23473      * @param {Array} selections Array of the selected nodes
23474      */
23475         "selectionchange" : true,
23476
23477     /**
23478      * @event beforeselect
23479      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23480      * @param {Roo.View} this
23481      * @param {HTMLElement} node The node to be selected
23482      * @param {Array} selections Array of currently selected nodes
23483      */
23484         "beforeselect" : true
23485     });
23486
23487     this.el.on({
23488         "click": this.onClick,
23489         "dblclick": this.onDblClick,
23490         "contextmenu": this.onContextMenu,
23491         scope:this
23492     });
23493
23494     this.selections = [];
23495     this.nodes = [];
23496     this.cmp = new Roo.CompositeElementLite([]);
23497     if(this.store){
23498         this.store = Roo.factory(this.store, Roo.data);
23499         this.setStore(this.store, true);
23500     }
23501     Roo.View.superclass.constructor.call(this);
23502 };
23503
23504 Roo.extend(Roo.View, Roo.util.Observable, {
23505     
23506      /**
23507      * @cfg {Roo.data.Store} store Data store to load data from.
23508      */
23509     store : false,
23510     
23511     /**
23512      * @cfg {String|Roo.Element} el The container element.
23513      */
23514     el : '',
23515     
23516     /**
23517      * @cfg {String|Roo.Template} tpl The template used by this View 
23518      */
23519     tpl : false,
23520     
23521     /**
23522      * @cfg {String} selectedClass The css class to add to selected nodes
23523      */
23524     selectedClass : "x-view-selected",
23525      /**
23526      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23527      */
23528     emptyText : "",
23529     /**
23530      * @cfg {Boolean} multiSelect Allow multiple selection
23531      */
23532     
23533     multiSelect : false,
23534     /**
23535      * @cfg {Boolean} singleSelect Allow single selection
23536      */
23537     singleSelect:  false,
23538     
23539     /**
23540      * Returns the element this view is bound to.
23541      * @return {Roo.Element}
23542      */
23543     getEl : function(){
23544         return this.el;
23545     },
23546
23547     /**
23548      * Refreshes the view.
23549      */
23550     refresh : function(){
23551         var t = this.tpl;
23552         this.clearSelections();
23553         this.el.update("");
23554         var html = [];
23555         var records = this.store.getRange();
23556         if(records.length < 1){
23557             this.el.update(this.emptyText);
23558             return;
23559         }
23560         for(var i = 0, len = records.length; i < len; i++){
23561             var data = this.prepareData(records[i].data, i, records[i]);
23562             html[html.length] = t.apply(data);
23563         }
23564         this.el.update(html.join(""));
23565         this.nodes = this.el.dom.childNodes;
23566         this.updateIndexes(0);
23567     },
23568
23569     /**
23570      * Function to override to reformat the data that is sent to
23571      * the template for each node.
23572      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23573      * a JSON object for an UpdateManager bound view).
23574      */
23575     prepareData : function(data){
23576         return data;
23577     },
23578
23579     onUpdate : function(ds, record){
23580         this.clearSelections();
23581         var index = this.store.indexOf(record);
23582         var n = this.nodes[index];
23583         this.tpl.insertBefore(n, this.prepareData(record.data));
23584         n.parentNode.removeChild(n);
23585         this.updateIndexes(index, index);
23586     },
23587
23588     onAdd : function(ds, records, index){
23589         this.clearSelections();
23590         if(this.nodes.length == 0){
23591             this.refresh();
23592             return;
23593         }
23594         var n = this.nodes[index];
23595         for(var i = 0, len = records.length; i < len; i++){
23596             var d = this.prepareData(records[i].data);
23597             if(n){
23598                 this.tpl.insertBefore(n, d);
23599             }else{
23600                 this.tpl.append(this.el, d);
23601             }
23602         }
23603         this.updateIndexes(index);
23604     },
23605
23606     onRemove : function(ds, record, index){
23607         this.clearSelections();
23608         this.el.dom.removeChild(this.nodes[index]);
23609         this.updateIndexes(index);
23610     },
23611
23612     /**
23613      * Refresh an individual node.
23614      * @param {Number} index
23615      */
23616     refreshNode : function(index){
23617         this.onUpdate(this.store, this.store.getAt(index));
23618     },
23619
23620     updateIndexes : function(startIndex, endIndex){
23621         var ns = this.nodes;
23622         startIndex = startIndex || 0;
23623         endIndex = endIndex || ns.length - 1;
23624         for(var i = startIndex; i <= endIndex; i++){
23625             ns[i].nodeIndex = i;
23626         }
23627     },
23628
23629     /**
23630      * Changes the data store this view uses and refresh the view.
23631      * @param {Store} store
23632      */
23633     setStore : function(store, initial){
23634         if(!initial && this.store){
23635             this.store.un("datachanged", this.refresh);
23636             this.store.un("add", this.onAdd);
23637             this.store.un("remove", this.onRemove);
23638             this.store.un("update", this.onUpdate);
23639             this.store.un("clear", this.refresh);
23640         }
23641         if(store){
23642           
23643             store.on("datachanged", this.refresh, this);
23644             store.on("add", this.onAdd, this);
23645             store.on("remove", this.onRemove, this);
23646             store.on("update", this.onUpdate, this);
23647             store.on("clear", this.refresh, this);
23648         }
23649         
23650         if(store){
23651             this.refresh();
23652         }
23653     },
23654
23655     /**
23656      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23657      * @param {HTMLElement} node
23658      * @return {HTMLElement} The template node
23659      */
23660     findItemFromChild : function(node){
23661         var el = this.el.dom;
23662         if(!node || node.parentNode == el){
23663                     return node;
23664             }
23665             var p = node.parentNode;
23666             while(p && p != el){
23667             if(p.parentNode == el){
23668                 return p;
23669             }
23670             p = p.parentNode;
23671         }
23672             return null;
23673     },
23674
23675     /** @ignore */
23676     onClick : function(e){
23677         var item = this.findItemFromChild(e.getTarget());
23678         if(item){
23679             var index = this.indexOf(item);
23680             if(this.onItemClick(item, index, e) !== false){
23681                 this.fireEvent("click", this, index, item, e);
23682             }
23683         }else{
23684             this.clearSelections();
23685         }
23686     },
23687
23688     /** @ignore */
23689     onContextMenu : function(e){
23690         var item = this.findItemFromChild(e.getTarget());
23691         if(item){
23692             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23693         }
23694     },
23695
23696     /** @ignore */
23697     onDblClick : function(e){
23698         var item = this.findItemFromChild(e.getTarget());
23699         if(item){
23700             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23701         }
23702     },
23703
23704     onItemClick : function(item, index, e){
23705         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23706             return false;
23707         }
23708         if(this.multiSelect || this.singleSelect){
23709             if(this.multiSelect && e.shiftKey && this.lastSelection){
23710                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23711             }else{
23712                 this.select(item, this.multiSelect && e.ctrlKey);
23713                 this.lastSelection = item;
23714             }
23715             e.preventDefault();
23716         }
23717         return true;
23718     },
23719
23720     /**
23721      * Get the number of selected nodes.
23722      * @return {Number}
23723      */
23724     getSelectionCount : function(){
23725         return this.selections.length;
23726     },
23727
23728     /**
23729      * Get the currently selected nodes.
23730      * @return {Array} An array of HTMLElements
23731      */
23732     getSelectedNodes : function(){
23733         return this.selections;
23734     },
23735
23736     /**
23737      * Get the indexes of the selected nodes.
23738      * @return {Array}
23739      */
23740     getSelectedIndexes : function(){
23741         var indexes = [], s = this.selections;
23742         for(var i = 0, len = s.length; i < len; i++){
23743             indexes.push(s[i].nodeIndex);
23744         }
23745         return indexes;
23746     },
23747
23748     /**
23749      * Clear all selections
23750      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23751      */
23752     clearSelections : function(suppressEvent){
23753         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23754             this.cmp.elements = this.selections;
23755             this.cmp.removeClass(this.selectedClass);
23756             this.selections = [];
23757             if(!suppressEvent){
23758                 this.fireEvent("selectionchange", this, this.selections);
23759             }
23760         }
23761     },
23762
23763     /**
23764      * Returns true if the passed node is selected
23765      * @param {HTMLElement/Number} node The node or node index
23766      * @return {Boolean}
23767      */
23768     isSelected : function(node){
23769         var s = this.selections;
23770         if(s.length < 1){
23771             return false;
23772         }
23773         node = this.getNode(node);
23774         return s.indexOf(node) !== -1;
23775     },
23776
23777     /**
23778      * Selects nodes.
23779      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
23780      * @param {Boolean} keepExisting (optional) true to keep existing selections
23781      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23782      */
23783     select : function(nodeInfo, keepExisting, suppressEvent){
23784         if(nodeInfo instanceof Array){
23785             if(!keepExisting){
23786                 this.clearSelections(true);
23787             }
23788             for(var i = 0, len = nodeInfo.length; i < len; i++){
23789                 this.select(nodeInfo[i], true, true);
23790             }
23791         } else{
23792             var node = this.getNode(nodeInfo);
23793             if(node && !this.isSelected(node)){
23794                 if(!keepExisting){
23795                     this.clearSelections(true);
23796                 }
23797                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23798                     Roo.fly(node).addClass(this.selectedClass);
23799                     this.selections.push(node);
23800                     if(!suppressEvent){
23801                         this.fireEvent("selectionchange", this, this.selections);
23802                     }
23803                 }
23804             }
23805         }
23806     },
23807
23808     /**
23809      * Gets a template node.
23810      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23811      * @return {HTMLElement} The node or null if it wasn't found
23812      */
23813     getNode : function(nodeInfo){
23814         if(typeof nodeInfo == "string"){
23815             return document.getElementById(nodeInfo);
23816         }else if(typeof nodeInfo == "number"){
23817             return this.nodes[nodeInfo];
23818         }
23819         return nodeInfo;
23820     },
23821
23822     /**
23823      * Gets a range template nodes.
23824      * @param {Number} startIndex
23825      * @param {Number} endIndex
23826      * @return {Array} An array of nodes
23827      */
23828     getNodes : function(start, end){
23829         var ns = this.nodes;
23830         start = start || 0;
23831         end = typeof end == "undefined" ? ns.length - 1 : end;
23832         var nodes = [];
23833         if(start <= end){
23834             for(var i = start; i <= end; i++){
23835                 nodes.push(ns[i]);
23836             }
23837         } else{
23838             for(var i = start; i >= end; i--){
23839                 nodes.push(ns[i]);
23840             }
23841         }
23842         return nodes;
23843     },
23844
23845     /**
23846      * Finds the index of the passed node
23847      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23848      * @return {Number} The index of the node or -1
23849      */
23850     indexOf : function(node){
23851         node = this.getNode(node);
23852         if(typeof node.nodeIndex == "number"){
23853             return node.nodeIndex;
23854         }
23855         var ns = this.nodes;
23856         for(var i = 0, len = ns.length; i < len; i++){
23857             if(ns[i] == node){
23858                 return i;
23859             }
23860         }
23861         return -1;
23862     }
23863 });
23864 /*
23865  * Based on:
23866  * Ext JS Library 1.1.1
23867  * Copyright(c) 2006-2007, Ext JS, LLC.
23868  *
23869  * Originally Released Under LGPL - original licence link has changed is not relivant.
23870  *
23871  * Fork - LGPL
23872  * <script type="text/javascript">
23873  */
23874
23875 /**
23876  * @class Roo.JsonView
23877  * @extends Roo.View
23878  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23879 <pre><code>
23880 var view = new Roo.JsonView({
23881     container: "my-element",
23882     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23883     multiSelect: true, 
23884     jsonRoot: "data" 
23885 });
23886
23887 // listen for node click?
23888 view.on("click", function(vw, index, node, e){
23889     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23890 });
23891
23892 // direct load of JSON data
23893 view.load("foobar.php");
23894
23895 // Example from my blog list
23896 var tpl = new Roo.Template(
23897     '&lt;div class="entry"&gt;' +
23898     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23899     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23900     "&lt;/div&gt;&lt;hr /&gt;"
23901 );
23902
23903 var moreView = new Roo.JsonView({
23904     container :  "entry-list", 
23905     template : tpl,
23906     jsonRoot: "posts"
23907 });
23908 moreView.on("beforerender", this.sortEntries, this);
23909 moreView.load({
23910     url: "/blog/get-posts.php",
23911     params: "allposts=true",
23912     text: "Loading Blog Entries..."
23913 });
23914 </code></pre>
23915
23916 * Note: old code is supported with arguments : (container, template, config)
23917
23918
23919  * @constructor
23920  * Create a new JsonView
23921  * 
23922  * @param {Object} config The config object
23923  * 
23924  */
23925 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23926     
23927     
23928     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23929
23930     var um = this.el.getUpdateManager();
23931     um.setRenderer(this);
23932     um.on("update", this.onLoad, this);
23933     um.on("failure", this.onLoadException, this);
23934
23935     /**
23936      * @event beforerender
23937      * Fires before rendering of the downloaded JSON data.
23938      * @param {Roo.JsonView} this
23939      * @param {Object} data The JSON data loaded
23940      */
23941     /**
23942      * @event load
23943      * Fires when data is loaded.
23944      * @param {Roo.JsonView} this
23945      * @param {Object} data The JSON data loaded
23946      * @param {Object} response The raw Connect response object
23947      */
23948     /**
23949      * @event loadexception
23950      * Fires when loading fails.
23951      * @param {Roo.JsonView} this
23952      * @param {Object} response The raw Connect response object
23953      */
23954     this.addEvents({
23955         'beforerender' : true,
23956         'load' : true,
23957         'loadexception' : true
23958     });
23959 };
23960 Roo.extend(Roo.JsonView, Roo.View, {
23961     /**
23962      * @type {String} The root property in the loaded JSON object that contains the data
23963      */
23964     jsonRoot : "",
23965
23966     /**
23967      * Refreshes the view.
23968      */
23969     refresh : function(){
23970         this.clearSelections();
23971         this.el.update("");
23972         var html = [];
23973         var o = this.jsonData;
23974         if(o && o.length > 0){
23975             for(var i = 0, len = o.length; i < len; i++){
23976                 var data = this.prepareData(o[i], i, o);
23977                 html[html.length] = this.tpl.apply(data);
23978             }
23979         }else{
23980             html.push(this.emptyText);
23981         }
23982         this.el.update(html.join(""));
23983         this.nodes = this.el.dom.childNodes;
23984         this.updateIndexes(0);
23985     },
23986
23987     /**
23988      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
23989      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
23990      <pre><code>
23991      view.load({
23992          url: "your-url.php",
23993          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23994          callback: yourFunction,
23995          scope: yourObject, //(optional scope)
23996          discardUrl: false,
23997          nocache: false,
23998          text: "Loading...",
23999          timeout: 30,
24000          scripts: false
24001      });
24002      </code></pre>
24003      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24004      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
24005      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
24006      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24007      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
24008      */
24009     load : function(){
24010         var um = this.el.getUpdateManager();
24011         um.update.apply(um, arguments);
24012     },
24013
24014     render : function(el, response){
24015         this.clearSelections();
24016         this.el.update("");
24017         var o;
24018         try{
24019             o = Roo.util.JSON.decode(response.responseText);
24020             if(this.jsonRoot){
24021                 
24022                 o = o[this.jsonRoot];
24023             }
24024         } catch(e){
24025         }
24026         /**
24027          * The current JSON data or null
24028          */
24029         this.jsonData = o;
24030         this.beforeRender();
24031         this.refresh();
24032     },
24033
24034 /**
24035  * Get the number of records in the current JSON dataset
24036  * @return {Number}
24037  */
24038     getCount : function(){
24039         return this.jsonData ? this.jsonData.length : 0;
24040     },
24041
24042 /**
24043  * Returns the JSON object for the specified node(s)
24044  * @param {HTMLElement/Array} node The node or an array of nodes
24045  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24046  * you get the JSON object for the node
24047  */
24048     getNodeData : function(node){
24049         if(node instanceof Array){
24050             var data = [];
24051             for(var i = 0, len = node.length; i < len; i++){
24052                 data.push(this.getNodeData(node[i]));
24053             }
24054             return data;
24055         }
24056         return this.jsonData[this.indexOf(node)] || null;
24057     },
24058
24059     beforeRender : function(){
24060         this.snapshot = this.jsonData;
24061         if(this.sortInfo){
24062             this.sort.apply(this, this.sortInfo);
24063         }
24064         this.fireEvent("beforerender", this, this.jsonData);
24065     },
24066
24067     onLoad : function(el, o){
24068         this.fireEvent("load", this, this.jsonData, o);
24069     },
24070
24071     onLoadException : function(el, o){
24072         this.fireEvent("loadexception", this, o);
24073     },
24074
24075 /**
24076  * Filter the data by a specific property.
24077  * @param {String} property A property on your JSON objects
24078  * @param {String/RegExp} value Either string that the property values
24079  * should start with, or a RegExp to test against the property
24080  */
24081     filter : function(property, value){
24082         if(this.jsonData){
24083             var data = [];
24084             var ss = this.snapshot;
24085             if(typeof value == "string"){
24086                 var vlen = value.length;
24087                 if(vlen == 0){
24088                     this.clearFilter();
24089                     return;
24090                 }
24091                 value = value.toLowerCase();
24092                 for(var i = 0, len = ss.length; i < len; i++){
24093                     var o = ss[i];
24094                     if(o[property].substr(0, vlen).toLowerCase() == value){
24095                         data.push(o);
24096                     }
24097                 }
24098             } else if(value.exec){ // regex?
24099                 for(var i = 0, len = ss.length; i < len; i++){
24100                     var o = ss[i];
24101                     if(value.test(o[property])){
24102                         data.push(o);
24103                     }
24104                 }
24105             } else{
24106                 return;
24107             }
24108             this.jsonData = data;
24109             this.refresh();
24110         }
24111     },
24112
24113 /**
24114  * Filter by a function. The passed function will be called with each
24115  * object in the current dataset. If the function returns true the value is kept,
24116  * otherwise it is filtered.
24117  * @param {Function} fn
24118  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24119  */
24120     filterBy : function(fn, scope){
24121         if(this.jsonData){
24122             var data = [];
24123             var ss = this.snapshot;
24124             for(var i = 0, len = ss.length; i < len; i++){
24125                 var o = ss[i];
24126                 if(fn.call(scope || this, o)){
24127                     data.push(o);
24128                 }
24129             }
24130             this.jsonData = data;
24131             this.refresh();
24132         }
24133     },
24134
24135 /**
24136  * Clears the current filter.
24137  */
24138     clearFilter : function(){
24139         if(this.snapshot && this.jsonData != this.snapshot){
24140             this.jsonData = this.snapshot;
24141             this.refresh();
24142         }
24143     },
24144
24145
24146 /**
24147  * Sorts the data for this view and refreshes it.
24148  * @param {String} property A property on your JSON objects to sort on
24149  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24150  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24151  */
24152     sort : function(property, dir, sortType){
24153         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24154         if(this.jsonData){
24155             var p = property;
24156             var dsc = dir && dir.toLowerCase() == "desc";
24157             var f = function(o1, o2){
24158                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24159                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24160                 ;
24161                 if(v1 < v2){
24162                     return dsc ? +1 : -1;
24163                 } else if(v1 > v2){
24164                     return dsc ? -1 : +1;
24165                 } else{
24166                     return 0;
24167                 }
24168             };
24169             this.jsonData.sort(f);
24170             this.refresh();
24171             if(this.jsonData != this.snapshot){
24172                 this.snapshot.sort(f);
24173             }
24174         }
24175     }
24176 });/*
24177  * Based on:
24178  * Ext JS Library 1.1.1
24179  * Copyright(c) 2006-2007, Ext JS, LLC.
24180  *
24181  * Originally Released Under LGPL - original licence link has changed is not relivant.
24182  *
24183  * Fork - LGPL
24184  * <script type="text/javascript">
24185  */
24186  
24187
24188 /**
24189  * @class Roo.ColorPalette
24190  * @extends Roo.Component
24191  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24192  * Here's an example of typical usage:
24193  * <pre><code>
24194 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24195 cp.render('my-div');
24196
24197 cp.on('select', function(palette, selColor){
24198     // do something with selColor
24199 });
24200 </code></pre>
24201  * @constructor
24202  * Create a new ColorPalette
24203  * @param {Object} config The config object
24204  */
24205 Roo.ColorPalette = function(config){
24206     Roo.ColorPalette.superclass.constructor.call(this, config);
24207     this.addEvents({
24208         /**
24209              * @event select
24210              * Fires when a color is selected
24211              * @param {ColorPalette} this
24212              * @param {String} color The 6-digit color hex code (without the # symbol)
24213              */
24214         select: true
24215     });
24216
24217     if(this.handler){
24218         this.on("select", this.handler, this.scope, true);
24219     }
24220 };
24221 Roo.extend(Roo.ColorPalette, Roo.Component, {
24222     /**
24223      * @cfg {String} itemCls
24224      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24225      */
24226     itemCls : "x-color-palette",
24227     /**
24228      * @cfg {String} value
24229      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24230      * the hex codes are case-sensitive.
24231      */
24232     value : null,
24233     clickEvent:'click',
24234     // private
24235     ctype: "Roo.ColorPalette",
24236
24237     /**
24238      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24239      */
24240     allowReselect : false,
24241
24242     /**
24243      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24244      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24245      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24246      * of colors with the width setting until the box is symmetrical.</p>
24247      * <p>You can override individual colors if needed:</p>
24248      * <pre><code>
24249 var cp = new Roo.ColorPalette();
24250 cp.colors[0] = "FF0000";  // change the first box to red
24251 </code></pre>
24252
24253 Or you can provide a custom array of your own for complete control:
24254 <pre><code>
24255 var cp = new Roo.ColorPalette();
24256 cp.colors = ["000000", "993300", "333300"];
24257 </code></pre>
24258      * @type Array
24259      */
24260     colors : [
24261         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24262         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24263         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24264         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24265         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24266     ],
24267
24268     // private
24269     onRender : function(container, position){
24270         var t = new Roo.MasterTemplate(
24271             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24272         );
24273         var c = this.colors;
24274         for(var i = 0, len = c.length; i < len; i++){
24275             t.add([c[i]]);
24276         }
24277         var el = document.createElement("div");
24278         el.className = this.itemCls;
24279         t.overwrite(el);
24280         container.dom.insertBefore(el, position);
24281         this.el = Roo.get(el);
24282         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24283         if(this.clickEvent != 'click'){
24284             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24285         }
24286     },
24287
24288     // private
24289     afterRender : function(){
24290         Roo.ColorPalette.superclass.afterRender.call(this);
24291         if(this.value){
24292             var s = this.value;
24293             this.value = null;
24294             this.select(s);
24295         }
24296     },
24297
24298     // private
24299     handleClick : function(e, t){
24300         e.preventDefault();
24301         if(!this.disabled){
24302             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24303             this.select(c.toUpperCase());
24304         }
24305     },
24306
24307     /**
24308      * Selects the specified color in the palette (fires the select event)
24309      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24310      */
24311     select : function(color){
24312         color = color.replace("#", "");
24313         if(color != this.value || this.allowReselect){
24314             var el = this.el;
24315             if(this.value){
24316                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24317             }
24318             el.child("a.color-"+color).addClass("x-color-palette-sel");
24319             this.value = color;
24320             this.fireEvent("select", this, color);
24321         }
24322     }
24323 });/*
24324  * Based on:
24325  * Ext JS Library 1.1.1
24326  * Copyright(c) 2006-2007, Ext JS, LLC.
24327  *
24328  * Originally Released Under LGPL - original licence link has changed is not relivant.
24329  *
24330  * Fork - LGPL
24331  * <script type="text/javascript">
24332  */
24333  
24334 /**
24335  * @class Roo.DatePicker
24336  * @extends Roo.Component
24337  * Simple date picker class.
24338  * @constructor
24339  * Create a new DatePicker
24340  * @param {Object} config The config object
24341  */
24342 Roo.DatePicker = function(config){
24343     Roo.DatePicker.superclass.constructor.call(this, config);
24344
24345     this.value = config && config.value ?
24346                  config.value.clearTime() : new Date().clearTime();
24347
24348     this.addEvents({
24349         /**
24350              * @event select
24351              * Fires when a date is selected
24352              * @param {DatePicker} this
24353              * @param {Date} date The selected date
24354              */
24355         select: true
24356     });
24357
24358     if(this.handler){
24359         this.on("select", this.handler,  this.scope || this);
24360     }
24361     // build the disabledDatesRE
24362     if(!this.disabledDatesRE && this.disabledDates){
24363         var dd = this.disabledDates;
24364         var re = "(?:";
24365         for(var i = 0; i < dd.length; i++){
24366             re += dd[i];
24367             if(i != dd.length-1) re += "|";
24368         }
24369         this.disabledDatesRE = new RegExp(re + ")");
24370     }
24371 };
24372
24373 Roo.extend(Roo.DatePicker, Roo.Component, {
24374     /**
24375      * @cfg {String} todayText
24376      * The text to display on the button that selects the current date (defaults to "Today")
24377      */
24378     todayText : "Today",
24379     /**
24380      * @cfg {String} okText
24381      * The text to display on the ok button
24382      */
24383     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24384     /**
24385      * @cfg {String} cancelText
24386      * The text to display on the cancel button
24387      */
24388     cancelText : "Cancel",
24389     /**
24390      * @cfg {String} todayTip
24391      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24392      */
24393     todayTip : "{0} (Spacebar)",
24394     /**
24395      * @cfg {Date} minDate
24396      * Minimum allowable date (JavaScript date object, defaults to null)
24397      */
24398     minDate : null,
24399     /**
24400      * @cfg {Date} maxDate
24401      * Maximum allowable date (JavaScript date object, defaults to null)
24402      */
24403     maxDate : null,
24404     /**
24405      * @cfg {String} minText
24406      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24407      */
24408     minText : "This date is before the minimum date",
24409     /**
24410      * @cfg {String} maxText
24411      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24412      */
24413     maxText : "This date is after the maximum date",
24414     /**
24415      * @cfg {String} format
24416      * The default date format string which can be overriden for localization support.  The format must be
24417      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24418      */
24419     format : "m/d/y",
24420     /**
24421      * @cfg {Array} disabledDays
24422      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24423      */
24424     disabledDays : null,
24425     /**
24426      * @cfg {String} disabledDaysText
24427      * The tooltip to display when the date falls on a disabled day (defaults to "")
24428      */
24429     disabledDaysText : "",
24430     /**
24431      * @cfg {RegExp} disabledDatesRE
24432      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24433      */
24434     disabledDatesRE : null,
24435     /**
24436      * @cfg {String} disabledDatesText
24437      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24438      */
24439     disabledDatesText : "",
24440     /**
24441      * @cfg {Boolean} constrainToViewport
24442      * True to constrain the date picker to the viewport (defaults to true)
24443      */
24444     constrainToViewport : true,
24445     /**
24446      * @cfg {Array} monthNames
24447      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24448      */
24449     monthNames : Date.monthNames,
24450     /**
24451      * @cfg {Array} dayNames
24452      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24453      */
24454     dayNames : Date.dayNames,
24455     /**
24456      * @cfg {String} nextText
24457      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24458      */
24459     nextText: 'Next Month (Control+Right)',
24460     /**
24461      * @cfg {String} prevText
24462      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24463      */
24464     prevText: 'Previous Month (Control+Left)',
24465     /**
24466      * @cfg {String} monthYearText
24467      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24468      */
24469     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24470     /**
24471      * @cfg {Number} startDay
24472      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24473      */
24474     startDay : 0,
24475     /**
24476      * @cfg {Bool} showClear
24477      * Show a clear button (usefull for date form elements that can be blank.)
24478      */
24479     
24480     showClear: false,
24481     
24482     /**
24483      * Sets the value of the date field
24484      * @param {Date} value The date to set
24485      */
24486     setValue : function(value){
24487         var old = this.value;
24488         this.value = value.clearTime(true);
24489         if(this.el){
24490             this.update(this.value);
24491         }
24492     },
24493
24494     /**
24495      * Gets the current selected value of the date field
24496      * @return {Date} The selected date
24497      */
24498     getValue : function(){
24499         return this.value;
24500     },
24501
24502     // private
24503     focus : function(){
24504         if(this.el){
24505             this.update(this.activeDate);
24506         }
24507     },
24508
24509     // private
24510     onRender : function(container, position){
24511         var m = [
24512              '<table cellspacing="0">',
24513                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
24514                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24515         var dn = this.dayNames;
24516         for(var i = 0; i < 7; i++){
24517             var d = this.startDay+i;
24518             if(d > 6){
24519                 d = d-7;
24520             }
24521             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24522         }
24523         m[m.length] = "</tr></thead><tbody><tr>";
24524         for(var i = 0; i < 42; i++) {
24525             if(i % 7 == 0 && i != 0){
24526                 m[m.length] = "</tr><tr>";
24527             }
24528             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24529         }
24530         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24531             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24532
24533         var el = document.createElement("div");
24534         el.className = "x-date-picker";
24535         el.innerHTML = m.join("");
24536
24537         container.dom.insertBefore(el, position);
24538
24539         this.el = Roo.get(el);
24540         this.eventEl = Roo.get(el.firstChild);
24541
24542         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24543             handler: this.showPrevMonth,
24544             scope: this,
24545             preventDefault:true,
24546             stopDefault:true
24547         });
24548
24549         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24550             handler: this.showNextMonth,
24551             scope: this,
24552             preventDefault:true,
24553             stopDefault:true
24554         });
24555
24556         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24557
24558         this.monthPicker = this.el.down('div.x-date-mp');
24559         this.monthPicker.enableDisplayMode('block');
24560         
24561         var kn = new Roo.KeyNav(this.eventEl, {
24562             "left" : function(e){
24563                 e.ctrlKey ?
24564                     this.showPrevMonth() :
24565                     this.update(this.activeDate.add("d", -1));
24566             },
24567
24568             "right" : function(e){
24569                 e.ctrlKey ?
24570                     this.showNextMonth() :
24571                     this.update(this.activeDate.add("d", 1));
24572             },
24573
24574             "up" : function(e){
24575                 e.ctrlKey ?
24576                     this.showNextYear() :
24577                     this.update(this.activeDate.add("d", -7));
24578             },
24579
24580             "down" : function(e){
24581                 e.ctrlKey ?
24582                     this.showPrevYear() :
24583                     this.update(this.activeDate.add("d", 7));
24584             },
24585
24586             "pageUp" : function(e){
24587                 this.showNextMonth();
24588             },
24589
24590             "pageDown" : function(e){
24591                 this.showPrevMonth();
24592             },
24593
24594             "enter" : function(e){
24595                 e.stopPropagation();
24596                 return true;
24597             },
24598
24599             scope : this
24600         });
24601
24602         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24603
24604         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24605
24606         this.el.unselectable();
24607         
24608         this.cells = this.el.select("table.x-date-inner tbody td");
24609         this.textNodes = this.el.query("table.x-date-inner tbody span");
24610
24611         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24612             text: "&#160;",
24613             tooltip: this.monthYearText
24614         });
24615
24616         this.mbtn.on('click', this.showMonthPicker, this);
24617         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24618
24619
24620         var today = (new Date()).dateFormat(this.format);
24621         
24622         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24623         if (this.showClear) {
24624             baseTb.add( new Roo.Toolbar.Fill());
24625         }
24626         baseTb.add({
24627             text: String.format(this.todayText, today),
24628             tooltip: String.format(this.todayTip, today),
24629             handler: this.selectToday,
24630             scope: this
24631         });
24632         
24633         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24634             
24635         //});
24636         if (this.showClear) {
24637             
24638             baseTb.add( new Roo.Toolbar.Fill());
24639             baseTb.add({
24640                 text: '&#160;',
24641                 cls: 'x-btn-icon x-btn-clear',
24642                 handler: function() {
24643                     //this.value = '';
24644                     this.fireEvent("select", this, '');
24645                 },
24646                 scope: this
24647             });
24648         }
24649         
24650         
24651         if(Roo.isIE){
24652             this.el.repaint();
24653         }
24654         this.update(this.value);
24655     },
24656
24657     createMonthPicker : function(){
24658         if(!this.monthPicker.dom.firstChild){
24659             var buf = ['<table border="0" cellspacing="0">'];
24660             for(var i = 0; i < 6; i++){
24661                 buf.push(
24662                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24663                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24664                     i == 0 ?
24665                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
24666                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24667                 );
24668             }
24669             buf.push(
24670                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24671                     this.okText,
24672                     '</button><button type="button" class="x-date-mp-cancel">',
24673                     this.cancelText,
24674                     '</button></td></tr>',
24675                 '</table>'
24676             );
24677             this.monthPicker.update(buf.join(''));
24678             this.monthPicker.on('click', this.onMonthClick, this);
24679             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24680
24681             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24682             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24683
24684             this.mpMonths.each(function(m, a, i){
24685                 i += 1;
24686                 if((i%2) == 0){
24687                     m.dom.xmonth = 5 + Math.round(i * .5);
24688                 }else{
24689                     m.dom.xmonth = Math.round((i-1) * .5);
24690                 }
24691             });
24692         }
24693     },
24694
24695     showMonthPicker : function(){
24696         this.createMonthPicker();
24697         var size = this.el.getSize();
24698         this.monthPicker.setSize(size);
24699         this.monthPicker.child('table').setSize(size);
24700
24701         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24702         this.updateMPMonth(this.mpSelMonth);
24703         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24704         this.updateMPYear(this.mpSelYear);
24705
24706         this.monthPicker.slideIn('t', {duration:.2});
24707     },
24708
24709     updateMPYear : function(y){
24710         this.mpyear = y;
24711         var ys = this.mpYears.elements;
24712         for(var i = 1; i <= 10; i++){
24713             var td = ys[i-1], y2;
24714             if((i%2) == 0){
24715                 y2 = y + Math.round(i * .5);
24716                 td.firstChild.innerHTML = y2;
24717                 td.xyear = y2;
24718             }else{
24719                 y2 = y - (5-Math.round(i * .5));
24720                 td.firstChild.innerHTML = y2;
24721                 td.xyear = y2;
24722             }
24723             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24724         }
24725     },
24726
24727     updateMPMonth : function(sm){
24728         this.mpMonths.each(function(m, a, i){
24729             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24730         });
24731     },
24732
24733     selectMPMonth: function(m){
24734         
24735     },
24736
24737     onMonthClick : function(e, t){
24738         e.stopEvent();
24739         var el = new Roo.Element(t), pn;
24740         if(el.is('button.x-date-mp-cancel')){
24741             this.hideMonthPicker();
24742         }
24743         else if(el.is('button.x-date-mp-ok')){
24744             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24745             this.hideMonthPicker();
24746         }
24747         else if(pn = el.up('td.x-date-mp-month', 2)){
24748             this.mpMonths.removeClass('x-date-mp-sel');
24749             pn.addClass('x-date-mp-sel');
24750             this.mpSelMonth = pn.dom.xmonth;
24751         }
24752         else if(pn = el.up('td.x-date-mp-year', 2)){
24753             this.mpYears.removeClass('x-date-mp-sel');
24754             pn.addClass('x-date-mp-sel');
24755             this.mpSelYear = pn.dom.xyear;
24756         }
24757         else if(el.is('a.x-date-mp-prev')){
24758             this.updateMPYear(this.mpyear-10);
24759         }
24760         else if(el.is('a.x-date-mp-next')){
24761             this.updateMPYear(this.mpyear+10);
24762         }
24763     },
24764
24765     onMonthDblClick : function(e, t){
24766         e.stopEvent();
24767         var el = new Roo.Element(t), pn;
24768         if(pn = el.up('td.x-date-mp-month', 2)){
24769             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24770             this.hideMonthPicker();
24771         }
24772         else if(pn = el.up('td.x-date-mp-year', 2)){
24773             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24774             this.hideMonthPicker();
24775         }
24776     },
24777
24778     hideMonthPicker : function(disableAnim){
24779         if(this.monthPicker){
24780             if(disableAnim === true){
24781                 this.monthPicker.hide();
24782             }else{
24783                 this.monthPicker.slideOut('t', {duration:.2});
24784             }
24785         }
24786     },
24787
24788     // private
24789     showPrevMonth : function(e){
24790         this.update(this.activeDate.add("mo", -1));
24791     },
24792
24793     // private
24794     showNextMonth : function(e){
24795         this.update(this.activeDate.add("mo", 1));
24796     },
24797
24798     // private
24799     showPrevYear : function(){
24800         this.update(this.activeDate.add("y", -1));
24801     },
24802
24803     // private
24804     showNextYear : function(){
24805         this.update(this.activeDate.add("y", 1));
24806     },
24807
24808     // private
24809     handleMouseWheel : function(e){
24810         var delta = e.getWheelDelta();
24811         if(delta > 0){
24812             this.showPrevMonth();
24813             e.stopEvent();
24814         } else if(delta < 0){
24815             this.showNextMonth();
24816             e.stopEvent();
24817         }
24818     },
24819
24820     // private
24821     handleDateClick : function(e, t){
24822         e.stopEvent();
24823         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24824             this.setValue(new Date(t.dateValue));
24825             this.fireEvent("select", this, this.value);
24826         }
24827     },
24828
24829     // private
24830     selectToday : function(){
24831         this.setValue(new Date().clearTime());
24832         this.fireEvent("select", this, this.value);
24833     },
24834
24835     // private
24836     update : function(date){
24837         var vd = this.activeDate;
24838         this.activeDate = date;
24839         if(vd && this.el){
24840             var t = date.getTime();
24841             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24842                 this.cells.removeClass("x-date-selected");
24843                 this.cells.each(function(c){
24844                    if(c.dom.firstChild.dateValue == t){
24845                        c.addClass("x-date-selected");
24846                        setTimeout(function(){
24847                             try{c.dom.firstChild.focus();}catch(e){}
24848                        }, 50);
24849                        return false;
24850                    }
24851                 });
24852                 return;
24853             }
24854         }
24855         var days = date.getDaysInMonth();
24856         var firstOfMonth = date.getFirstDateOfMonth();
24857         var startingPos = firstOfMonth.getDay()-this.startDay;
24858
24859         if(startingPos <= this.startDay){
24860             startingPos += 7;
24861         }
24862
24863         var pm = date.add("mo", -1);
24864         var prevStart = pm.getDaysInMonth()-startingPos;
24865
24866         var cells = this.cells.elements;
24867         var textEls = this.textNodes;
24868         days += startingPos;
24869
24870         // convert everything to numbers so it's fast
24871         var day = 86400000;
24872         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24873         var today = new Date().clearTime().getTime();
24874         var sel = date.clearTime().getTime();
24875         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24876         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24877         var ddMatch = this.disabledDatesRE;
24878         var ddText = this.disabledDatesText;
24879         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24880         var ddaysText = this.disabledDaysText;
24881         var format = this.format;
24882
24883         var setCellClass = function(cal, cell){
24884             cell.title = "";
24885             var t = d.getTime();
24886             cell.firstChild.dateValue = t;
24887             if(t == today){
24888                 cell.className += " x-date-today";
24889                 cell.title = cal.todayText;
24890             }
24891             if(t == sel){
24892                 cell.className += " x-date-selected";
24893                 setTimeout(function(){
24894                     try{cell.firstChild.focus();}catch(e){}
24895                 }, 50);
24896             }
24897             // disabling
24898             if(t < min) {
24899                 cell.className = " x-date-disabled";
24900                 cell.title = cal.minText;
24901                 return;
24902             }
24903             if(t > max) {
24904                 cell.className = " x-date-disabled";
24905                 cell.title = cal.maxText;
24906                 return;
24907             }
24908             if(ddays){
24909                 if(ddays.indexOf(d.getDay()) != -1){
24910                     cell.title = ddaysText;
24911                     cell.className = " x-date-disabled";
24912                 }
24913             }
24914             if(ddMatch && format){
24915                 var fvalue = d.dateFormat(format);
24916                 if(ddMatch.test(fvalue)){
24917                     cell.title = ddText.replace("%0", fvalue);
24918                     cell.className = " x-date-disabled";
24919                 }
24920             }
24921         };
24922
24923         var i = 0;
24924         for(; i < startingPos; i++) {
24925             textEls[i].innerHTML = (++prevStart);
24926             d.setDate(d.getDate()+1);
24927             cells[i].className = "x-date-prevday";
24928             setCellClass(this, cells[i]);
24929         }
24930         for(; i < days; i++){
24931             intDay = i - startingPos + 1;
24932             textEls[i].innerHTML = (intDay);
24933             d.setDate(d.getDate()+1);
24934             cells[i].className = "x-date-active";
24935             setCellClass(this, cells[i]);
24936         }
24937         var extraDays = 0;
24938         for(; i < 42; i++) {
24939              textEls[i].innerHTML = (++extraDays);
24940              d.setDate(d.getDate()+1);
24941              cells[i].className = "x-date-nextday";
24942              setCellClass(this, cells[i]);
24943         }
24944
24945         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24946
24947         if(!this.internalRender){
24948             var main = this.el.dom.firstChild;
24949             var w = main.offsetWidth;
24950             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24951             Roo.fly(main).setWidth(w);
24952             this.internalRender = true;
24953             // opera does not respect the auto grow header center column
24954             // then, after it gets a width opera refuses to recalculate
24955             // without a second pass
24956             if(Roo.isOpera && !this.secondPass){
24957                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24958                 this.secondPass = true;
24959                 this.update.defer(10, this, [date]);
24960             }
24961         }
24962     }
24963 });/*
24964  * Based on:
24965  * Ext JS Library 1.1.1
24966  * Copyright(c) 2006-2007, Ext JS, LLC.
24967  *
24968  * Originally Released Under LGPL - original licence link has changed is not relivant.
24969  *
24970  * Fork - LGPL
24971  * <script type="text/javascript">
24972  */
24973 /**
24974  * @class Roo.TabPanel
24975  * @extends Roo.util.Observable
24976  * A lightweight tab container.
24977  * <br><br>
24978  * Usage:
24979  * <pre><code>
24980 // basic tabs 1, built from existing content
24981 var tabs = new Roo.TabPanel("tabs1");
24982 tabs.addTab("script", "View Script");
24983 tabs.addTab("markup", "View Markup");
24984 tabs.activate("script");
24985
24986 // more advanced tabs, built from javascript
24987 var jtabs = new Roo.TabPanel("jtabs");
24988 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24989
24990 // set up the UpdateManager
24991 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24992 var updater = tab2.getUpdateManager();
24993 updater.setDefaultUrl("ajax1.htm");
24994 tab2.on('activate', updater.refresh, updater, true);
24995
24996 // Use setUrl for Ajax loading
24997 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24998 tab3.setUrl("ajax2.htm", null, true);
24999
25000 // Disabled tab
25001 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25002 tab4.disable();
25003
25004 jtabs.activate("jtabs-1");
25005  * </code></pre>
25006  * @constructor
25007  * Create a new TabPanel.
25008  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25009  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25010  */
25011 Roo.TabPanel = function(container, config){
25012     /**
25013     * The container element for this TabPanel.
25014     * @type Roo.Element
25015     */
25016     this.el = Roo.get(container, true);
25017     if(config){
25018         if(typeof config == "boolean"){
25019             this.tabPosition = config ? "bottom" : "top";
25020         }else{
25021             Roo.apply(this, config);
25022         }
25023     }
25024     if(this.tabPosition == "bottom"){
25025         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25026         this.el.addClass("x-tabs-bottom");
25027     }
25028     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25029     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25030     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25031     if(Roo.isIE){
25032         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25033     }
25034     if(this.tabPosition != "bottom"){
25035     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25036      * @type Roo.Element
25037      */
25038       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25039       this.el.addClass("x-tabs-top");
25040     }
25041     this.items = [];
25042
25043     this.bodyEl.setStyle("position", "relative");
25044
25045     this.active = null;
25046     this.activateDelegate = this.activate.createDelegate(this);
25047
25048     this.addEvents({
25049         /**
25050          * @event tabchange
25051          * Fires when the active tab changes
25052          * @param {Roo.TabPanel} this
25053          * @param {Roo.TabPanelItem} activePanel The new active tab
25054          */
25055         "tabchange": true,
25056         /**
25057          * @event beforetabchange
25058          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25059          * @param {Roo.TabPanel} this
25060          * @param {Object} e Set cancel to true on this object to cancel the tab change
25061          * @param {Roo.TabPanelItem} tab The tab being changed to
25062          */
25063         "beforetabchange" : true
25064     });
25065
25066     Roo.EventManager.onWindowResize(this.onResize, this);
25067     this.cpad = this.el.getPadding("lr");
25068     this.hiddenCount = 0;
25069
25070     Roo.TabPanel.superclass.constructor.call(this);
25071 };
25072
25073 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25074         /*
25075          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25076          */
25077     tabPosition : "top",
25078         /*
25079          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25080          */
25081     currentTabWidth : 0,
25082         /*
25083          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25084          */
25085     minTabWidth : 40,
25086         /*
25087          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25088          */
25089     maxTabWidth : 250,
25090         /*
25091          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25092          */
25093     preferredTabWidth : 175,
25094         /*
25095          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25096          */
25097     resizeTabs : false,
25098         /*
25099          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25100          */
25101     monitorResize : true,
25102
25103     /**
25104      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25105      * @param {String} id The id of the div to use <b>or create</b>
25106      * @param {String} text The text for the tab
25107      * @param {String} content (optional) Content to put in the TabPanelItem body
25108      * @param {Boolean} closable (optional) True to create a close icon on the tab
25109      * @return {Roo.TabPanelItem} The created TabPanelItem
25110      */
25111     addTab : function(id, text, content, closable){
25112         var item = new Roo.TabPanelItem(this, id, text, closable);
25113         this.addTabItem(item);
25114         if(content){
25115             item.setContent(content);
25116         }
25117         return item;
25118     },
25119
25120     /**
25121      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25122      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25123      * @return {Roo.TabPanelItem}
25124      */
25125     getTab : function(id){
25126         return this.items[id];
25127     },
25128
25129     /**
25130      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25131      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25132      */
25133     hideTab : function(id){
25134         var t = this.items[id];
25135         if(!t.isHidden()){
25136            t.setHidden(true);
25137            this.hiddenCount++;
25138            this.autoSizeTabs();
25139         }
25140     },
25141
25142     /**
25143      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25144      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25145      */
25146     unhideTab : function(id){
25147         var t = this.items[id];
25148         if(t.isHidden()){
25149            t.setHidden(false);
25150            this.hiddenCount--;
25151            this.autoSizeTabs();
25152         }
25153     },
25154
25155     /**
25156      * Adds an existing {@link Roo.TabPanelItem}.
25157      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25158      */
25159     addTabItem : function(item){
25160         this.items[item.id] = item;
25161         this.items.push(item);
25162         if(this.resizeTabs){
25163            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25164            this.autoSizeTabs();
25165         }else{
25166             item.autoSize();
25167         }
25168     },
25169
25170     /**
25171      * Removes a {@link Roo.TabPanelItem}.
25172      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25173      */
25174     removeTab : function(id){
25175         var items = this.items;
25176         var tab = items[id];
25177         if(!tab) { return; }
25178         var index = items.indexOf(tab);
25179         if(this.active == tab && items.length > 1){
25180             var newTab = this.getNextAvailable(index);
25181             if(newTab) {
25182                 newTab.activate();
25183             }
25184         }
25185         this.stripEl.dom.removeChild(tab.pnode.dom);
25186         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25187             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25188         }
25189         items.splice(index, 1);
25190         delete this.items[tab.id];
25191         tab.fireEvent("close", tab);
25192         tab.purgeListeners();
25193         this.autoSizeTabs();
25194     },
25195
25196     getNextAvailable : function(start){
25197         var items = this.items;
25198         var index = start;
25199         // look for a next tab that will slide over to
25200         // replace the one being removed
25201         while(index < items.length){
25202             var item = items[++index];
25203             if(item && !item.isHidden()){
25204                 return item;
25205             }
25206         }
25207         // if one isn't found select the previous tab (on the left)
25208         index = start;
25209         while(index >= 0){
25210             var item = items[--index];
25211             if(item && !item.isHidden()){
25212                 return item;
25213             }
25214         }
25215         return null;
25216     },
25217
25218     /**
25219      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25220      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25221      */
25222     disableTab : function(id){
25223         var tab = this.items[id];
25224         if(tab && this.active != tab){
25225             tab.disable();
25226         }
25227     },
25228
25229     /**
25230      * Enables a {@link Roo.TabPanelItem} that is disabled.
25231      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25232      */
25233     enableTab : function(id){
25234         var tab = this.items[id];
25235         tab.enable();
25236     },
25237
25238     /**
25239      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25240      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25241      * @return {Roo.TabPanelItem} The TabPanelItem.
25242      */
25243     activate : function(id){
25244         var tab = this.items[id];
25245         if(!tab){
25246             return null;
25247         }
25248         if(tab == this.active || tab.disabled){
25249             return tab;
25250         }
25251         var e = {};
25252         this.fireEvent("beforetabchange", this, e, tab);
25253         if(e.cancel !== true && !tab.disabled){
25254             if(this.active){
25255                 this.active.hide();
25256             }
25257             this.active = this.items[id];
25258             this.active.show();
25259             this.fireEvent("tabchange", this, this.active);
25260         }
25261         return tab;
25262     },
25263
25264     /**
25265      * Gets the active {@link Roo.TabPanelItem}.
25266      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25267      */
25268     getActiveTab : function(){
25269         return this.active;
25270     },
25271
25272     /**
25273      * Updates the tab body element to fit the height of the container element
25274      * for overflow scrolling
25275      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25276      */
25277     syncHeight : function(targetHeight){
25278         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25279         var bm = this.bodyEl.getMargins();
25280         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25281         this.bodyEl.setHeight(newHeight);
25282         return newHeight;
25283     },
25284
25285     onResize : function(){
25286         if(this.monitorResize){
25287             this.autoSizeTabs();
25288         }
25289     },
25290
25291     /**
25292      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25293      */
25294     beginUpdate : function(){
25295         this.updating = true;
25296     },
25297
25298     /**
25299      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25300      */
25301     endUpdate : function(){
25302         this.updating = false;
25303         this.autoSizeTabs();
25304     },
25305
25306     /**
25307      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25308      */
25309     autoSizeTabs : function(){
25310         var count = this.items.length;
25311         var vcount = count - this.hiddenCount;
25312         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25313         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25314         var availWidth = Math.floor(w / vcount);
25315         var b = this.stripBody;
25316         if(b.getWidth() > w){
25317             var tabs = this.items;
25318             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25319             if(availWidth < this.minTabWidth){
25320                 /*if(!this.sleft){    // incomplete scrolling code
25321                     this.createScrollButtons();
25322                 }
25323                 this.showScroll();
25324                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25325             }
25326         }else{
25327             if(this.currentTabWidth < this.preferredTabWidth){
25328                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25329             }
25330         }
25331     },
25332
25333     /**
25334      * Returns the number of tabs in this TabPanel.
25335      * @return {Number}
25336      */
25337      getCount : function(){
25338          return this.items.length;
25339      },
25340
25341     /**
25342      * Resizes all the tabs to the passed width
25343      * @param {Number} The new width
25344      */
25345     setTabWidth : function(width){
25346         this.currentTabWidth = width;
25347         for(var i = 0, len = this.items.length; i < len; i++) {
25348                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25349         }
25350     },
25351
25352     /**
25353      * Destroys this TabPanel
25354      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25355      */
25356     destroy : function(removeEl){
25357         Roo.EventManager.removeResizeListener(this.onResize, this);
25358         for(var i = 0, len = this.items.length; i < len; i++){
25359             this.items[i].purgeListeners();
25360         }
25361         if(removeEl === true){
25362             this.el.update("");
25363             this.el.remove();
25364         }
25365     }
25366 });
25367
25368 /**
25369  * @class Roo.TabPanelItem
25370  * @extends Roo.util.Observable
25371  * Represents an individual item (tab plus body) in a TabPanel.
25372  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25373  * @param {String} id The id of this TabPanelItem
25374  * @param {String} text The text for the tab of this TabPanelItem
25375  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25376  */
25377 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25378     /**
25379      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25380      * @type Roo.TabPanel
25381      */
25382     this.tabPanel = tabPanel;
25383     /**
25384      * The id for this TabPanelItem
25385      * @type String
25386      */
25387     this.id = id;
25388     /** @private */
25389     this.disabled = false;
25390     /** @private */
25391     this.text = text;
25392     /** @private */
25393     this.loaded = false;
25394     this.closable = closable;
25395
25396     /**
25397      * The body element for this TabPanelItem.
25398      * @type Roo.Element
25399      */
25400     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25401     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25402     this.bodyEl.setStyle("display", "block");
25403     this.bodyEl.setStyle("zoom", "1");
25404     this.hideAction();
25405
25406     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25407     /** @private */
25408     this.el = Roo.get(els.el, true);
25409     this.inner = Roo.get(els.inner, true);
25410     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25411     this.pnode = Roo.get(els.el.parentNode, true);
25412     this.el.on("mousedown", this.onTabMouseDown, this);
25413     this.el.on("click", this.onTabClick, this);
25414     /** @private */
25415     if(closable){
25416         var c = Roo.get(els.close, true);
25417         c.dom.title = this.closeText;
25418         c.addClassOnOver("close-over");
25419         c.on("click", this.closeClick, this);
25420      }
25421
25422     this.addEvents({
25423          /**
25424          * @event activate
25425          * Fires when this tab becomes the active tab.
25426          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25427          * @param {Roo.TabPanelItem} this
25428          */
25429         "activate": true,
25430         /**
25431          * @event beforeclose
25432          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25433          * @param {Roo.TabPanelItem} this
25434          * @param {Object} e Set cancel to true on this object to cancel the close.
25435          */
25436         "beforeclose": true,
25437         /**
25438          * @event close
25439          * Fires when this tab is closed.
25440          * @param {Roo.TabPanelItem} this
25441          */
25442          "close": true,
25443         /**
25444          * @event deactivate
25445          * Fires when this tab is no longer the active tab.
25446          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25447          * @param {Roo.TabPanelItem} this
25448          */
25449          "deactivate" : true
25450     });
25451     this.hidden = false;
25452
25453     Roo.TabPanelItem.superclass.constructor.call(this);
25454 };
25455
25456 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25457     purgeListeners : function(){
25458        Roo.util.Observable.prototype.purgeListeners.call(this);
25459        this.el.removeAllListeners();
25460     },
25461     /**
25462      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25463      */
25464     show : function(){
25465         this.pnode.addClass("on");
25466         this.showAction();
25467         if(Roo.isOpera){
25468             this.tabPanel.stripWrap.repaint();
25469         }
25470         this.fireEvent("activate", this.tabPanel, this);
25471     },
25472
25473     /**
25474      * Returns true if this tab is the active tab.
25475      * @return {Boolean}
25476      */
25477     isActive : function(){
25478         return this.tabPanel.getActiveTab() == this;
25479     },
25480
25481     /**
25482      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25483      */
25484     hide : function(){
25485         this.pnode.removeClass("on");
25486         this.hideAction();
25487         this.fireEvent("deactivate", this.tabPanel, this);
25488     },
25489
25490     hideAction : function(){
25491         this.bodyEl.hide();
25492         this.bodyEl.setStyle("position", "absolute");
25493         this.bodyEl.setLeft("-20000px");
25494         this.bodyEl.setTop("-20000px");
25495     },
25496
25497     showAction : function(){
25498         this.bodyEl.setStyle("position", "relative");
25499         this.bodyEl.setTop("");
25500         this.bodyEl.setLeft("");
25501         this.bodyEl.show();
25502     },
25503
25504     /**
25505      * Set the tooltip for the tab.
25506      * @param {String} tooltip The tab's tooltip
25507      */
25508     setTooltip : function(text){
25509         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25510             this.textEl.dom.qtip = text;
25511             this.textEl.dom.removeAttribute('title');
25512         }else{
25513             this.textEl.dom.title = text;
25514         }
25515     },
25516
25517     onTabClick : function(e){
25518         e.preventDefault();
25519         this.tabPanel.activate(this.id);
25520     },
25521
25522     onTabMouseDown : function(e){
25523         e.preventDefault();
25524         this.tabPanel.activate(this.id);
25525     },
25526
25527     getWidth : function(){
25528         return this.inner.getWidth();
25529     },
25530
25531     setWidth : function(width){
25532         var iwidth = width - this.pnode.getPadding("lr");
25533         this.inner.setWidth(iwidth);
25534         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25535         this.pnode.setWidth(width);
25536     },
25537
25538     /**
25539      * Show or hide the tab
25540      * @param {Boolean} hidden True to hide or false to show.
25541      */
25542     setHidden : function(hidden){
25543         this.hidden = hidden;
25544         this.pnode.setStyle("display", hidden ? "none" : "");
25545     },
25546
25547     /**
25548      * Returns true if this tab is "hidden"
25549      * @return {Boolean}
25550      */
25551     isHidden : function(){
25552         return this.hidden;
25553     },
25554
25555     /**
25556      * Returns the text for this tab
25557      * @return {String}
25558      */
25559     getText : function(){
25560         return this.text;
25561     },
25562
25563     autoSize : function(){
25564         //this.el.beginMeasure();
25565         this.textEl.setWidth(1);
25566         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25567         //this.el.endMeasure();
25568     },
25569
25570     /**
25571      * Sets the text for the tab (Note: this also sets the tooltip text)
25572      * @param {String} text The tab's text and tooltip
25573      */
25574     setText : function(text){
25575         this.text = text;
25576         this.textEl.update(text);
25577         this.setTooltip(text);
25578         if(!this.tabPanel.resizeTabs){
25579             this.autoSize();
25580         }
25581     },
25582     /**
25583      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25584      */
25585     activate : function(){
25586         this.tabPanel.activate(this.id);
25587     },
25588
25589     /**
25590      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25591      */
25592     disable : function(){
25593         if(this.tabPanel.active != this){
25594             this.disabled = true;
25595             this.pnode.addClass("disabled");
25596         }
25597     },
25598
25599     /**
25600      * Enables this TabPanelItem if it was previously disabled.
25601      */
25602     enable : function(){
25603         this.disabled = false;
25604         this.pnode.removeClass("disabled");
25605     },
25606
25607     /**
25608      * Sets the content for this TabPanelItem.
25609      * @param {String} content The content
25610      * @param {Boolean} loadScripts true to look for and load scripts
25611      */
25612     setContent : function(content, loadScripts){
25613         this.bodyEl.update(content, loadScripts);
25614     },
25615
25616     /**
25617      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25618      * @return {Roo.UpdateManager} The UpdateManager
25619      */
25620     getUpdateManager : function(){
25621         return this.bodyEl.getUpdateManager();
25622     },
25623
25624     /**
25625      * Set a URL to be used to load the content for this TabPanelItem.
25626      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25627      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
25628      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
25629      * @return {Roo.UpdateManager} The UpdateManager
25630      */
25631     setUrl : function(url, params, loadOnce){
25632         if(this.refreshDelegate){
25633             this.un('activate', this.refreshDelegate);
25634         }
25635         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25636         this.on("activate", this.refreshDelegate);
25637         return this.bodyEl.getUpdateManager();
25638     },
25639
25640     /** @private */
25641     _handleRefresh : function(url, params, loadOnce){
25642         if(!loadOnce || !this.loaded){
25643             var updater = this.bodyEl.getUpdateManager();
25644             updater.update(url, params, this._setLoaded.createDelegate(this));
25645         }
25646     },
25647
25648     /**
25649      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25650      *   Will fail silently if the setUrl method has not been called.
25651      *   This does not activate the panel, just updates its content.
25652      */
25653     refresh : function(){
25654         if(this.refreshDelegate){
25655            this.loaded = false;
25656            this.refreshDelegate();
25657         }
25658     },
25659
25660     /** @private */
25661     _setLoaded : function(){
25662         this.loaded = true;
25663     },
25664
25665     /** @private */
25666     closeClick : function(e){
25667         var o = {};
25668         e.stopEvent();
25669         this.fireEvent("beforeclose", this, o);
25670         if(o.cancel !== true){
25671             this.tabPanel.removeTab(this.id);
25672         }
25673     },
25674     /**
25675      * The text displayed in the tooltip for the close icon.
25676      * @type String
25677      */
25678     closeText : "Close this tab"
25679 });
25680
25681 /** @private */
25682 Roo.TabPanel.prototype.createStrip = function(container){
25683     var strip = document.createElement("div");
25684     strip.className = "x-tabs-wrap";
25685     container.appendChild(strip);
25686     return strip;
25687 };
25688 /** @private */
25689 Roo.TabPanel.prototype.createStripList = function(strip){
25690     // div wrapper for retard IE
25691     strip.innerHTML = '<div class="x-tabs-strip-wrap"><table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr></tr></tbody></table></div>';
25692     return strip.firstChild.firstChild.firstChild.firstChild;
25693 };
25694 /** @private */
25695 Roo.TabPanel.prototype.createBody = function(container){
25696     var body = document.createElement("div");
25697     Roo.id(body, "tab-body");
25698     Roo.fly(body).addClass("x-tabs-body");
25699     container.appendChild(body);
25700     return body;
25701 };
25702 /** @private */
25703 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25704     var body = Roo.getDom(id);
25705     if(!body){
25706         body = document.createElement("div");
25707         body.id = id;
25708     }
25709     Roo.fly(body).addClass("x-tabs-item-body");
25710     bodyEl.insertBefore(body, bodyEl.firstChild);
25711     return body;
25712 };
25713 /** @private */
25714 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25715     var td = document.createElement("td");
25716     stripEl.appendChild(td);
25717     if(closable){
25718         td.className = "x-tabs-closable";
25719         if(!this.closeTpl){
25720             this.closeTpl = new Roo.Template(
25721                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25722                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25723                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25724             );
25725         }
25726         var el = this.closeTpl.overwrite(td, {"text": text});
25727         var close = el.getElementsByTagName("div")[0];
25728         var inner = el.getElementsByTagName("em")[0];
25729         return {"el": el, "close": close, "inner": inner};
25730     } else {
25731         if(!this.tabTpl){
25732             this.tabTpl = new Roo.Template(
25733                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25734                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25735             );
25736         }
25737         var el = this.tabTpl.overwrite(td, {"text": text});
25738         var inner = el.getElementsByTagName("em")[0];
25739         return {"el": el, "inner": inner};
25740     }
25741 };/*
25742  * Based on:
25743  * Ext JS Library 1.1.1
25744  * Copyright(c) 2006-2007, Ext JS, LLC.
25745  *
25746  * Originally Released Under LGPL - original licence link has changed is not relivant.
25747  *
25748  * Fork - LGPL
25749  * <script type="text/javascript">
25750  */
25751
25752 /**
25753  * @class Roo.Button
25754  * @extends Roo.util.Observable
25755  * Simple Button class
25756  * @cfg {String} text The button text
25757  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25758  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25759  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25760  * @cfg {Object} scope The scope of the handler
25761  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25762  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25763  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25764  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25765  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25766  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25767    applies if enableToggle = true)
25768  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25769  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25770   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25771  * @constructor
25772  * Create a new button
25773  * @param {Object} config The config object
25774  */
25775 Roo.Button = function(renderTo, config)
25776 {
25777     if (!config) {
25778         config = renderTo;
25779         renderTo = config.renderTo || false;
25780     }
25781     
25782     Roo.apply(this, config);
25783     this.addEvents({
25784         /**
25785              * @event click
25786              * Fires when this button is clicked
25787              * @param {Button} this
25788              * @param {EventObject} e The click event
25789              */
25790             "click" : true,
25791         /**
25792              * @event toggle
25793              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25794              * @param {Button} this
25795              * @param {Boolean} pressed
25796              */
25797             "toggle" : true,
25798         /**
25799              * @event mouseover
25800              * Fires when the mouse hovers over the button
25801              * @param {Button} this
25802              * @param {Event} e The event object
25803              */
25804         'mouseover' : true,
25805         /**
25806              * @event mouseout
25807              * Fires when the mouse exits the button
25808              * @param {Button} this
25809              * @param {Event} e The event object
25810              */
25811         'mouseout': true,
25812          /**
25813              * @event render
25814              * Fires when the button is rendered
25815              * @param {Button} this
25816              */
25817         'render': true
25818     });
25819     if(this.menu){
25820         this.menu = Roo.menu.MenuMgr.get(this.menu);
25821     }
25822     // register listeners first!!  - so render can be captured..
25823     Roo.util.Observable.call(this);
25824     if(renderTo){
25825         this.render(renderTo);
25826     }
25827     
25828   
25829 };
25830
25831 Roo.extend(Roo.Button, Roo.util.Observable, {
25832     /**
25833      * 
25834      */
25835     
25836     /**
25837      * Read-only. True if this button is hidden
25838      * @type Boolean
25839      */
25840     hidden : false,
25841     /**
25842      * Read-only. True if this button is disabled
25843      * @type Boolean
25844      */
25845     disabled : false,
25846     /**
25847      * Read-only. True if this button is pressed (only if enableToggle = true)
25848      * @type Boolean
25849      */
25850     pressed : false,
25851
25852     /**
25853      * @cfg {Number} tabIndex 
25854      * The DOM tabIndex for this button (defaults to undefined)
25855      */
25856     tabIndex : undefined,
25857
25858     /**
25859      * @cfg {Boolean} enableToggle
25860      * True to enable pressed/not pressed toggling (defaults to false)
25861      */
25862     enableToggle: false,
25863     /**
25864      * @cfg {Mixed} menu
25865      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25866      */
25867     menu : undefined,
25868     /**
25869      * @cfg {String} menuAlign
25870      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25871      */
25872     menuAlign : "tl-bl?",
25873
25874     /**
25875      * @cfg {String} iconCls
25876      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25877      */
25878     iconCls : undefined,
25879     /**
25880      * @cfg {String} type
25881      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25882      */
25883     type : 'button',
25884
25885     // private
25886     menuClassTarget: 'tr',
25887
25888     /**
25889      * @cfg {String} clickEvent
25890      * The type of event to map to the button's event handler (defaults to 'click')
25891      */
25892     clickEvent : 'click',
25893
25894     /**
25895      * @cfg {Boolean} handleMouseEvents
25896      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25897      */
25898     handleMouseEvents : true,
25899
25900     /**
25901      * @cfg {String} tooltipType
25902      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25903      */
25904     tooltipType : 'qtip',
25905
25906     /**
25907      * @cfg {String} cls
25908      * A CSS class to apply to the button's main element.
25909      */
25910     
25911     /**
25912      * @cfg {Roo.Template} template (Optional)
25913      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25914      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25915      * require code modifications if required elements (e.g. a button) aren't present.
25916      */
25917
25918     // private
25919     render : function(renderTo){
25920         var btn;
25921         if(this.hideParent){
25922             this.parentEl = Roo.get(renderTo);
25923         }
25924         if(!this.dhconfig){
25925             if(!this.template){
25926                 if(!Roo.Button.buttonTemplate){
25927                     // hideous table template
25928                     Roo.Button.buttonTemplate = new Roo.Template(
25929                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25930                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
25931                         "</tr></tbody></table>");
25932                 }
25933                 this.template = Roo.Button.buttonTemplate;
25934             }
25935             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25936             var btnEl = btn.child("button:first");
25937             btnEl.on('focus', this.onFocus, this);
25938             btnEl.on('blur', this.onBlur, this);
25939             if(this.cls){
25940                 btn.addClass(this.cls);
25941             }
25942             if(this.icon){
25943                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25944             }
25945             if(this.iconCls){
25946                 btnEl.addClass(this.iconCls);
25947                 if(!this.cls){
25948                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25949                 }
25950             }
25951             if(this.tabIndex !== undefined){
25952                 btnEl.dom.tabIndex = this.tabIndex;
25953             }
25954             if(this.tooltip){
25955                 if(typeof this.tooltip == 'object'){
25956                     Roo.QuickTips.tips(Roo.apply({
25957                           target: btnEl.id
25958                     }, this.tooltip));
25959                 } else {
25960                     btnEl.dom[this.tooltipType] = this.tooltip;
25961                 }
25962             }
25963         }else{
25964             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25965         }
25966         this.el = btn;
25967         if(this.id){
25968             this.el.dom.id = this.el.id = this.id;
25969         }
25970         if(this.menu){
25971             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25972             this.menu.on("show", this.onMenuShow, this);
25973             this.menu.on("hide", this.onMenuHide, this);
25974         }
25975         btn.addClass("x-btn");
25976         if(Roo.isIE && !Roo.isIE7){
25977             this.autoWidth.defer(1, this);
25978         }else{
25979             this.autoWidth();
25980         }
25981         if(this.handleMouseEvents){
25982             btn.on("mouseover", this.onMouseOver, this);
25983             btn.on("mouseout", this.onMouseOut, this);
25984             btn.on("mousedown", this.onMouseDown, this);
25985         }
25986         btn.on(this.clickEvent, this.onClick, this);
25987         //btn.on("mouseup", this.onMouseUp, this);
25988         if(this.hidden){
25989             this.hide();
25990         }
25991         if(this.disabled){
25992             this.disable();
25993         }
25994         Roo.ButtonToggleMgr.register(this);
25995         if(this.pressed){
25996             this.el.addClass("x-btn-pressed");
25997         }
25998         if(this.repeat){
25999             var repeater = new Roo.util.ClickRepeater(btn,
26000                 typeof this.repeat == "object" ? this.repeat : {}
26001             );
26002             repeater.on("click", this.onClick,  this);
26003         }
26004         
26005         this.fireEvent('render', this);
26006         
26007     },
26008     /**
26009      * Returns the button's underlying element
26010      * @return {Roo.Element} The element
26011      */
26012     getEl : function(){
26013         return this.el;  
26014     },
26015     
26016     /**
26017      * Destroys this Button and removes any listeners.
26018      */
26019     destroy : function(){
26020         Roo.ButtonToggleMgr.unregister(this);
26021         this.el.removeAllListeners();
26022         this.purgeListeners();
26023         this.el.remove();
26024     },
26025
26026     // private
26027     autoWidth : function(){
26028         if(this.el){
26029             this.el.setWidth("auto");
26030             if(Roo.isIE7 && Roo.isStrict){
26031                 var ib = this.el.child('button');
26032                 if(ib && ib.getWidth() > 20){
26033                     ib.clip();
26034                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26035                 }
26036             }
26037             if(this.minWidth){
26038                 if(this.hidden){
26039                     this.el.beginMeasure();
26040                 }
26041                 if(this.el.getWidth() < this.minWidth){
26042                     this.el.setWidth(this.minWidth);
26043                 }
26044                 if(this.hidden){
26045                     this.el.endMeasure();
26046                 }
26047             }
26048         }
26049     },
26050
26051     /**
26052      * Assigns this button's click handler
26053      * @param {Function} handler The function to call when the button is clicked
26054      * @param {Object} scope (optional) Scope for the function passed in
26055      */
26056     setHandler : function(handler, scope){
26057         this.handler = handler;
26058         this.scope = scope;  
26059     },
26060     
26061     /**
26062      * Sets this button's text
26063      * @param {String} text The button text
26064      */
26065     setText : function(text){
26066         this.text = text;
26067         if(this.el){
26068             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26069         }
26070         this.autoWidth();
26071     },
26072     
26073     /**
26074      * Gets the text for this button
26075      * @return {String} The button text
26076      */
26077     getText : function(){
26078         return this.text;  
26079     },
26080     
26081     /**
26082      * Show this button
26083      */
26084     show: function(){
26085         this.hidden = false;
26086         if(this.el){
26087             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26088         }
26089     },
26090     
26091     /**
26092      * Hide this button
26093      */
26094     hide: function(){
26095         this.hidden = true;
26096         if(this.el){
26097             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26098         }
26099     },
26100     
26101     /**
26102      * Convenience function for boolean show/hide
26103      * @param {Boolean} visible True to show, false to hide
26104      */
26105     setVisible: function(visible){
26106         if(visible) {
26107             this.show();
26108         }else{
26109             this.hide();
26110         }
26111     },
26112     
26113     /**
26114      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26115      * @param {Boolean} state (optional) Force a particular state
26116      */
26117     toggle : function(state){
26118         state = state === undefined ? !this.pressed : state;
26119         if(state != this.pressed){
26120             if(state){
26121                 this.el.addClass("x-btn-pressed");
26122                 this.pressed = true;
26123                 this.fireEvent("toggle", this, true);
26124             }else{
26125                 this.el.removeClass("x-btn-pressed");
26126                 this.pressed = false;
26127                 this.fireEvent("toggle", this, false);
26128             }
26129             if(this.toggleHandler){
26130                 this.toggleHandler.call(this.scope || this, this, state);
26131             }
26132         }
26133     },
26134     
26135     /**
26136      * Focus the button
26137      */
26138     focus : function(){
26139         this.el.child('button:first').focus();
26140     },
26141     
26142     /**
26143      * Disable this button
26144      */
26145     disable : function(){
26146         if(this.el){
26147             this.el.addClass("x-btn-disabled");
26148         }
26149         this.disabled = true;
26150     },
26151     
26152     /**
26153      * Enable this button
26154      */
26155     enable : function(){
26156         if(this.el){
26157             this.el.removeClass("x-btn-disabled");
26158         }
26159         this.disabled = false;
26160     },
26161
26162     /**
26163      * Convenience function for boolean enable/disable
26164      * @param {Boolean} enabled True to enable, false to disable
26165      */
26166     setDisabled : function(v){
26167         this[v !== true ? "enable" : "disable"]();
26168     },
26169
26170     // private
26171     onClick : function(e){
26172         if(e){
26173             e.preventDefault();
26174         }
26175         if(e.button != 0){
26176             return;
26177         }
26178         if(!this.disabled){
26179             if(this.enableToggle){
26180                 this.toggle();
26181             }
26182             if(this.menu && !this.menu.isVisible()){
26183                 this.menu.show(this.el, this.menuAlign);
26184             }
26185             this.fireEvent("click", this, e);
26186             if(this.handler){
26187                 this.el.removeClass("x-btn-over");
26188                 this.handler.call(this.scope || this, this, e);
26189             }
26190         }
26191     },
26192     // private
26193     onMouseOver : function(e){
26194         if(!this.disabled){
26195             this.el.addClass("x-btn-over");
26196             this.fireEvent('mouseover', this, e);
26197         }
26198     },
26199     // private
26200     onMouseOut : function(e){
26201         if(!e.within(this.el,  true)){
26202             this.el.removeClass("x-btn-over");
26203             this.fireEvent('mouseout', this, e);
26204         }
26205     },
26206     // private
26207     onFocus : function(e){
26208         if(!this.disabled){
26209             this.el.addClass("x-btn-focus");
26210         }
26211     },
26212     // private
26213     onBlur : function(e){
26214         this.el.removeClass("x-btn-focus");
26215     },
26216     // private
26217     onMouseDown : function(e){
26218         if(!this.disabled && e.button == 0){
26219             this.el.addClass("x-btn-click");
26220             Roo.get(document).on('mouseup', this.onMouseUp, this);
26221         }
26222     },
26223     // private
26224     onMouseUp : function(e){
26225         if(e.button == 0){
26226             this.el.removeClass("x-btn-click");
26227             Roo.get(document).un('mouseup', this.onMouseUp, this);
26228         }
26229     },
26230     // private
26231     onMenuShow : function(e){
26232         this.el.addClass("x-btn-menu-active");
26233     },
26234     // private
26235     onMenuHide : function(e){
26236         this.el.removeClass("x-btn-menu-active");
26237     }   
26238 });
26239
26240 // Private utility class used by Button
26241 Roo.ButtonToggleMgr = function(){
26242    var groups = {};
26243    
26244    function toggleGroup(btn, state){
26245        if(state){
26246            var g = groups[btn.toggleGroup];
26247            for(var i = 0, l = g.length; i < l; i++){
26248                if(g[i] != btn){
26249                    g[i].toggle(false);
26250                }
26251            }
26252        }
26253    }
26254    
26255    return {
26256        register : function(btn){
26257            if(!btn.toggleGroup){
26258                return;
26259            }
26260            var g = groups[btn.toggleGroup];
26261            if(!g){
26262                g = groups[btn.toggleGroup] = [];
26263            }
26264            g.push(btn);
26265            btn.on("toggle", toggleGroup);
26266        },
26267        
26268        unregister : function(btn){
26269            if(!btn.toggleGroup){
26270                return;
26271            }
26272            var g = groups[btn.toggleGroup];
26273            if(g){
26274                g.remove(btn);
26275                btn.un("toggle", toggleGroup);
26276            }
26277        }
26278    };
26279 }();/*
26280  * Based on:
26281  * Ext JS Library 1.1.1
26282  * Copyright(c) 2006-2007, Ext JS, LLC.
26283  *
26284  * Originally Released Under LGPL - original licence link has changed is not relivant.
26285  *
26286  * Fork - LGPL
26287  * <script type="text/javascript">
26288  */
26289  
26290 /**
26291  * @class Roo.SplitButton
26292  * @extends Roo.Button
26293  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26294  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26295  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26296  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26297  * @cfg {String} arrowTooltip The title attribute of the arrow
26298  * @constructor
26299  * Create a new menu button
26300  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26301  * @param {Object} config The config object
26302  */
26303 Roo.SplitButton = function(renderTo, config){
26304     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26305     /**
26306      * @event arrowclick
26307      * Fires when this button's arrow is clicked
26308      * @param {SplitButton} this
26309      * @param {EventObject} e The click event
26310      */
26311     this.addEvents({"arrowclick":true});
26312 };
26313
26314 Roo.extend(Roo.SplitButton, Roo.Button, {
26315     render : function(renderTo){
26316         // this is one sweet looking template!
26317         var tpl = new Roo.Template(
26318             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26319             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26320             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
26321             "</tbody></table></td><td>",
26322             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26323             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
26324             "</tbody></table></td></tr></table>"
26325         );
26326         var btn = tpl.append(renderTo, [this.text, this.type], true);
26327         var btnEl = btn.child("button");
26328         if(this.cls){
26329             btn.addClass(this.cls);
26330         }
26331         if(this.icon){
26332             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26333         }
26334         if(this.iconCls){
26335             btnEl.addClass(this.iconCls);
26336             if(!this.cls){
26337                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26338             }
26339         }
26340         this.el = btn;
26341         if(this.handleMouseEvents){
26342             btn.on("mouseover", this.onMouseOver, this);
26343             btn.on("mouseout", this.onMouseOut, this);
26344             btn.on("mousedown", this.onMouseDown, this);
26345             btn.on("mouseup", this.onMouseUp, this);
26346         }
26347         btn.on(this.clickEvent, this.onClick, this);
26348         if(this.tooltip){
26349             if(typeof this.tooltip == 'object'){
26350                 Roo.QuickTips.tips(Roo.apply({
26351                       target: btnEl.id
26352                 }, this.tooltip));
26353             } else {
26354                 btnEl.dom[this.tooltipType] = this.tooltip;
26355             }
26356         }
26357         if(this.arrowTooltip){
26358             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26359         }
26360         if(this.hidden){
26361             this.hide();
26362         }
26363         if(this.disabled){
26364             this.disable();
26365         }
26366         if(this.pressed){
26367             this.el.addClass("x-btn-pressed");
26368         }
26369         if(Roo.isIE && !Roo.isIE7){
26370             this.autoWidth.defer(1, this);
26371         }else{
26372             this.autoWidth();
26373         }
26374         if(this.menu){
26375             this.menu.on("show", this.onMenuShow, this);
26376             this.menu.on("hide", this.onMenuHide, this);
26377         }
26378         this.fireEvent('render', this);
26379     },
26380
26381     // private
26382     autoWidth : function(){
26383         if(this.el){
26384             var tbl = this.el.child("table:first");
26385             var tbl2 = this.el.child("table:last");
26386             this.el.setWidth("auto");
26387             tbl.setWidth("auto");
26388             if(Roo.isIE7 && Roo.isStrict){
26389                 var ib = this.el.child('button:first');
26390                 if(ib && ib.getWidth() > 20){
26391                     ib.clip();
26392                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26393                 }
26394             }
26395             if(this.minWidth){
26396                 if(this.hidden){
26397                     this.el.beginMeasure();
26398                 }
26399                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26400                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26401                 }
26402                 if(this.hidden){
26403                     this.el.endMeasure();
26404                 }
26405             }
26406             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26407         } 
26408     },
26409     /**
26410      * Sets this button's click handler
26411      * @param {Function} handler The function to call when the button is clicked
26412      * @param {Object} scope (optional) Scope for the function passed above
26413      */
26414     setHandler : function(handler, scope){
26415         this.handler = handler;
26416         this.scope = scope;  
26417     },
26418     
26419     /**
26420      * Sets this button's arrow click handler
26421      * @param {Function} handler The function to call when the arrow is clicked
26422      * @param {Object} scope (optional) Scope for the function passed above
26423      */
26424     setArrowHandler : function(handler, scope){
26425         this.arrowHandler = handler;
26426         this.scope = scope;  
26427     },
26428     
26429     /**
26430      * Focus the button
26431      */
26432     focus : function(){
26433         if(this.el){
26434             this.el.child("button:first").focus();
26435         }
26436     },
26437
26438     // private
26439     onClick : function(e){
26440         e.preventDefault();
26441         if(!this.disabled){
26442             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26443                 if(this.menu && !this.menu.isVisible()){
26444                     this.menu.show(this.el, this.menuAlign);
26445                 }
26446                 this.fireEvent("arrowclick", this, e);
26447                 if(this.arrowHandler){
26448                     this.arrowHandler.call(this.scope || this, this, e);
26449                 }
26450             }else{
26451                 this.fireEvent("click", this, e);
26452                 if(this.handler){
26453                     this.handler.call(this.scope || this, this, e);
26454                 }
26455             }
26456         }
26457     },
26458     // private
26459     onMouseDown : function(e){
26460         if(!this.disabled){
26461             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26462         }
26463     },
26464     // private
26465     onMouseUp : function(e){
26466         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26467     }   
26468 });
26469
26470
26471 // backwards compat
26472 Roo.MenuButton = Roo.SplitButton;/*
26473  * Based on:
26474  * Ext JS Library 1.1.1
26475  * Copyright(c) 2006-2007, Ext JS, LLC.
26476  *
26477  * Originally Released Under LGPL - original licence link has changed is not relivant.
26478  *
26479  * Fork - LGPL
26480  * <script type="text/javascript">
26481  */
26482
26483 /**
26484  * @class Roo.Toolbar
26485  * Basic Toolbar class.
26486  * @constructor
26487  * Creates a new Toolbar
26488  * @param {Object} config The config object
26489  */ 
26490 Roo.Toolbar = function(container, buttons, config)
26491 {
26492     /// old consturctor format still supported..
26493     if(container instanceof Array){ // omit the container for later rendering
26494         buttons = container;
26495         config = buttons;
26496         container = null;
26497     }
26498     if (typeof(container) == 'object' && container.xtype) {
26499         config = container;
26500         container = config.container;
26501         buttons = config.buttons; // not really - use items!!
26502     }
26503     var xitems = [];
26504     if (config && config.items) {
26505         xitems = config.items;
26506         delete config.items;
26507     }
26508     Roo.apply(this, config);
26509     this.buttons = buttons;
26510     
26511     if(container){
26512         this.render(container);
26513     }
26514     Roo.each(xitems, function(b) {
26515         this.add(b);
26516     }, this);
26517     
26518 };
26519
26520 Roo.Toolbar.prototype = {
26521     /**
26522      * @cfg {Roo.data.Store} items
26523      * array of button configs or elements to add
26524      */
26525     
26526     /**
26527      * @cfg {String/HTMLElement/Element} container
26528      * The id or element that will contain the toolbar
26529      */
26530     // private
26531     render : function(ct){
26532         this.el = Roo.get(ct);
26533         if(this.cls){
26534             this.el.addClass(this.cls);
26535         }
26536         // using a table allows for vertical alignment
26537         // 100% width is needed by Safari...
26538         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26539         this.tr = this.el.child("tr", true);
26540         var autoId = 0;
26541         this.items = new Roo.util.MixedCollection(false, function(o){
26542             return o.id || ("item" + (++autoId));
26543         });
26544         if(this.buttons){
26545             this.add.apply(this, this.buttons);
26546             delete this.buttons;
26547         }
26548     },
26549
26550     /**
26551      * Adds element(s) to the toolbar -- this function takes a variable number of 
26552      * arguments of mixed type and adds them to the toolbar.
26553      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26554      * <ul>
26555      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26556      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26557      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26558      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26559      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26560      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26561      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26562      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26563      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26564      * </ul>
26565      * @param {Mixed} arg2
26566      * @param {Mixed} etc.
26567      */
26568     add : function(){
26569         var a = arguments, l = a.length;
26570         for(var i = 0; i < l; i++){
26571             this._add(a[i]);
26572         }
26573     },
26574     // private..
26575     _add : function(el) {
26576         
26577         if (el.xtype) {
26578             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26579         }
26580         
26581         if (el.applyTo){ // some kind of form field
26582             return this.addField(el);
26583         } 
26584         if (el.render){ // some kind of Toolbar.Item
26585             return this.addItem(el);
26586         }
26587         if (typeof el == "string"){ // string
26588             if(el == "separator" || el == "-"){
26589                 return this.addSeparator();
26590             }
26591             if (el == " "){
26592                 return this.addSpacer();
26593             }
26594             if(el == "->"){
26595                 return this.addFill();
26596             }
26597             return this.addText(el);
26598             
26599         }
26600         if(el.tagName){ // element
26601             return this.addElement(el);
26602         }
26603         if(typeof el == "object"){ // must be button config?
26604             return this.addButton(el);
26605         }
26606         // and now what?!?!
26607         return false;
26608         
26609     },
26610     
26611     /**
26612      * Add an Xtype element
26613      * @param {Object} xtype Xtype Object
26614      * @return {Object} created Object
26615      */
26616     addxtype : function(e){
26617         return this.add(e);  
26618     },
26619     
26620     /**
26621      * Returns the Element for this toolbar.
26622      * @return {Roo.Element}
26623      */
26624     getEl : function(){
26625         return this.el;  
26626     },
26627     
26628     /**
26629      * Adds a separator
26630      * @return {Roo.Toolbar.Item} The separator item
26631      */
26632     addSeparator : function(){
26633         return this.addItem(new Roo.Toolbar.Separator());
26634     },
26635
26636     /**
26637      * Adds a spacer element
26638      * @return {Roo.Toolbar.Spacer} The spacer item
26639      */
26640     addSpacer : function(){
26641         return this.addItem(new Roo.Toolbar.Spacer());
26642     },
26643
26644     /**
26645      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26646      * @return {Roo.Toolbar.Fill} The fill item
26647      */
26648     addFill : function(){
26649         return this.addItem(new Roo.Toolbar.Fill());
26650     },
26651
26652     /**
26653      * Adds any standard HTML element to the toolbar
26654      * @param {String/HTMLElement/Element} el The element or id of the element to add
26655      * @return {Roo.Toolbar.Item} The element's item
26656      */
26657     addElement : function(el){
26658         return this.addItem(new Roo.Toolbar.Item(el));
26659     },
26660     /**
26661      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26662      * @type Roo.util.MixedCollection  
26663      */
26664     items : false,
26665      
26666     /**
26667      * Adds any Toolbar.Item or subclass
26668      * @param {Roo.Toolbar.Item} item
26669      * @return {Roo.Toolbar.Item} The item
26670      */
26671     addItem : function(item){
26672         var td = this.nextBlock();
26673         item.render(td);
26674         this.items.add(item);
26675         return item;
26676     },
26677     
26678     /**
26679      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26680      * @param {Object/Array} config A button config or array of configs
26681      * @return {Roo.Toolbar.Button/Array}
26682      */
26683     addButton : function(config){
26684         if(config instanceof Array){
26685             var buttons = [];
26686             for(var i = 0, len = config.length; i < len; i++) {
26687                 buttons.push(this.addButton(config[i]));
26688             }
26689             return buttons;
26690         }
26691         var b = config;
26692         if(!(config instanceof Roo.Toolbar.Button)){
26693             b = config.split ?
26694                 new Roo.Toolbar.SplitButton(config) :
26695                 new Roo.Toolbar.Button(config);
26696         }
26697         var td = this.nextBlock();
26698         b.render(td);
26699         this.items.add(b);
26700         return b;
26701     },
26702     
26703     /**
26704      * Adds text to the toolbar
26705      * @param {String} text The text to add
26706      * @return {Roo.Toolbar.Item} The element's item
26707      */
26708     addText : function(text){
26709         return this.addItem(new Roo.Toolbar.TextItem(text));
26710     },
26711     
26712     /**
26713      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26714      * @param {Number} index The index where the item is to be inserted
26715      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26716      * @return {Roo.Toolbar.Button/Item}
26717      */
26718     insertButton : function(index, item){
26719         if(item instanceof Array){
26720             var buttons = [];
26721             for(var i = 0, len = item.length; i < len; i++) {
26722                buttons.push(this.insertButton(index + i, item[i]));
26723             }
26724             return buttons;
26725         }
26726         if (!(item instanceof Roo.Toolbar.Button)){
26727            item = new Roo.Toolbar.Button(item);
26728         }
26729         var td = document.createElement("td");
26730         this.tr.insertBefore(td, this.tr.childNodes[index]);
26731         item.render(td);
26732         this.items.insert(index, item);
26733         return item;
26734     },
26735     
26736     /**
26737      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26738      * @param {Object} config
26739      * @return {Roo.Toolbar.Item} The element's item
26740      */
26741     addDom : function(config, returnEl){
26742         var td = this.nextBlock();
26743         Roo.DomHelper.overwrite(td, config);
26744         var ti = new Roo.Toolbar.Item(td.firstChild);
26745         ti.render(td);
26746         this.items.add(ti);
26747         return ti;
26748     },
26749
26750     /**
26751      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26752      * @type Roo.util.MixedCollection  
26753      */
26754     fields : false,
26755     
26756     /**
26757      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26758      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26759      * @param {Roo.form.Field} field
26760      * @return {Roo.ToolbarItem}
26761      */
26762      
26763       
26764     addField : function(field) {
26765         if (!this.fields) {
26766             var autoId = 0;
26767             this.fields = new Roo.util.MixedCollection(false, function(o){
26768                 return o.id || ("item" + (++autoId));
26769             });
26770
26771         }
26772         
26773         var td = this.nextBlock();
26774         field.render(td);
26775         var ti = new Roo.Toolbar.Item(td.firstChild);
26776         ti.render(td);
26777         this.items.add(ti);
26778         this.fields.add(field);
26779         return ti;
26780     },
26781     /**
26782      * Hide the toolbar
26783      * @method hide
26784      */
26785      
26786       
26787     hide : function()
26788     {
26789         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26790         this.el.child('div').hide();
26791     },
26792     /**
26793      * Show the toolbar
26794      * @method show
26795      */
26796     show : function()
26797     {
26798         this.el.child('div').show();
26799     },
26800       
26801     // private
26802     nextBlock : function(){
26803         var td = document.createElement("td");
26804         this.tr.appendChild(td);
26805         return td;
26806     },
26807
26808     // private
26809     destroy : function(){
26810         if(this.items){ // rendered?
26811             Roo.destroy.apply(Roo, this.items.items);
26812         }
26813         if(this.fields){ // rendered?
26814             Roo.destroy.apply(Roo, this.fields.items);
26815         }
26816         Roo.Element.uncache(this.el, this.tr);
26817     }
26818 };
26819
26820 /**
26821  * @class Roo.Toolbar.Item
26822  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26823  * @constructor
26824  * Creates a new Item
26825  * @param {HTMLElement} el 
26826  */
26827 Roo.Toolbar.Item = function(el){
26828     this.el = Roo.getDom(el);
26829     this.id = Roo.id(this.el);
26830     this.hidden = false;
26831 };
26832
26833 Roo.Toolbar.Item.prototype = {
26834     
26835     /**
26836      * Get this item's HTML Element
26837      * @return {HTMLElement}
26838      */
26839     getEl : function(){
26840        return this.el;  
26841     },
26842
26843     // private
26844     render : function(td){
26845         this.td = td;
26846         td.appendChild(this.el);
26847     },
26848     
26849     /**
26850      * Removes and destroys this item.
26851      */
26852     destroy : function(){
26853         this.td.parentNode.removeChild(this.td);
26854     },
26855     
26856     /**
26857      * Shows this item.
26858      */
26859     show: function(){
26860         this.hidden = false;
26861         this.td.style.display = "";
26862     },
26863     
26864     /**
26865      * Hides this item.
26866      */
26867     hide: function(){
26868         this.hidden = true;
26869         this.td.style.display = "none";
26870     },
26871     
26872     /**
26873      * Convenience function for boolean show/hide.
26874      * @param {Boolean} visible true to show/false to hide
26875      */
26876     setVisible: function(visible){
26877         if(visible) {
26878             this.show();
26879         }else{
26880             this.hide();
26881         }
26882     },
26883     
26884     /**
26885      * Try to focus this item.
26886      */
26887     focus : function(){
26888         Roo.fly(this.el).focus();
26889     },
26890     
26891     /**
26892      * Disables this item.
26893      */
26894     disable : function(){
26895         Roo.fly(this.td).addClass("x-item-disabled");
26896         this.disabled = true;
26897         this.el.disabled = true;
26898     },
26899     
26900     /**
26901      * Enables this item.
26902      */
26903     enable : function(){
26904         Roo.fly(this.td).removeClass("x-item-disabled");
26905         this.disabled = false;
26906         this.el.disabled = false;
26907     }
26908 };
26909
26910
26911 /**
26912  * @class Roo.Toolbar.Separator
26913  * @extends Roo.Toolbar.Item
26914  * A simple toolbar separator class
26915  * @constructor
26916  * Creates a new Separator
26917  */
26918 Roo.Toolbar.Separator = function(){
26919     var s = document.createElement("span");
26920     s.className = "ytb-sep";
26921     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26922 };
26923 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26924     enable:Roo.emptyFn,
26925     disable:Roo.emptyFn,
26926     focus:Roo.emptyFn
26927 });
26928
26929 /**
26930  * @class Roo.Toolbar.Spacer
26931  * @extends Roo.Toolbar.Item
26932  * A simple element that adds extra horizontal space to a toolbar.
26933  * @constructor
26934  * Creates a new Spacer
26935  */
26936 Roo.Toolbar.Spacer = function(){
26937     var s = document.createElement("div");
26938     s.className = "ytb-spacer";
26939     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26940 };
26941 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26942     enable:Roo.emptyFn,
26943     disable:Roo.emptyFn,
26944     focus:Roo.emptyFn
26945 });
26946
26947 /**
26948  * @class Roo.Toolbar.Fill
26949  * @extends Roo.Toolbar.Spacer
26950  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26951  * @constructor
26952  * Creates a new Spacer
26953  */
26954 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26955     // private
26956     render : function(td){
26957         td.style.width = '100%';
26958         Roo.Toolbar.Fill.superclass.render.call(this, td);
26959     }
26960 });
26961
26962 /**
26963  * @class Roo.Toolbar.TextItem
26964  * @extends Roo.Toolbar.Item
26965  * A simple class that renders text directly into a toolbar.
26966  * @constructor
26967  * Creates a new TextItem
26968  * @param {String} text
26969  */
26970 Roo.Toolbar.TextItem = function(text){
26971     if (typeof(text) == 'object') {
26972         text = text.text;
26973     }
26974     var s = document.createElement("span");
26975     s.className = "ytb-text";
26976     s.innerHTML = text;
26977     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26978 };
26979 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26980     enable:Roo.emptyFn,
26981     disable:Roo.emptyFn,
26982     focus:Roo.emptyFn
26983 });
26984
26985 /**
26986  * @class Roo.Toolbar.Button
26987  * @extends Roo.Button
26988  * A button that renders into a toolbar.
26989  * @constructor
26990  * Creates a new Button
26991  * @param {Object} config A standard {@link Roo.Button} config object
26992  */
26993 Roo.Toolbar.Button = function(config){
26994     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26995 };
26996 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26997     render : function(td){
26998         this.td = td;
26999         Roo.Toolbar.Button.superclass.render.call(this, td);
27000     },
27001     
27002     /**
27003      * Removes and destroys this button
27004      */
27005     destroy : function(){
27006         Roo.Toolbar.Button.superclass.destroy.call(this);
27007         this.td.parentNode.removeChild(this.td);
27008     },
27009     
27010     /**
27011      * Shows this button
27012      */
27013     show: function(){
27014         this.hidden = false;
27015         this.td.style.display = "";
27016     },
27017     
27018     /**
27019      * Hides this button
27020      */
27021     hide: function(){
27022         this.hidden = true;
27023         this.td.style.display = "none";
27024     },
27025
27026     /**
27027      * Disables this item
27028      */
27029     disable : function(){
27030         Roo.fly(this.td).addClass("x-item-disabled");
27031         this.disabled = true;
27032     },
27033
27034     /**
27035      * Enables this item
27036      */
27037     enable : function(){
27038         Roo.fly(this.td).removeClass("x-item-disabled");
27039         this.disabled = false;
27040     }
27041 });
27042 // backwards compat
27043 Roo.ToolbarButton = Roo.Toolbar.Button;
27044
27045 /**
27046  * @class Roo.Toolbar.SplitButton
27047  * @extends Roo.SplitButton
27048  * A menu button that renders into a toolbar.
27049  * @constructor
27050  * Creates a new SplitButton
27051  * @param {Object} config A standard {@link Roo.SplitButton} config object
27052  */
27053 Roo.Toolbar.SplitButton = function(config){
27054     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27055 };
27056 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27057     render : function(td){
27058         this.td = td;
27059         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27060     },
27061     
27062     /**
27063      * Removes and destroys this button
27064      */
27065     destroy : function(){
27066         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27067         this.td.parentNode.removeChild(this.td);
27068     },
27069     
27070     /**
27071      * Shows this button
27072      */
27073     show: function(){
27074         this.hidden = false;
27075         this.td.style.display = "";
27076     },
27077     
27078     /**
27079      * Hides this button
27080      */
27081     hide: function(){
27082         this.hidden = true;
27083         this.td.style.display = "none";
27084     }
27085 });
27086
27087 // backwards compat
27088 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27089  * Based on:
27090  * Ext JS Library 1.1.1
27091  * Copyright(c) 2006-2007, Ext JS, LLC.
27092  *
27093  * Originally Released Under LGPL - original licence link has changed is not relivant.
27094  *
27095  * Fork - LGPL
27096  * <script type="text/javascript">
27097  */
27098  
27099 /**
27100  * @class Roo.PagingToolbar
27101  * @extends Roo.Toolbar
27102  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27103  * @constructor
27104  * Create a new PagingToolbar
27105  * @param {Object} config The config object
27106  */
27107 Roo.PagingToolbar = function(el, ds, config)
27108 {
27109     // old args format still supported... - xtype is prefered..
27110     if (typeof(el) == 'object' && el.xtype) {
27111         // created from xtype...
27112         config = el;
27113         ds = el.dataSource;
27114         el = config.container;
27115     }
27116     
27117     
27118     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27119     this.ds = ds;
27120     this.cursor = 0;
27121     this.renderButtons(this.el);
27122     this.bind(ds);
27123 };
27124
27125 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27126     /**
27127      * @cfg {Roo.data.Store} dataSource
27128      * The underlying data store providing the paged data
27129      */
27130     /**
27131      * @cfg {String/HTMLElement/Element} container
27132      * container The id or element that will contain the toolbar
27133      */
27134     /**
27135      * @cfg {Boolean} displayInfo
27136      * True to display the displayMsg (defaults to false)
27137      */
27138     /**
27139      * @cfg {Number} pageSize
27140      * The number of records to display per page (defaults to 20)
27141      */
27142     pageSize: 20,
27143     /**
27144      * @cfg {String} displayMsg
27145      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27146      */
27147     displayMsg : 'Displaying {0} - {1} of {2}',
27148     /**
27149      * @cfg {String} emptyMsg
27150      * The message to display when no records are found (defaults to "No data to display")
27151      */
27152     emptyMsg : 'No data to display',
27153     /**
27154      * Customizable piece of the default paging text (defaults to "Page")
27155      * @type String
27156      */
27157     beforePageText : "Page",
27158     /**
27159      * Customizable piece of the default paging text (defaults to "of %0")
27160      * @type String
27161      */
27162     afterPageText : "of {0}",
27163     /**
27164      * Customizable piece of the default paging text (defaults to "First Page")
27165      * @type String
27166      */
27167     firstText : "First Page",
27168     /**
27169      * Customizable piece of the default paging text (defaults to "Previous Page")
27170      * @type String
27171      */
27172     prevText : "Previous Page",
27173     /**
27174      * Customizable piece of the default paging text (defaults to "Next Page")
27175      * @type String
27176      */
27177     nextText : "Next Page",
27178     /**
27179      * Customizable piece of the default paging text (defaults to "Last Page")
27180      * @type String
27181      */
27182     lastText : "Last Page",
27183     /**
27184      * Customizable piece of the default paging text (defaults to "Refresh")
27185      * @type String
27186      */
27187     refreshText : "Refresh",
27188
27189     // private
27190     renderButtons : function(el){
27191         Roo.PagingToolbar.superclass.render.call(this, el);
27192         this.first = this.addButton({
27193             tooltip: this.firstText,
27194             cls: "x-btn-icon x-grid-page-first",
27195             disabled: true,
27196             handler: this.onClick.createDelegate(this, ["first"])
27197         });
27198         this.prev = this.addButton({
27199             tooltip: this.prevText,
27200             cls: "x-btn-icon x-grid-page-prev",
27201             disabled: true,
27202             handler: this.onClick.createDelegate(this, ["prev"])
27203         });
27204         //this.addSeparator();
27205         this.add(this.beforePageText);
27206         this.field = Roo.get(this.addDom({
27207            tag: "input",
27208            type: "text",
27209            size: "3",
27210            value: "1",
27211            cls: "x-grid-page-number"
27212         }).el);
27213         this.field.on("keydown", this.onPagingKeydown, this);
27214         this.field.on("focus", function(){this.dom.select();});
27215         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27216         this.field.setHeight(18);
27217         //this.addSeparator();
27218         this.next = this.addButton({
27219             tooltip: this.nextText,
27220             cls: "x-btn-icon x-grid-page-next",
27221             disabled: true,
27222             handler: this.onClick.createDelegate(this, ["next"])
27223         });
27224         this.last = this.addButton({
27225             tooltip: this.lastText,
27226             cls: "x-btn-icon x-grid-page-last",
27227             disabled: true,
27228             handler: this.onClick.createDelegate(this, ["last"])
27229         });
27230         //this.addSeparator();
27231         this.loading = this.addButton({
27232             tooltip: this.refreshText,
27233             cls: "x-btn-icon x-grid-loading",
27234             handler: this.onClick.createDelegate(this, ["refresh"])
27235         });
27236
27237         if(this.displayInfo){
27238             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27239         }
27240     },
27241
27242     // private
27243     updateInfo : function(){
27244         if(this.displayEl){
27245             var count = this.ds.getCount();
27246             var msg = count == 0 ?
27247                 this.emptyMsg :
27248                 String.format(
27249                     this.displayMsg,
27250                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27251                 );
27252             this.displayEl.update(msg);
27253         }
27254     },
27255
27256     // private
27257     onLoad : function(ds, r, o){
27258        this.cursor = o.params ? o.params.start : 0;
27259        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27260
27261        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27262        this.field.dom.value = ap;
27263        this.first.setDisabled(ap == 1);
27264        this.prev.setDisabled(ap == 1);
27265        this.next.setDisabled(ap == ps);
27266        this.last.setDisabled(ap == ps);
27267        this.loading.enable();
27268        this.updateInfo();
27269     },
27270
27271     // private
27272     getPageData : function(){
27273         var total = this.ds.getTotalCount();
27274         return {
27275             total : total,
27276             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27277             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27278         };
27279     },
27280
27281     // private
27282     onLoadError : function(){
27283         this.loading.enable();
27284     },
27285
27286     // private
27287     onPagingKeydown : function(e){
27288         var k = e.getKey();
27289         var d = this.getPageData();
27290         if(k == e.RETURN){
27291             var v = this.field.dom.value, pageNum;
27292             if(!v || isNaN(pageNum = parseInt(v, 10))){
27293                 this.field.dom.value = d.activePage;
27294                 return;
27295             }
27296             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27297             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27298             e.stopEvent();
27299         }
27300         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
27301         {
27302           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27303           this.field.dom.value = pageNum;
27304           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27305           e.stopEvent();
27306         }
27307         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27308         {
27309           var v = this.field.dom.value, pageNum; 
27310           var increment = (e.shiftKey) ? 10 : 1;
27311           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27312             increment *= -1;
27313           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27314             this.field.dom.value = d.activePage;
27315             return;
27316           }
27317           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27318           {
27319             this.field.dom.value = parseInt(v, 10) + increment;
27320             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27321             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27322           }
27323           e.stopEvent();
27324         }
27325     },
27326
27327     // private
27328     beforeLoad : function(){
27329         if(this.loading){
27330             this.loading.disable();
27331         }
27332     },
27333
27334     // private
27335     onClick : function(which){
27336         var ds = this.ds;
27337         switch(which){
27338             case "first":
27339                 ds.load({params:{start: 0, limit: this.pageSize}});
27340             break;
27341             case "prev":
27342                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27343             break;
27344             case "next":
27345                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27346             break;
27347             case "last":
27348                 var total = ds.getTotalCount();
27349                 var extra = total % this.pageSize;
27350                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27351                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27352             break;
27353             case "refresh":
27354                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27355             break;
27356         }
27357     },
27358
27359     /**
27360      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27361      * @param {Roo.data.Store} store The data store to unbind
27362      */
27363     unbind : function(ds){
27364         ds.un("beforeload", this.beforeLoad, this);
27365         ds.un("load", this.onLoad, this);
27366         ds.un("loadexception", this.onLoadError, this);
27367         ds.un("remove", this.updateInfo, this);
27368         ds.un("add", this.updateInfo, this);
27369         this.ds = undefined;
27370     },
27371
27372     /**
27373      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27374      * @param {Roo.data.Store} store The data store to bind
27375      */
27376     bind : function(ds){
27377         ds.on("beforeload", this.beforeLoad, this);
27378         ds.on("load", this.onLoad, this);
27379         ds.on("loadexception", this.onLoadError, this);
27380         ds.on("remove", this.updateInfo, this);
27381         ds.on("add", this.updateInfo, this);
27382         this.ds = ds;
27383     }
27384 });/*
27385  * Based on:
27386  * Ext JS Library 1.1.1
27387  * Copyright(c) 2006-2007, Ext JS, LLC.
27388  *
27389  * Originally Released Under LGPL - original licence link has changed is not relivant.
27390  *
27391  * Fork - LGPL
27392  * <script type="text/javascript">
27393  */
27394
27395 /**
27396  * @class Roo.Resizable
27397  * @extends Roo.util.Observable
27398  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27399  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27400  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
27401  * the element will be wrapped for you automatically.</p>
27402  * <p>Here is the list of valid resize handles:</p>
27403  * <pre>
27404 Value   Description
27405 ------  -------------------
27406  'n'     north
27407  's'     south
27408  'e'     east
27409  'w'     west
27410  'nw'    northwest
27411  'sw'    southwest
27412  'se'    southeast
27413  'ne'    northeast
27414  'hd'    horizontal drag
27415  'all'   all
27416 </pre>
27417  * <p>Here's an example showing the creation of a typical Resizable:</p>
27418  * <pre><code>
27419 var resizer = new Roo.Resizable("element-id", {
27420     handles: 'all',
27421     minWidth: 200,
27422     minHeight: 100,
27423     maxWidth: 500,
27424     maxHeight: 400,
27425     pinned: true
27426 });
27427 resizer.on("resize", myHandler);
27428 </code></pre>
27429  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27430  * resizer.east.setDisplayed(false);</p>
27431  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27432  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27433  * resize operation's new size (defaults to [0, 0])
27434  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27435  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27436  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27437  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27438  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27439  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27440  * @cfg {Number} width The width of the element in pixels (defaults to null)
27441  * @cfg {Number} height The height of the element in pixels (defaults to null)
27442  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27443  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27444  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27445  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27446  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27447  * in favor of the handles config option (defaults to false)
27448  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27449  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27450  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27451  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27452  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27453  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27454  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27455  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27456  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27457  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27458  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27459  * @constructor
27460  * Create a new resizable component
27461  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27462  * @param {Object} config configuration options
27463   */
27464 Roo.Resizable = function(el, config)
27465 {
27466     this.el = Roo.get(el);
27467
27468     if(config && config.wrap){
27469         config.resizeChild = this.el;
27470         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27471         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27472         this.el.setStyle("overflow", "hidden");
27473         this.el.setPositioning(config.resizeChild.getPositioning());
27474         config.resizeChild.clearPositioning();
27475         if(!config.width || !config.height){
27476             var csize = config.resizeChild.getSize();
27477             this.el.setSize(csize.width, csize.height);
27478         }
27479         if(config.pinned && !config.adjustments){
27480             config.adjustments = "auto";
27481         }
27482     }
27483
27484     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27485     this.proxy.unselectable();
27486     this.proxy.enableDisplayMode('block');
27487
27488     Roo.apply(this, config);
27489
27490     if(this.pinned){
27491         this.disableTrackOver = true;
27492         this.el.addClass("x-resizable-pinned");
27493     }
27494     // if the element isn't positioned, make it relative
27495     var position = this.el.getStyle("position");
27496     if(position != "absolute" && position != "fixed"){
27497         this.el.setStyle("position", "relative");
27498     }
27499     if(!this.handles){ // no handles passed, must be legacy style
27500         this.handles = 's,e,se';
27501         if(this.multiDirectional){
27502             this.handles += ',n,w';
27503         }
27504     }
27505     if(this.handles == "all"){
27506         this.handles = "n s e w ne nw se sw";
27507     }
27508     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27509     var ps = Roo.Resizable.positions;
27510     for(var i = 0, len = hs.length; i < len; i++){
27511         if(hs[i] && ps[hs[i]]){
27512             var pos = ps[hs[i]];
27513             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27514         }
27515     }
27516     // legacy
27517     this.corner = this.southeast;
27518     
27519     // updateBox = the box can move..
27520     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27521         this.updateBox = true;
27522     }
27523
27524     this.activeHandle = null;
27525
27526     if(this.resizeChild){
27527         if(typeof this.resizeChild == "boolean"){
27528             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27529         }else{
27530             this.resizeChild = Roo.get(this.resizeChild, true);
27531         }
27532     }
27533     
27534     if(this.adjustments == "auto"){
27535         var rc = this.resizeChild;
27536         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27537         if(rc && (hw || hn)){
27538             rc.position("relative");
27539             rc.setLeft(hw ? hw.el.getWidth() : 0);
27540             rc.setTop(hn ? hn.el.getHeight() : 0);
27541         }
27542         this.adjustments = [
27543             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27544             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27545         ];
27546     }
27547
27548     if(this.draggable){
27549         this.dd = this.dynamic ?
27550             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27551         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27552     }
27553
27554     // public events
27555     this.addEvents({
27556         /**
27557          * @event beforeresize
27558          * Fired before resize is allowed. Set enabled to false to cancel resize.
27559          * @param {Roo.Resizable} this
27560          * @param {Roo.EventObject} e The mousedown event
27561          */
27562         "beforeresize" : true,
27563         /**
27564          * @event resize
27565          * Fired after a resize.
27566          * @param {Roo.Resizable} this
27567          * @param {Number} width The new width
27568          * @param {Number} height The new height
27569          * @param {Roo.EventObject} e The mouseup event
27570          */
27571         "resize" : true
27572     });
27573
27574     if(this.width !== null && this.height !== null){
27575         this.resizeTo(this.width, this.height);
27576     }else{
27577         this.updateChildSize();
27578     }
27579     if(Roo.isIE){
27580         this.el.dom.style.zoom = 1;
27581     }
27582     Roo.Resizable.superclass.constructor.call(this);
27583 };
27584
27585 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27586         resizeChild : false,
27587         adjustments : [0, 0],
27588         minWidth : 5,
27589         minHeight : 5,
27590         maxWidth : 10000,
27591         maxHeight : 10000,
27592         enabled : true,
27593         animate : false,
27594         duration : .35,
27595         dynamic : false,
27596         handles : false,
27597         multiDirectional : false,
27598         disableTrackOver : false,
27599         easing : 'easeOutStrong',
27600         widthIncrement : 0,
27601         heightIncrement : 0,
27602         pinned : false,
27603         width : null,
27604         height : null,
27605         preserveRatio : false,
27606         transparent: false,
27607         minX: 0,
27608         minY: 0,
27609         draggable: false,
27610
27611         /**
27612          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27613          */
27614         constrainTo: undefined,
27615         /**
27616          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27617          */
27618         resizeRegion: undefined,
27619
27620
27621     /**
27622      * Perform a manual resize
27623      * @param {Number} width
27624      * @param {Number} height
27625      */
27626     resizeTo : function(width, height){
27627         this.el.setSize(width, height);
27628         this.updateChildSize();
27629         this.fireEvent("resize", this, width, height, null);
27630     },
27631
27632     // private
27633     startSizing : function(e, handle){
27634         this.fireEvent("beforeresize", this, e);
27635         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27636
27637             if(!this.overlay){
27638                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27639                 this.overlay.unselectable();
27640                 this.overlay.enableDisplayMode("block");
27641                 this.overlay.on("mousemove", this.onMouseMove, this);
27642                 this.overlay.on("mouseup", this.onMouseUp, this);
27643             }
27644             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27645
27646             this.resizing = true;
27647             this.startBox = this.el.getBox();
27648             this.startPoint = e.getXY();
27649             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27650                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27651
27652             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27653             this.overlay.show();
27654
27655             if(this.constrainTo) {
27656                 var ct = Roo.get(this.constrainTo);
27657                 this.resizeRegion = ct.getRegion().adjust(
27658                     ct.getFrameWidth('t'),
27659                     ct.getFrameWidth('l'),
27660                     -ct.getFrameWidth('b'),
27661                     -ct.getFrameWidth('r')
27662                 );
27663             }
27664
27665             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27666             this.proxy.show();
27667             this.proxy.setBox(this.startBox);
27668             if(!this.dynamic){
27669                 this.proxy.setStyle('visibility', 'visible');
27670             }
27671         }
27672     },
27673
27674     // private
27675     onMouseDown : function(handle, e){
27676         if(this.enabled){
27677             e.stopEvent();
27678             this.activeHandle = handle;
27679             this.startSizing(e, handle);
27680         }
27681     },
27682
27683     // private
27684     onMouseUp : function(e){
27685         var size = this.resizeElement();
27686         this.resizing = false;
27687         this.handleOut();
27688         this.overlay.hide();
27689         this.proxy.hide();
27690         this.fireEvent("resize", this, size.width, size.height, e);
27691     },
27692
27693     // private
27694     updateChildSize : function(){
27695         if(this.resizeChild){
27696             var el = this.el;
27697             var child = this.resizeChild;
27698             var adj = this.adjustments;
27699             if(el.dom.offsetWidth){
27700                 var b = el.getSize(true);
27701                 child.setSize(b.width+adj[0], b.height+adj[1]);
27702             }
27703             // Second call here for IE
27704             // The first call enables instant resizing and
27705             // the second call corrects scroll bars if they
27706             // exist
27707             if(Roo.isIE){
27708                 setTimeout(function(){
27709                     if(el.dom.offsetWidth){
27710                         var b = el.getSize(true);
27711                         child.setSize(b.width+adj[0], b.height+adj[1]);
27712                     }
27713                 }, 10);
27714             }
27715         }
27716     },
27717
27718     // private
27719     snap : function(value, inc, min){
27720         if(!inc || !value) return value;
27721         var newValue = value;
27722         var m = value % inc;
27723         if(m > 0){
27724             if(m > (inc/2)){
27725                 newValue = value + (inc-m);
27726             }else{
27727                 newValue = value - m;
27728             }
27729         }
27730         return Math.max(min, newValue);
27731     },
27732
27733     // private
27734     resizeElement : function(){
27735         var box = this.proxy.getBox();
27736         if(this.updateBox){
27737             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27738         }else{
27739             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27740         }
27741         this.updateChildSize();
27742         if(!this.dynamic){
27743             this.proxy.hide();
27744         }
27745         return box;
27746     },
27747
27748     // private
27749     constrain : function(v, diff, m, mx){
27750         if(v - diff < m){
27751             diff = v - m;
27752         }else if(v - diff > mx){
27753             diff = mx - v;
27754         }
27755         return diff;
27756     },
27757
27758     // private
27759     onMouseMove : function(e){
27760         if(this.enabled){
27761             try{// try catch so if something goes wrong the user doesn't get hung
27762
27763             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27764                 return;
27765             }
27766
27767             //var curXY = this.startPoint;
27768             var curSize = this.curSize || this.startBox;
27769             var x = this.startBox.x, y = this.startBox.y;
27770             var ox = x, oy = y;
27771             var w = curSize.width, h = curSize.height;
27772             var ow = w, oh = h;
27773             var mw = this.minWidth, mh = this.minHeight;
27774             var mxw = this.maxWidth, mxh = this.maxHeight;
27775             var wi = this.widthIncrement;
27776             var hi = this.heightIncrement;
27777
27778             var eventXY = e.getXY();
27779             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27780             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27781
27782             var pos = this.activeHandle.position;
27783
27784             switch(pos){
27785                 case "east":
27786                     w += diffX;
27787                     w = Math.min(Math.max(mw, w), mxw);
27788                     break;
27789              
27790                 case "south":
27791                     h += diffY;
27792                     h = Math.min(Math.max(mh, h), mxh);
27793                     break;
27794                 case "southeast":
27795                     w += diffX;
27796                     h += diffY;
27797                     w = Math.min(Math.max(mw, w), mxw);
27798                     h = Math.min(Math.max(mh, h), mxh);
27799                     break;
27800                 case "north":
27801                     diffY = this.constrain(h, diffY, mh, mxh);
27802                     y += diffY;
27803                     h -= diffY;
27804                     break;
27805                 case "hdrag":
27806                     
27807                     if (wi) {
27808                         var adiffX = Math.abs(diffX);
27809                         var sub = (adiffX % wi); // how much 
27810                         if (sub > (wi/2)) { // far enough to snap
27811                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27812                         } else {
27813                             // remove difference.. 
27814                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27815                         }
27816                     }
27817                     x += diffX;
27818                     x = Math.max(this.minX, x);
27819                     break;
27820                 case "west":
27821                     diffX = this.constrain(w, diffX, mw, mxw);
27822                     x += diffX;
27823                     w -= diffX;
27824                     break;
27825                 case "northeast":
27826                     w += diffX;
27827                     w = Math.min(Math.max(mw, w), mxw);
27828                     diffY = this.constrain(h, diffY, mh, mxh);
27829                     y += diffY;
27830                     h -= diffY;
27831                     break;
27832                 case "northwest":
27833                     diffX = this.constrain(w, diffX, mw, mxw);
27834                     diffY = this.constrain(h, diffY, mh, mxh);
27835                     y += diffY;
27836                     h -= diffY;
27837                     x += diffX;
27838                     w -= diffX;
27839                     break;
27840                case "southwest":
27841                     diffX = this.constrain(w, diffX, mw, mxw);
27842                     h += diffY;
27843                     h = Math.min(Math.max(mh, h), mxh);
27844                     x += diffX;
27845                     w -= diffX;
27846                     break;
27847             }
27848
27849             var sw = this.snap(w, wi, mw);
27850             var sh = this.snap(h, hi, mh);
27851             if(sw != w || sh != h){
27852                 switch(pos){
27853                     case "northeast":
27854                         y -= sh - h;
27855                     break;
27856                     case "north":
27857                         y -= sh - h;
27858                         break;
27859                     case "southwest":
27860                         x -= sw - w;
27861                     break;
27862                     case "west":
27863                         x -= sw - w;
27864                         break;
27865                     case "northwest":
27866                         x -= sw - w;
27867                         y -= sh - h;
27868                     break;
27869                 }
27870                 w = sw;
27871                 h = sh;
27872             }
27873
27874             if(this.preserveRatio){
27875                 switch(pos){
27876                     case "southeast":
27877                     case "east":
27878                         h = oh * (w/ow);
27879                         h = Math.min(Math.max(mh, h), mxh);
27880                         w = ow * (h/oh);
27881                        break;
27882                     case "south":
27883                         w = ow * (h/oh);
27884                         w = Math.min(Math.max(mw, w), mxw);
27885                         h = oh * (w/ow);
27886                         break;
27887                     case "northeast":
27888                         w = ow * (h/oh);
27889                         w = Math.min(Math.max(mw, w), mxw);
27890                         h = oh * (w/ow);
27891                     break;
27892                     case "north":
27893                         var tw = w;
27894                         w = ow * (h/oh);
27895                         w = Math.min(Math.max(mw, w), mxw);
27896                         h = oh * (w/ow);
27897                         x += (tw - w) / 2;
27898                         break;
27899                     case "southwest":
27900                         h = oh * (w/ow);
27901                         h = Math.min(Math.max(mh, h), mxh);
27902                         var tw = w;
27903                         w = ow * (h/oh);
27904                         x += tw - w;
27905                         break;
27906                     case "west":
27907                         var th = h;
27908                         h = oh * (w/ow);
27909                         h = Math.min(Math.max(mh, h), mxh);
27910                         y += (th - h) / 2;
27911                         var tw = w;
27912                         w = ow * (h/oh);
27913                         x += tw - w;
27914                        break;
27915                     case "northwest":
27916                         var tw = w;
27917                         var th = h;
27918                         h = oh * (w/ow);
27919                         h = Math.min(Math.max(mh, h), mxh);
27920                         w = ow * (h/oh);
27921                         y += th - h;
27922                         x += tw - w;
27923                        break;
27924
27925                 }
27926             }
27927             if (pos == 'hdrag') {
27928                 w = ow;
27929             }
27930             this.proxy.setBounds(x, y, w, h);
27931             if(this.dynamic){
27932                 this.resizeElement();
27933             }
27934             }catch(e){}
27935         }
27936     },
27937
27938     // private
27939     handleOver : function(){
27940         if(this.enabled){
27941             this.el.addClass("x-resizable-over");
27942         }
27943     },
27944
27945     // private
27946     handleOut : function(){
27947         if(!this.resizing){
27948             this.el.removeClass("x-resizable-over");
27949         }
27950     },
27951
27952     /**
27953      * Returns the element this component is bound to.
27954      * @return {Roo.Element}
27955      */
27956     getEl : function(){
27957         return this.el;
27958     },
27959
27960     /**
27961      * Returns the resizeChild element (or null).
27962      * @return {Roo.Element}
27963      */
27964     getResizeChild : function(){
27965         return this.resizeChild;
27966     },
27967
27968     /**
27969      * Destroys this resizable. If the element was wrapped and
27970      * removeEl is not true then the element remains.
27971      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27972      */
27973     destroy : function(removeEl){
27974         this.proxy.remove();
27975         if(this.overlay){
27976             this.overlay.removeAllListeners();
27977             this.overlay.remove();
27978         }
27979         var ps = Roo.Resizable.positions;
27980         for(var k in ps){
27981             if(typeof ps[k] != "function" && this[ps[k]]){
27982                 var h = this[ps[k]];
27983                 h.el.removeAllListeners();
27984                 h.el.remove();
27985             }
27986         }
27987         if(removeEl){
27988             this.el.update("");
27989             this.el.remove();
27990         }
27991     }
27992 });
27993
27994 // private
27995 // hash to map config positions to true positions
27996 Roo.Resizable.positions = {
27997     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
27998     hd: "hdrag"
27999 };
28000
28001 // private
28002 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28003     if(!this.tpl){
28004         // only initialize the template if resizable is used
28005         var tpl = Roo.DomHelper.createTemplate(
28006             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28007         );
28008         tpl.compile();
28009         Roo.Resizable.Handle.prototype.tpl = tpl;
28010     }
28011     this.position = pos;
28012     this.rz = rz;
28013     // show north drag fro topdra
28014     var handlepos = pos == 'hdrag' ? 'north' : pos;
28015     
28016     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28017     if (pos == 'hdrag') {
28018         this.el.setStyle('cursor', 'pointer');
28019     }
28020     this.el.unselectable();
28021     if(transparent){
28022         this.el.setOpacity(0);
28023     }
28024     this.el.on("mousedown", this.onMouseDown, this);
28025     if(!disableTrackOver){
28026         this.el.on("mouseover", this.onMouseOver, this);
28027         this.el.on("mouseout", this.onMouseOut, this);
28028     }
28029 };
28030
28031 // private
28032 Roo.Resizable.Handle.prototype = {
28033     afterResize : function(rz){
28034         // do nothing
28035     },
28036     // private
28037     onMouseDown : function(e){
28038         this.rz.onMouseDown(this, e);
28039     },
28040     // private
28041     onMouseOver : function(e){
28042         this.rz.handleOver(this, e);
28043     },
28044     // private
28045     onMouseOut : function(e){
28046         this.rz.handleOut(this, e);
28047     }
28048 };/*
28049  * Based on:
28050  * Ext JS Library 1.1.1
28051  * Copyright(c) 2006-2007, Ext JS, LLC.
28052  *
28053  * Originally Released Under LGPL - original licence link has changed is not relivant.
28054  *
28055  * Fork - LGPL
28056  * <script type="text/javascript">
28057  */
28058
28059 /**
28060  * @class Roo.Editor
28061  * @extends Roo.Component
28062  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28063  * @constructor
28064  * Create a new Editor
28065  * @param {Roo.form.Field} field The Field object (or descendant)
28066  * @param {Object} config The config object
28067  */
28068 Roo.Editor = function(field, config){
28069     Roo.Editor.superclass.constructor.call(this, config);
28070     this.field = field;
28071     this.addEvents({
28072         /**
28073              * @event beforestartedit
28074              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28075              * false from the handler of this event.
28076              * @param {Editor} this
28077              * @param {Roo.Element} boundEl The underlying element bound to this editor
28078              * @param {Mixed} value The field value being set
28079              */
28080         "beforestartedit" : true,
28081         /**
28082              * @event startedit
28083              * Fires when this editor is displayed
28084              * @param {Roo.Element} boundEl The underlying element bound to this editor
28085              * @param {Mixed} value The starting field value
28086              */
28087         "startedit" : true,
28088         /**
28089              * @event beforecomplete
28090              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28091              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28092              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28093              * event will not fire since no edit actually occurred.
28094              * @param {Editor} this
28095              * @param {Mixed} value The current field value
28096              * @param {Mixed} startValue The original field value
28097              */
28098         "beforecomplete" : true,
28099         /**
28100              * @event complete
28101              * Fires after editing is complete and any changed value has been written to the underlying field.
28102              * @param {Editor} this
28103              * @param {Mixed} value The current field value
28104              * @param {Mixed} startValue The original field value
28105              */
28106         "complete" : true,
28107         /**
28108          * @event specialkey
28109          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28110          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28111          * @param {Roo.form.Field} this
28112          * @param {Roo.EventObject} e The event object
28113          */
28114         "specialkey" : true
28115     });
28116 };
28117
28118 Roo.extend(Roo.Editor, Roo.Component, {
28119     /**
28120      * @cfg {Boolean/String} autosize
28121      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28122      * or "height" to adopt the height only (defaults to false)
28123      */
28124     /**
28125      * @cfg {Boolean} revertInvalid
28126      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28127      * validation fails (defaults to true)
28128      */
28129     /**
28130      * @cfg {Boolean} ignoreNoChange
28131      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28132      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28133      * will never be ignored.
28134      */
28135     /**
28136      * @cfg {Boolean} hideEl
28137      * False to keep the bound element visible while the editor is displayed (defaults to true)
28138      */
28139     /**
28140      * @cfg {Mixed} value
28141      * The data value of the underlying field (defaults to "")
28142      */
28143     value : "",
28144     /**
28145      * @cfg {String} alignment
28146      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28147      */
28148     alignment: "c-c?",
28149     /**
28150      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28151      * for bottom-right shadow (defaults to "frame")
28152      */
28153     shadow : "frame",
28154     /**
28155      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28156      */
28157     constrain : false,
28158     /**
28159      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28160      */
28161     completeOnEnter : false,
28162     /**
28163      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28164      */
28165     cancelOnEsc : false,
28166     /**
28167      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28168      */
28169     updateEl : false,
28170
28171     // private
28172     onRender : function(ct, position){
28173         this.el = new Roo.Layer({
28174             shadow: this.shadow,
28175             cls: "x-editor",
28176             parentEl : ct,
28177             shim : this.shim,
28178             shadowOffset:4,
28179             id: this.id,
28180             constrain: this.constrain
28181         });
28182         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28183         if(this.field.msgTarget != 'title'){
28184             this.field.msgTarget = 'qtip';
28185         }
28186         this.field.render(this.el);
28187         if(Roo.isGecko){
28188             this.field.el.dom.setAttribute('autocomplete', 'off');
28189         }
28190         this.field.on("specialkey", this.onSpecialKey, this);
28191         if(this.swallowKeys){
28192             this.field.el.swallowEvent(['keydown','keypress']);
28193         }
28194         this.field.show();
28195         this.field.on("blur", this.onBlur, this);
28196         if(this.field.grow){
28197             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28198         }
28199     },
28200
28201     onSpecialKey : function(field, e){
28202         //Roo.log('editor onSpecialKey');
28203         if(this.completeOnEnter && e.getKey() == e.ENTER){
28204             e.stopEvent();
28205             this.completeEdit();
28206         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28207             this.cancelEdit();
28208         }else{
28209             this.fireEvent('specialkey', field, e);
28210         }
28211     },
28212
28213     /**
28214      * Starts the editing process and shows the editor.
28215      * @param {String/HTMLElement/Element} el The element to edit
28216      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28217       * to the innerHTML of el.
28218      */
28219     startEdit : function(el, value){
28220         if(this.editing){
28221             this.completeEdit();
28222         }
28223         this.boundEl = Roo.get(el);
28224         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28225         if(!this.rendered){
28226             this.render(this.parentEl || document.body);
28227         }
28228         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28229             return;
28230         }
28231         this.startValue = v;
28232         this.field.setValue(v);
28233         if(this.autoSize){
28234             var sz = this.boundEl.getSize();
28235             switch(this.autoSize){
28236                 case "width":
28237                 this.setSize(sz.width,  "");
28238                 break;
28239                 case "height":
28240                 this.setSize("",  sz.height);
28241                 break;
28242                 default:
28243                 this.setSize(sz.width,  sz.height);
28244             }
28245         }
28246         this.el.alignTo(this.boundEl, this.alignment);
28247         this.editing = true;
28248         if(Roo.QuickTips){
28249             Roo.QuickTips.disable();
28250         }
28251         this.show();
28252     },
28253
28254     /**
28255      * Sets the height and width of this editor.
28256      * @param {Number} width The new width
28257      * @param {Number} height The new height
28258      */
28259     setSize : function(w, h){
28260         this.field.setSize(w, h);
28261         if(this.el){
28262             this.el.sync();
28263         }
28264     },
28265
28266     /**
28267      * Realigns the editor to the bound field based on the current alignment config value.
28268      */
28269     realign : function(){
28270         this.el.alignTo(this.boundEl, this.alignment);
28271     },
28272
28273     /**
28274      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28275      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28276      */
28277     completeEdit : function(remainVisible){
28278         if(!this.editing){
28279             return;
28280         }
28281         var v = this.getValue();
28282         if(this.revertInvalid !== false && !this.field.isValid()){
28283             v = this.startValue;
28284             this.cancelEdit(true);
28285         }
28286         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28287             this.editing = false;
28288             this.hide();
28289             return;
28290         }
28291         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28292             this.editing = false;
28293             if(this.updateEl && this.boundEl){
28294                 this.boundEl.update(v);
28295             }
28296             if(remainVisible !== true){
28297                 this.hide();
28298             }
28299             this.fireEvent("complete", this, v, this.startValue);
28300         }
28301     },
28302
28303     // private
28304     onShow : function(){
28305         this.el.show();
28306         if(this.hideEl !== false){
28307             this.boundEl.hide();
28308         }
28309         this.field.show();
28310         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28311             this.fixIEFocus = true;
28312             this.deferredFocus.defer(50, this);
28313         }else{
28314             this.field.focus();
28315         }
28316         this.fireEvent("startedit", this.boundEl, this.startValue);
28317     },
28318
28319     deferredFocus : function(){
28320         if(this.editing){
28321             this.field.focus();
28322         }
28323     },
28324
28325     /**
28326      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28327      * reverted to the original starting value.
28328      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28329      * cancel (defaults to false)
28330      */
28331     cancelEdit : function(remainVisible){
28332         if(this.editing){
28333             this.setValue(this.startValue);
28334             if(remainVisible !== true){
28335                 this.hide();
28336             }
28337         }
28338     },
28339
28340     // private
28341     onBlur : function(){
28342         if(this.allowBlur !== true && this.editing){
28343             this.completeEdit();
28344         }
28345     },
28346
28347     // private
28348     onHide : function(){
28349         if(this.editing){
28350             this.completeEdit();
28351             return;
28352         }
28353         this.field.blur();
28354         if(this.field.collapse){
28355             this.field.collapse();
28356         }
28357         this.el.hide();
28358         if(this.hideEl !== false){
28359             this.boundEl.show();
28360         }
28361         if(Roo.QuickTips){
28362             Roo.QuickTips.enable();
28363         }
28364     },
28365
28366     /**
28367      * Sets the data value of the editor
28368      * @param {Mixed} value Any valid value supported by the underlying field
28369      */
28370     setValue : function(v){
28371         this.field.setValue(v);
28372     },
28373
28374     /**
28375      * Gets the data value of the editor
28376      * @return {Mixed} The data value
28377      */
28378     getValue : function(){
28379         return this.field.getValue();
28380     }
28381 });/*
28382  * Based on:
28383  * Ext JS Library 1.1.1
28384  * Copyright(c) 2006-2007, Ext JS, LLC.
28385  *
28386  * Originally Released Under LGPL - original licence link has changed is not relivant.
28387  *
28388  * Fork - LGPL
28389  * <script type="text/javascript">
28390  */
28391  
28392 /**
28393  * @class Roo.BasicDialog
28394  * @extends Roo.util.Observable
28395  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28396  * <pre><code>
28397 var dlg = new Roo.BasicDialog("my-dlg", {
28398     height: 200,
28399     width: 300,
28400     minHeight: 100,
28401     minWidth: 150,
28402     modal: true,
28403     proxyDrag: true,
28404     shadow: true
28405 });
28406 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28407 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28408 dlg.addButton('Cancel', dlg.hide, dlg);
28409 dlg.show();
28410 </code></pre>
28411   <b>A Dialog should always be a direct child of the body element.</b>
28412  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28413  * @cfg {String} title Default text to display in the title bar (defaults to null)
28414  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28415  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28416  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28417  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28418  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28419  * (defaults to null with no animation)
28420  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28421  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28422  * property for valid values (defaults to 'all')
28423  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28424  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28425  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28426  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28427  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28428  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28429  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28430  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28431  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28432  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28433  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28434  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28435  * draggable = true (defaults to false)
28436  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28437  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28438  * shadow (defaults to false)
28439  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28440  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28441  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28442  * @cfg {Array} buttons Array of buttons
28443  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28444  * @constructor
28445  * Create a new BasicDialog.
28446  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28447  * @param {Object} config Configuration options
28448  */
28449 Roo.BasicDialog = function(el, config){
28450     this.el = Roo.get(el);
28451     var dh = Roo.DomHelper;
28452     if(!this.el && config && config.autoCreate){
28453         if(typeof config.autoCreate == "object"){
28454             if(!config.autoCreate.id){
28455                 config.autoCreate.id = el;
28456             }
28457             this.el = dh.append(document.body,
28458                         config.autoCreate, true);
28459         }else{
28460             this.el = dh.append(document.body,
28461                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28462         }
28463     }
28464     el = this.el;
28465     el.setDisplayed(true);
28466     el.hide = this.hideAction;
28467     this.id = el.id;
28468     el.addClass("x-dlg");
28469
28470     Roo.apply(this, config);
28471
28472     this.proxy = el.createProxy("x-dlg-proxy");
28473     this.proxy.hide = this.hideAction;
28474     this.proxy.setOpacity(.5);
28475     this.proxy.hide();
28476
28477     if(config.width){
28478         el.setWidth(config.width);
28479     }
28480     if(config.height){
28481         el.setHeight(config.height);
28482     }
28483     this.size = el.getSize();
28484     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28485         this.xy = [config.x,config.y];
28486     }else{
28487         this.xy = el.getCenterXY(true);
28488     }
28489     /** The header element @type Roo.Element */
28490     this.header = el.child("> .x-dlg-hd");
28491     /** The body element @type Roo.Element */
28492     this.body = el.child("> .x-dlg-bd");
28493     /** The footer element @type Roo.Element */
28494     this.footer = el.child("> .x-dlg-ft");
28495
28496     if(!this.header){
28497         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28498     }
28499     if(!this.body){
28500         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28501     }
28502
28503     this.header.unselectable();
28504     if(this.title){
28505         this.header.update(this.title);
28506     }
28507     // this element allows the dialog to be focused for keyboard event
28508     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28509     this.focusEl.swallowEvent("click", true);
28510
28511     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28512
28513     // wrap the body and footer for special rendering
28514     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28515     if(this.footer){
28516         this.bwrap.dom.appendChild(this.footer.dom);
28517     }
28518
28519     this.bg = this.el.createChild({
28520         tag: "div", cls:"x-dlg-bg",
28521         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28522     });
28523     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28524
28525
28526     if(this.autoScroll !== false && !this.autoTabs){
28527         this.body.setStyle("overflow", "auto");
28528     }
28529
28530     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28531
28532     if(this.closable !== false){
28533         this.el.addClass("x-dlg-closable");
28534         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28535         this.close.on("click", this.closeClick, this);
28536         this.close.addClassOnOver("x-dlg-close-over");
28537     }
28538     if(this.collapsible !== false){
28539         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28540         this.collapseBtn.on("click", this.collapseClick, this);
28541         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28542         this.header.on("dblclick", this.collapseClick, this);
28543     }
28544     if(this.resizable !== false){
28545         this.el.addClass("x-dlg-resizable");
28546         this.resizer = new Roo.Resizable(el, {
28547             minWidth: this.minWidth || 80,
28548             minHeight:this.minHeight || 80,
28549             handles: this.resizeHandles || "all",
28550             pinned: true
28551         });
28552         this.resizer.on("beforeresize", this.beforeResize, this);
28553         this.resizer.on("resize", this.onResize, this);
28554     }
28555     if(this.draggable !== false){
28556         el.addClass("x-dlg-draggable");
28557         if (!this.proxyDrag) {
28558             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28559         }
28560         else {
28561             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28562         }
28563         dd.setHandleElId(this.header.id);
28564         dd.endDrag = this.endMove.createDelegate(this);
28565         dd.startDrag = this.startMove.createDelegate(this);
28566         dd.onDrag = this.onDrag.createDelegate(this);
28567         dd.scroll = false;
28568         this.dd = dd;
28569     }
28570     if(this.modal){
28571         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28572         this.mask.enableDisplayMode("block");
28573         this.mask.hide();
28574         this.el.addClass("x-dlg-modal");
28575     }
28576     if(this.shadow){
28577         this.shadow = new Roo.Shadow({
28578             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28579             offset : this.shadowOffset
28580         });
28581     }else{
28582         this.shadowOffset = 0;
28583     }
28584     if(Roo.useShims && this.shim !== false){
28585         this.shim = this.el.createShim();
28586         this.shim.hide = this.hideAction;
28587         this.shim.hide();
28588     }else{
28589         this.shim = false;
28590     }
28591     if(this.autoTabs){
28592         this.initTabs();
28593     }
28594     if (this.buttons) { 
28595         var bts= this.buttons;
28596         this.buttons = [];
28597         Roo.each(bts, function(b) {
28598             this.addButton(b);
28599         }, this);
28600     }
28601     
28602     
28603     this.addEvents({
28604         /**
28605          * @event keydown
28606          * Fires when a key is pressed
28607          * @param {Roo.BasicDialog} this
28608          * @param {Roo.EventObject} e
28609          */
28610         "keydown" : true,
28611         /**
28612          * @event move
28613          * Fires when this dialog is moved by the user.
28614          * @param {Roo.BasicDialog} this
28615          * @param {Number} x The new page X
28616          * @param {Number} y The new page Y
28617          */
28618         "move" : true,
28619         /**
28620          * @event resize
28621          * Fires when this dialog is resized by the user.
28622          * @param {Roo.BasicDialog} this
28623          * @param {Number} width The new width
28624          * @param {Number} height The new height
28625          */
28626         "resize" : true,
28627         /**
28628          * @event beforehide
28629          * Fires before this dialog is hidden.
28630          * @param {Roo.BasicDialog} this
28631          */
28632         "beforehide" : true,
28633         /**
28634          * @event hide
28635          * Fires when this dialog is hidden.
28636          * @param {Roo.BasicDialog} this
28637          */
28638         "hide" : true,
28639         /**
28640          * @event beforeshow
28641          * Fires before this dialog is shown.
28642          * @param {Roo.BasicDialog} this
28643          */
28644         "beforeshow" : true,
28645         /**
28646          * @event show
28647          * Fires when this dialog is shown.
28648          * @param {Roo.BasicDialog} this
28649          */
28650         "show" : true
28651     });
28652     el.on("keydown", this.onKeyDown, this);
28653     el.on("mousedown", this.toFront, this);
28654     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28655     this.el.hide();
28656     Roo.DialogManager.register(this);
28657     Roo.BasicDialog.superclass.constructor.call(this);
28658 };
28659
28660 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28661     shadowOffset: Roo.isIE ? 6 : 5,
28662     minHeight: 80,
28663     minWidth: 200,
28664     minButtonWidth: 75,
28665     defaultButton: null,
28666     buttonAlign: "right",
28667     tabTag: 'div',
28668     firstShow: true,
28669
28670     /**
28671      * Sets the dialog title text
28672      * @param {String} text The title text to display
28673      * @return {Roo.BasicDialog} this
28674      */
28675     setTitle : function(text){
28676         this.header.update(text);
28677         return this;
28678     },
28679
28680     // private
28681     closeClick : function(){
28682         this.hide();
28683     },
28684
28685     // private
28686     collapseClick : function(){
28687         this[this.collapsed ? "expand" : "collapse"]();
28688     },
28689
28690     /**
28691      * Collapses the dialog to its minimized state (only the title bar is visible).
28692      * Equivalent to the user clicking the collapse dialog button.
28693      */
28694     collapse : function(){
28695         if(!this.collapsed){
28696             this.collapsed = true;
28697             this.el.addClass("x-dlg-collapsed");
28698             this.restoreHeight = this.el.getHeight();
28699             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28700         }
28701     },
28702
28703     /**
28704      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28705      * clicking the expand dialog button.
28706      */
28707     expand : function(){
28708         if(this.collapsed){
28709             this.collapsed = false;
28710             this.el.removeClass("x-dlg-collapsed");
28711             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28712         }
28713     },
28714
28715     /**
28716      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28717      * @return {Roo.TabPanel} The tabs component
28718      */
28719     initTabs : function(){
28720         var tabs = this.getTabs();
28721         while(tabs.getTab(0)){
28722             tabs.removeTab(0);
28723         }
28724         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28725             var dom = el.dom;
28726             tabs.addTab(Roo.id(dom), dom.title);
28727             dom.title = "";
28728         });
28729         tabs.activate(0);
28730         return tabs;
28731     },
28732
28733     // private
28734     beforeResize : function(){
28735         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28736     },
28737
28738     // private
28739     onResize : function(){
28740         this.refreshSize();
28741         this.syncBodyHeight();
28742         this.adjustAssets();
28743         this.focus();
28744         this.fireEvent("resize", this, this.size.width, this.size.height);
28745     },
28746
28747     // private
28748     onKeyDown : function(e){
28749         if(this.isVisible()){
28750             this.fireEvent("keydown", this, e);
28751         }
28752     },
28753
28754     /**
28755      * Resizes the dialog.
28756      * @param {Number} width
28757      * @param {Number} height
28758      * @return {Roo.BasicDialog} this
28759      */
28760     resizeTo : function(width, height){
28761         this.el.setSize(width, height);
28762         this.size = {width: width, height: height};
28763         this.syncBodyHeight();
28764         if(this.fixedcenter){
28765             this.center();
28766         }
28767         if(this.isVisible()){
28768             this.constrainXY();
28769             this.adjustAssets();
28770         }
28771         this.fireEvent("resize", this, width, height);
28772         return this;
28773     },
28774
28775
28776     /**
28777      * Resizes the dialog to fit the specified content size.
28778      * @param {Number} width
28779      * @param {Number} height
28780      * @return {Roo.BasicDialog} this
28781      */
28782     setContentSize : function(w, h){
28783         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28784         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28785         //if(!this.el.isBorderBox()){
28786             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28787             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28788         //}
28789         if(this.tabs){
28790             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28791             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28792         }
28793         this.resizeTo(w, h);
28794         return this;
28795     },
28796
28797     /**
28798      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28799      * executed in response to a particular key being pressed while the dialog is active.
28800      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28801      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28802      * @param {Function} fn The function to call
28803      * @param {Object} scope (optional) The scope of the function
28804      * @return {Roo.BasicDialog} this
28805      */
28806     addKeyListener : function(key, fn, scope){
28807         var keyCode, shift, ctrl, alt;
28808         if(typeof key == "object" && !(key instanceof Array)){
28809             keyCode = key["key"];
28810             shift = key["shift"];
28811             ctrl = key["ctrl"];
28812             alt = key["alt"];
28813         }else{
28814             keyCode = key;
28815         }
28816         var handler = function(dlg, e){
28817             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28818                 var k = e.getKey();
28819                 if(keyCode instanceof Array){
28820                     for(var i = 0, len = keyCode.length; i < len; i++){
28821                         if(keyCode[i] == k){
28822                           fn.call(scope || window, dlg, k, e);
28823                           return;
28824                         }
28825                     }
28826                 }else{
28827                     if(k == keyCode){
28828                         fn.call(scope || window, dlg, k, e);
28829                     }
28830                 }
28831             }
28832         };
28833         this.on("keydown", handler);
28834         return this;
28835     },
28836
28837     /**
28838      * Returns the TabPanel component (creates it if it doesn't exist).
28839      * Note: If you wish to simply check for the existence of tabs without creating them,
28840      * check for a null 'tabs' property.
28841      * @return {Roo.TabPanel} The tabs component
28842      */
28843     getTabs : function(){
28844         if(!this.tabs){
28845             this.el.addClass("x-dlg-auto-tabs");
28846             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28847             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28848         }
28849         return this.tabs;
28850     },
28851
28852     /**
28853      * Adds a button to the footer section of the dialog.
28854      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28855      * object or a valid Roo.DomHelper element config
28856      * @param {Function} handler The function called when the button is clicked
28857      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28858      * @return {Roo.Button} The new button
28859      */
28860     addButton : function(config, handler, scope){
28861         var dh = Roo.DomHelper;
28862         if(!this.footer){
28863             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28864         }
28865         if(!this.btnContainer){
28866             var tb = this.footer.createChild({
28867
28868                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28869                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28870             }, null, true);
28871             this.btnContainer = tb.firstChild.firstChild.firstChild;
28872         }
28873         var bconfig = {
28874             handler: handler,
28875             scope: scope,
28876             minWidth: this.minButtonWidth,
28877             hideParent:true
28878         };
28879         if(typeof config == "string"){
28880             bconfig.text = config;
28881         }else{
28882             if(config.tag){
28883                 bconfig.dhconfig = config;
28884             }else{
28885                 Roo.apply(bconfig, config);
28886             }
28887         }
28888         var fc = false;
28889         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28890             bconfig.position = Math.max(0, bconfig.position);
28891             fc = this.btnContainer.childNodes[bconfig.position];
28892         }
28893          
28894         var btn = new Roo.Button(
28895             fc ? 
28896                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28897                 : this.btnContainer.appendChild(document.createElement("td")),
28898             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28899             bconfig
28900         );
28901         this.syncBodyHeight();
28902         if(!this.buttons){
28903             /**
28904              * Array of all the buttons that have been added to this dialog via addButton
28905              * @type Array
28906              */
28907             this.buttons = [];
28908         }
28909         this.buttons.push(btn);
28910         return btn;
28911     },
28912
28913     /**
28914      * Sets the default button to be focused when the dialog is displayed.
28915      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28916      * @return {Roo.BasicDialog} this
28917      */
28918     setDefaultButton : function(btn){
28919         this.defaultButton = btn;
28920         return this;
28921     },
28922
28923     // private
28924     getHeaderFooterHeight : function(safe){
28925         var height = 0;
28926         if(this.header){
28927            height += this.header.getHeight();
28928         }
28929         if(this.footer){
28930            var fm = this.footer.getMargins();
28931             height += (this.footer.getHeight()+fm.top+fm.bottom);
28932         }
28933         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28934         height += this.centerBg.getPadding("tb");
28935         return height;
28936     },
28937
28938     // private
28939     syncBodyHeight : function(){
28940         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28941         var height = this.size.height - this.getHeaderFooterHeight(false);
28942         bd.setHeight(height-bd.getMargins("tb"));
28943         var hh = this.header.getHeight();
28944         var h = this.size.height-hh;
28945         cb.setHeight(h);
28946         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28947         bw.setHeight(h-cb.getPadding("tb"));
28948         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28949         bd.setWidth(bw.getWidth(true));
28950         if(this.tabs){
28951             this.tabs.syncHeight();
28952             if(Roo.isIE){
28953                 this.tabs.el.repaint();
28954             }
28955         }
28956     },
28957
28958     /**
28959      * Restores the previous state of the dialog if Roo.state is configured.
28960      * @return {Roo.BasicDialog} this
28961      */
28962     restoreState : function(){
28963         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28964         if(box && box.width){
28965             this.xy = [box.x, box.y];
28966             this.resizeTo(box.width, box.height);
28967         }
28968         return this;
28969     },
28970
28971     // private
28972     beforeShow : function(){
28973         this.expand();
28974         if(this.fixedcenter){
28975             this.xy = this.el.getCenterXY(true);
28976         }
28977         if(this.modal){
28978             Roo.get(document.body).addClass("x-body-masked");
28979             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28980             this.mask.show();
28981         }
28982         this.constrainXY();
28983     },
28984
28985     // private
28986     animShow : function(){
28987         var b = Roo.get(this.animateTarget).getBox();
28988         this.proxy.setSize(b.width, b.height);
28989         this.proxy.setLocation(b.x, b.y);
28990         this.proxy.show();
28991         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28992                     true, .35, this.showEl.createDelegate(this));
28993     },
28994
28995     /**
28996      * Shows the dialog.
28997      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28998      * @return {Roo.BasicDialog} this
28999      */
29000     show : function(animateTarget){
29001         if (this.fireEvent("beforeshow", this) === false){
29002             return;
29003         }
29004         if(this.syncHeightBeforeShow){
29005             this.syncBodyHeight();
29006         }else if(this.firstShow){
29007             this.firstShow = false;
29008             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29009         }
29010         this.animateTarget = animateTarget || this.animateTarget;
29011         if(!this.el.isVisible()){
29012             this.beforeShow();
29013             if(this.animateTarget && Roo.get(this.animateTarget)){
29014                 this.animShow();
29015             }else{
29016                 this.showEl();
29017             }
29018         }
29019         return this;
29020     },
29021
29022     // private
29023     showEl : function(){
29024         this.proxy.hide();
29025         this.el.setXY(this.xy);
29026         this.el.show();
29027         this.adjustAssets(true);
29028         this.toFront();
29029         this.focus();
29030         // IE peekaboo bug - fix found by Dave Fenwick
29031         if(Roo.isIE){
29032             this.el.repaint();
29033         }
29034         this.fireEvent("show", this);
29035     },
29036
29037     /**
29038      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29039      * dialog itself will receive focus.
29040      */
29041     focus : function(){
29042         if(this.defaultButton){
29043             this.defaultButton.focus();
29044         }else{
29045             this.focusEl.focus();
29046         }
29047     },
29048
29049     // private
29050     constrainXY : function(){
29051         if(this.constraintoviewport !== false){
29052             if(!this.viewSize){
29053                 if(this.container){
29054                     var s = this.container.getSize();
29055                     this.viewSize = [s.width, s.height];
29056                 }else{
29057                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29058                 }
29059             }
29060             var s = Roo.get(this.container||document).getScroll();
29061
29062             var x = this.xy[0], y = this.xy[1];
29063             var w = this.size.width, h = this.size.height;
29064             var vw = this.viewSize[0], vh = this.viewSize[1];
29065             // only move it if it needs it
29066             var moved = false;
29067             // first validate right/bottom
29068             if(x + w > vw+s.left){
29069                 x = vw - w;
29070                 moved = true;
29071             }
29072             if(y + h > vh+s.top){
29073                 y = vh - h;
29074                 moved = true;
29075             }
29076             // then make sure top/left isn't negative
29077             if(x < s.left){
29078                 x = s.left;
29079                 moved = true;
29080             }
29081             if(y < s.top){
29082                 y = s.top;
29083                 moved = true;
29084             }
29085             if(moved){
29086                 // cache xy
29087                 this.xy = [x, y];
29088                 if(this.isVisible()){
29089                     this.el.setLocation(x, y);
29090                     this.adjustAssets();
29091                 }
29092             }
29093         }
29094     },
29095
29096     // private
29097     onDrag : function(){
29098         if(!this.proxyDrag){
29099             this.xy = this.el.getXY();
29100             this.adjustAssets();
29101         }
29102     },
29103
29104     // private
29105     adjustAssets : function(doShow){
29106         var x = this.xy[0], y = this.xy[1];
29107         var w = this.size.width, h = this.size.height;
29108         if(doShow === true){
29109             if(this.shadow){
29110                 this.shadow.show(this.el);
29111             }
29112             if(this.shim){
29113                 this.shim.show();
29114             }
29115         }
29116         if(this.shadow && this.shadow.isVisible()){
29117             this.shadow.show(this.el);
29118         }
29119         if(this.shim && this.shim.isVisible()){
29120             this.shim.setBounds(x, y, w, h);
29121         }
29122     },
29123
29124     // private
29125     adjustViewport : function(w, h){
29126         if(!w || !h){
29127             w = Roo.lib.Dom.getViewWidth();
29128             h = Roo.lib.Dom.getViewHeight();
29129         }
29130         // cache the size
29131         this.viewSize = [w, h];
29132         if(this.modal && this.mask.isVisible()){
29133             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29134             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29135         }
29136         if(this.isVisible()){
29137             this.constrainXY();
29138         }
29139     },
29140
29141     /**
29142      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29143      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29144      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29145      */
29146     destroy : function(removeEl){
29147         if(this.isVisible()){
29148             this.animateTarget = null;
29149             this.hide();
29150         }
29151         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29152         if(this.tabs){
29153             this.tabs.destroy(removeEl);
29154         }
29155         Roo.destroy(
29156              this.shim,
29157              this.proxy,
29158              this.resizer,
29159              this.close,
29160              this.mask
29161         );
29162         if(this.dd){
29163             this.dd.unreg();
29164         }
29165         if(this.buttons){
29166            for(var i = 0, len = this.buttons.length; i < len; i++){
29167                this.buttons[i].destroy();
29168            }
29169         }
29170         this.el.removeAllListeners();
29171         if(removeEl === true){
29172             this.el.update("");
29173             this.el.remove();
29174         }
29175         Roo.DialogManager.unregister(this);
29176     },
29177
29178     // private
29179     startMove : function(){
29180         if(this.proxyDrag){
29181             this.proxy.show();
29182         }
29183         if(this.constraintoviewport !== false){
29184             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29185         }
29186     },
29187
29188     // private
29189     endMove : function(){
29190         if(!this.proxyDrag){
29191             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29192         }else{
29193             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29194             this.proxy.hide();
29195         }
29196         this.refreshSize();
29197         this.adjustAssets();
29198         this.focus();
29199         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29200     },
29201
29202     /**
29203      * Brings this dialog to the front of any other visible dialogs
29204      * @return {Roo.BasicDialog} this
29205      */
29206     toFront : function(){
29207         Roo.DialogManager.bringToFront(this);
29208         return this;
29209     },
29210
29211     /**
29212      * Sends this dialog to the back (under) of any other visible dialogs
29213      * @return {Roo.BasicDialog} this
29214      */
29215     toBack : function(){
29216         Roo.DialogManager.sendToBack(this);
29217         return this;
29218     },
29219
29220     /**
29221      * Centers this dialog in the viewport
29222      * @return {Roo.BasicDialog} this
29223      */
29224     center : function(){
29225         var xy = this.el.getCenterXY(true);
29226         this.moveTo(xy[0], xy[1]);
29227         return this;
29228     },
29229
29230     /**
29231      * Moves the dialog's top-left corner to the specified point
29232      * @param {Number} x
29233      * @param {Number} y
29234      * @return {Roo.BasicDialog} this
29235      */
29236     moveTo : function(x, y){
29237         this.xy = [x,y];
29238         if(this.isVisible()){
29239             this.el.setXY(this.xy);
29240             this.adjustAssets();
29241         }
29242         return this;
29243     },
29244
29245     /**
29246      * Aligns the dialog to the specified element
29247      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29248      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29249      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29250      * @return {Roo.BasicDialog} this
29251      */
29252     alignTo : function(element, position, offsets){
29253         this.xy = this.el.getAlignToXY(element, position, offsets);
29254         if(this.isVisible()){
29255             this.el.setXY(this.xy);
29256             this.adjustAssets();
29257         }
29258         return this;
29259     },
29260
29261     /**
29262      * Anchors an element to another element and realigns it when the window is resized.
29263      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29264      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29265      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29266      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29267      * is a number, it is used as the buffer delay (defaults to 50ms).
29268      * @return {Roo.BasicDialog} this
29269      */
29270     anchorTo : function(el, alignment, offsets, monitorScroll){
29271         var action = function(){
29272             this.alignTo(el, alignment, offsets);
29273         };
29274         Roo.EventManager.onWindowResize(action, this);
29275         var tm = typeof monitorScroll;
29276         if(tm != 'undefined'){
29277             Roo.EventManager.on(window, 'scroll', action, this,
29278                 {buffer: tm == 'number' ? monitorScroll : 50});
29279         }
29280         action.call(this);
29281         return this;
29282     },
29283
29284     /**
29285      * Returns true if the dialog is visible
29286      * @return {Boolean}
29287      */
29288     isVisible : function(){
29289         return this.el.isVisible();
29290     },
29291
29292     // private
29293     animHide : function(callback){
29294         var b = Roo.get(this.animateTarget).getBox();
29295         this.proxy.show();
29296         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29297         this.el.hide();
29298         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29299                     this.hideEl.createDelegate(this, [callback]));
29300     },
29301
29302     /**
29303      * Hides the dialog.
29304      * @param {Function} callback (optional) Function to call when the dialog is hidden
29305      * @return {Roo.BasicDialog} this
29306      */
29307     hide : function(callback){
29308         if (this.fireEvent("beforehide", this) === false){
29309             return;
29310         }
29311         if(this.shadow){
29312             this.shadow.hide();
29313         }
29314         if(this.shim) {
29315           this.shim.hide();
29316         }
29317         // sometimes animateTarget seems to get set.. causing problems...
29318         // this just double checks..
29319         if(this.animateTarget && Roo.get(this.animateTarget)) {
29320            this.animHide(callback);
29321         }else{
29322             this.el.hide();
29323             this.hideEl(callback);
29324         }
29325         return this;
29326     },
29327
29328     // private
29329     hideEl : function(callback){
29330         this.proxy.hide();
29331         if(this.modal){
29332             this.mask.hide();
29333             Roo.get(document.body).removeClass("x-body-masked");
29334         }
29335         this.fireEvent("hide", this);
29336         if(typeof callback == "function"){
29337             callback();
29338         }
29339     },
29340
29341     // private
29342     hideAction : function(){
29343         this.setLeft("-10000px");
29344         this.setTop("-10000px");
29345         this.setStyle("visibility", "hidden");
29346     },
29347
29348     // private
29349     refreshSize : function(){
29350         this.size = this.el.getSize();
29351         this.xy = this.el.getXY();
29352         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29353     },
29354
29355     // private
29356     // z-index is managed by the DialogManager and may be overwritten at any time
29357     setZIndex : function(index){
29358         if(this.modal){
29359             this.mask.setStyle("z-index", index);
29360         }
29361         if(this.shim){
29362             this.shim.setStyle("z-index", ++index);
29363         }
29364         if(this.shadow){
29365             this.shadow.setZIndex(++index);
29366         }
29367         this.el.setStyle("z-index", ++index);
29368         if(this.proxy){
29369             this.proxy.setStyle("z-index", ++index);
29370         }
29371         if(this.resizer){
29372             this.resizer.proxy.setStyle("z-index", ++index);
29373         }
29374
29375         this.lastZIndex = index;
29376     },
29377
29378     /**
29379      * Returns the element for this dialog
29380      * @return {Roo.Element} The underlying dialog Element
29381      */
29382     getEl : function(){
29383         return this.el;
29384     }
29385 });
29386
29387 /**
29388  * @class Roo.DialogManager
29389  * Provides global access to BasicDialogs that have been created and
29390  * support for z-indexing (layering) multiple open dialogs.
29391  */
29392 Roo.DialogManager = function(){
29393     var list = {};
29394     var accessList = [];
29395     var front = null;
29396
29397     // private
29398     var sortDialogs = function(d1, d2){
29399         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29400     };
29401
29402     // private
29403     var orderDialogs = function(){
29404         accessList.sort(sortDialogs);
29405         var seed = Roo.DialogManager.zseed;
29406         for(var i = 0, len = accessList.length; i < len; i++){
29407             var dlg = accessList[i];
29408             if(dlg){
29409                 dlg.setZIndex(seed + (i*10));
29410             }
29411         }
29412     };
29413
29414     return {
29415         /**
29416          * The starting z-index for BasicDialogs (defaults to 9000)
29417          * @type Number The z-index value
29418          */
29419         zseed : 9000,
29420
29421         // private
29422         register : function(dlg){
29423             list[dlg.id] = dlg;
29424             accessList.push(dlg);
29425         },
29426
29427         // private
29428         unregister : function(dlg){
29429             delete list[dlg.id];
29430             var i=0;
29431             var len=0;
29432             if(!accessList.indexOf){
29433                 for(  i = 0, len = accessList.length; i < len; i++){
29434                     if(accessList[i] == dlg){
29435                         accessList.splice(i, 1);
29436                         return;
29437                     }
29438                 }
29439             }else{
29440                  i = accessList.indexOf(dlg);
29441                 if(i != -1){
29442                     accessList.splice(i, 1);
29443                 }
29444             }
29445         },
29446
29447         /**
29448          * Gets a registered dialog by id
29449          * @param {String/Object} id The id of the dialog or a dialog
29450          * @return {Roo.BasicDialog} this
29451          */
29452         get : function(id){
29453             return typeof id == "object" ? id : list[id];
29454         },
29455
29456         /**
29457          * Brings the specified dialog to the front
29458          * @param {String/Object} dlg The id of the dialog or a dialog
29459          * @return {Roo.BasicDialog} this
29460          */
29461         bringToFront : function(dlg){
29462             dlg = this.get(dlg);
29463             if(dlg != front){
29464                 front = dlg;
29465                 dlg._lastAccess = new Date().getTime();
29466                 orderDialogs();
29467             }
29468             return dlg;
29469         },
29470
29471         /**
29472          * Sends the specified dialog to the back
29473          * @param {String/Object} dlg The id of the dialog or a dialog
29474          * @return {Roo.BasicDialog} this
29475          */
29476         sendToBack : function(dlg){
29477             dlg = this.get(dlg);
29478             dlg._lastAccess = -(new Date().getTime());
29479             orderDialogs();
29480             return dlg;
29481         },
29482
29483         /**
29484          * Hides all dialogs
29485          */
29486         hideAll : function(){
29487             for(var id in list){
29488                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29489                     list[id].hide();
29490                 }
29491             }
29492         }
29493     };
29494 }();
29495
29496 /**
29497  * @class Roo.LayoutDialog
29498  * @extends Roo.BasicDialog
29499  * Dialog which provides adjustments for working with a layout in a Dialog.
29500  * Add your necessary layout config options to the dialog's config.<br>
29501  * Example usage (including a nested layout):
29502  * <pre><code>
29503 if(!dialog){
29504     dialog = new Roo.LayoutDialog("download-dlg", {
29505         modal: true,
29506         width:600,
29507         height:450,
29508         shadow:true,
29509         minWidth:500,
29510         minHeight:350,
29511         autoTabs:true,
29512         proxyDrag:true,
29513         // layout config merges with the dialog config
29514         center:{
29515             tabPosition: "top",
29516             alwaysShowTabs: true
29517         }
29518     });
29519     dialog.addKeyListener(27, dialog.hide, dialog);
29520     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29521     dialog.addButton("Build It!", this.getDownload, this);
29522
29523     // we can even add nested layouts
29524     var innerLayout = new Roo.BorderLayout("dl-inner", {
29525         east: {
29526             initialSize: 200,
29527             autoScroll:true,
29528             split:true
29529         },
29530         center: {
29531             autoScroll:true
29532         }
29533     });
29534     innerLayout.beginUpdate();
29535     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29536     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29537     innerLayout.endUpdate(true);
29538
29539     var layout = dialog.getLayout();
29540     layout.beginUpdate();
29541     layout.add("center", new Roo.ContentPanel("standard-panel",
29542                         {title: "Download the Source", fitToFrame:true}));
29543     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29544                {title: "Build your own roo.js"}));
29545     layout.getRegion("center").showPanel(sp);
29546     layout.endUpdate();
29547 }
29548 </code></pre>
29549     * @constructor
29550     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29551     * @param {Object} config configuration options
29552   */
29553 Roo.LayoutDialog = function(el, cfg){
29554     
29555     var config=  cfg;
29556     if (typeof(cfg) == 'undefined') {
29557         config = Roo.apply({}, el);
29558         // not sure why we use documentElement here.. - it should always be body.
29559         // IE7 borks horribly if we use documentElement.
29560         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
29561         //config.autoCreate = true;
29562     }
29563     
29564     
29565     config.autoTabs = false;
29566     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29567     this.body.setStyle({overflow:"hidden", position:"relative"});
29568     this.layout = new Roo.BorderLayout(this.body.dom, config);
29569     this.layout.monitorWindowResize = false;
29570     this.el.addClass("x-dlg-auto-layout");
29571     // fix case when center region overwrites center function
29572     this.center = Roo.BasicDialog.prototype.center;
29573     this.on("show", this.layout.layout, this.layout, true);
29574     if (config.items) {
29575         var xitems = config.items;
29576         delete config.items;
29577         Roo.each(xitems, this.addxtype, this);
29578     }
29579     
29580     
29581 };
29582 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29583     /**
29584      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29585      * @deprecated
29586      */
29587     endUpdate : function(){
29588         this.layout.endUpdate();
29589     },
29590
29591     /**
29592      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29593      *  @deprecated
29594      */
29595     beginUpdate : function(){
29596         this.layout.beginUpdate();
29597     },
29598
29599     /**
29600      * Get the BorderLayout for this dialog
29601      * @return {Roo.BorderLayout}
29602      */
29603     getLayout : function(){
29604         return this.layout;
29605     },
29606
29607     showEl : function(){
29608         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29609         if(Roo.isIE7){
29610             this.layout.layout();
29611         }
29612     },
29613
29614     // private
29615     // Use the syncHeightBeforeShow config option to control this automatically
29616     syncBodyHeight : function(){
29617         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29618         if(this.layout){this.layout.layout();}
29619     },
29620     
29621       /**
29622      * Add an xtype element (actually adds to the layout.)
29623      * @return {Object} xdata xtype object data.
29624      */
29625     
29626     addxtype : function(c) {
29627         return this.layout.addxtype(c);
29628     }
29629 });/*
29630  * Based on:
29631  * Ext JS Library 1.1.1
29632  * Copyright(c) 2006-2007, Ext JS, LLC.
29633  *
29634  * Originally Released Under LGPL - original licence link has changed is not relivant.
29635  *
29636  * Fork - LGPL
29637  * <script type="text/javascript">
29638  */
29639  
29640 /**
29641  * @class Roo.MessageBox
29642  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29643  * Example usage:
29644  *<pre><code>
29645 // Basic alert:
29646 Roo.Msg.alert('Status', 'Changes saved successfully.');
29647
29648 // Prompt for user data:
29649 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29650     if (btn == 'ok'){
29651         // process text value...
29652     }
29653 });
29654
29655 // Show a dialog using config options:
29656 Roo.Msg.show({
29657    title:'Save Changes?',
29658    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29659    buttons: Roo.Msg.YESNOCANCEL,
29660    fn: processResult,
29661    animEl: 'elId'
29662 });
29663 </code></pre>
29664  * @singleton
29665  */
29666 Roo.MessageBox = function(){
29667     var dlg, opt, mask, waitTimer;
29668     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29669     var buttons, activeTextEl, bwidth;
29670
29671     // private
29672     var handleButton = function(button){
29673         dlg.hide();
29674         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29675     };
29676
29677     // private
29678     var handleHide = function(){
29679         if(opt && opt.cls){
29680             dlg.el.removeClass(opt.cls);
29681         }
29682         if(waitTimer){
29683             Roo.TaskMgr.stop(waitTimer);
29684             waitTimer = null;
29685         }
29686     };
29687
29688     // private
29689     var updateButtons = function(b){
29690         var width = 0;
29691         if(!b){
29692             buttons["ok"].hide();
29693             buttons["cancel"].hide();
29694             buttons["yes"].hide();
29695             buttons["no"].hide();
29696             dlg.footer.dom.style.display = 'none';
29697             return width;
29698         }
29699         dlg.footer.dom.style.display = '';
29700         for(var k in buttons){
29701             if(typeof buttons[k] != "function"){
29702                 if(b[k]){
29703                     buttons[k].show();
29704                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29705                     width += buttons[k].el.getWidth()+15;
29706                 }else{
29707                     buttons[k].hide();
29708                 }
29709             }
29710         }
29711         return width;
29712     };
29713
29714     // private
29715     var handleEsc = function(d, k, e){
29716         if(opt && opt.closable !== false){
29717             dlg.hide();
29718         }
29719         if(e){
29720             e.stopEvent();
29721         }
29722     };
29723
29724     return {
29725         /**
29726          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29727          * @return {Roo.BasicDialog} The BasicDialog element
29728          */
29729         getDialog : function(){
29730            if(!dlg){
29731                 dlg = new Roo.BasicDialog("x-msg-box", {
29732                     autoCreate : true,
29733                     shadow: true,
29734                     draggable: true,
29735                     resizable:false,
29736                     constraintoviewport:false,
29737                     fixedcenter:true,
29738                     collapsible : false,
29739                     shim:true,
29740                     modal: true,
29741                     width:400, height:100,
29742                     buttonAlign:"center",
29743                     closeClick : function(){
29744                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29745                             handleButton("no");
29746                         }else{
29747                             handleButton("cancel");
29748                         }
29749                     }
29750                 });
29751                 dlg.on("hide", handleHide);
29752                 mask = dlg.mask;
29753                 dlg.addKeyListener(27, handleEsc);
29754                 buttons = {};
29755                 var bt = this.buttonText;
29756                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29757                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29758                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29759                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29760                 bodyEl = dlg.body.createChild({
29761
29762                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
29763                 });
29764                 msgEl = bodyEl.dom.firstChild;
29765                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29766                 textboxEl.enableDisplayMode();
29767                 textboxEl.addKeyListener([10,13], function(){
29768                     if(dlg.isVisible() && opt && opt.buttons){
29769                         if(opt.buttons.ok){
29770                             handleButton("ok");
29771                         }else if(opt.buttons.yes){
29772                             handleButton("yes");
29773                         }
29774                     }
29775                 });
29776                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29777                 textareaEl.enableDisplayMode();
29778                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29779                 progressEl.enableDisplayMode();
29780                 var pf = progressEl.dom.firstChild;
29781                 if (pf) {
29782                     pp = Roo.get(pf.firstChild);
29783                     pp.setHeight(pf.offsetHeight);
29784                 }
29785                 
29786             }
29787             return dlg;
29788         },
29789
29790         /**
29791          * Updates the message box body text
29792          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29793          * the XHTML-compliant non-breaking space character '&amp;#160;')
29794          * @return {Roo.MessageBox} This message box
29795          */
29796         updateText : function(text){
29797             if(!dlg.isVisible() && !opt.width){
29798                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29799             }
29800             msgEl.innerHTML = text || '&#160;';
29801             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29802                         Math.max(opt.minWidth || this.minWidth, bwidth));
29803             if(opt.prompt){
29804                 activeTextEl.setWidth(w);
29805             }
29806             if(dlg.isVisible()){
29807                 dlg.fixedcenter = false;
29808             }
29809             dlg.setContentSize(w, bodyEl.getHeight());
29810             if(dlg.isVisible()){
29811                 dlg.fixedcenter = true;
29812             }
29813             return this;
29814         },
29815
29816         /**
29817          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29818          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29819          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29820          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29821          * @return {Roo.MessageBox} This message box
29822          */
29823         updateProgress : function(value, text){
29824             if(text){
29825                 this.updateText(text);
29826             }
29827             if (pp) { // weird bug on my firefox - for some reason this is not defined
29828                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29829             }
29830             return this;
29831         },        
29832
29833         /**
29834          * Returns true if the message box is currently displayed
29835          * @return {Boolean} True if the message box is visible, else false
29836          */
29837         isVisible : function(){
29838             return dlg && dlg.isVisible();  
29839         },
29840
29841         /**
29842          * Hides the message box if it is displayed
29843          */
29844         hide : function(){
29845             if(this.isVisible()){
29846                 dlg.hide();
29847             }  
29848         },
29849
29850         /**
29851          * Displays a new message box, or reinitializes an existing message box, based on the config options
29852          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29853          * The following config object properties are supported:
29854          * <pre>
29855 Property    Type             Description
29856 ----------  ---------------  ------------------------------------------------------------------------------------
29857 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29858                                    closes (defaults to undefined)
29859 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29860                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29861 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29862                                    progress and wait dialogs will ignore this property and always hide the
29863                                    close button as they can only be closed programmatically.
29864 cls               String           A custom CSS class to apply to the message box element
29865 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29866                                    displayed (defaults to 75)
29867 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29868                                    function will be btn (the name of the button that was clicked, if applicable,
29869                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29870                                    Progress and wait dialogs will ignore this option since they do not respond to
29871                                    user actions and can only be closed programmatically, so any required function
29872                                    should be called by the same code after it closes the dialog.
29873 icon              String           A CSS class that provides a background image to be used as an icon for
29874                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29875 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29876 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29877 modal             Boolean          False to allow user interaction with the page while the message box is
29878                                    displayed (defaults to true)
29879 msg               String           A string that will replace the existing message box body text (defaults
29880                                    to the XHTML-compliant non-breaking space character '&#160;')
29881 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29882 progress          Boolean          True to display a progress bar (defaults to false)
29883 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29884 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29885 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29886 title             String           The title text
29887 value             String           The string value to set into the active textbox element if displayed
29888 wait              Boolean          True to display a progress bar (defaults to false)
29889 width             Number           The width of the dialog in pixels
29890 </pre>
29891          *
29892          * Example usage:
29893          * <pre><code>
29894 Roo.Msg.show({
29895    title: 'Address',
29896    msg: 'Please enter your address:',
29897    width: 300,
29898    buttons: Roo.MessageBox.OKCANCEL,
29899    multiline: true,
29900    fn: saveAddress,
29901    animEl: 'addAddressBtn'
29902 });
29903 </code></pre>
29904          * @param {Object} config Configuration options
29905          * @return {Roo.MessageBox} This message box
29906          */
29907         show : function(options){
29908             if(this.isVisible()){
29909                 this.hide();
29910             }
29911             var d = this.getDialog();
29912             opt = options;
29913             d.setTitle(opt.title || "&#160;");
29914             d.close.setDisplayed(opt.closable !== false);
29915             activeTextEl = textboxEl;
29916             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29917             if(opt.prompt){
29918                 if(opt.multiline){
29919                     textboxEl.hide();
29920                     textareaEl.show();
29921                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29922                         opt.multiline : this.defaultTextHeight);
29923                     activeTextEl = textareaEl;
29924                 }else{
29925                     textboxEl.show();
29926                     textareaEl.hide();
29927                 }
29928             }else{
29929                 textboxEl.hide();
29930                 textareaEl.hide();
29931             }
29932             progressEl.setDisplayed(opt.progress === true);
29933             this.updateProgress(0);
29934             activeTextEl.dom.value = opt.value || "";
29935             if(opt.prompt){
29936                 dlg.setDefaultButton(activeTextEl);
29937             }else{
29938                 var bs = opt.buttons;
29939                 var db = null;
29940                 if(bs && bs.ok){
29941                     db = buttons["ok"];
29942                 }else if(bs && bs.yes){
29943                     db = buttons["yes"];
29944                 }
29945                 dlg.setDefaultButton(db);
29946             }
29947             bwidth = updateButtons(opt.buttons);
29948             this.updateText(opt.msg);
29949             if(opt.cls){
29950                 d.el.addClass(opt.cls);
29951             }
29952             d.proxyDrag = opt.proxyDrag === true;
29953             d.modal = opt.modal !== false;
29954             d.mask = opt.modal !== false ? mask : false;
29955             if(!d.isVisible()){
29956                 // force it to the end of the z-index stack so it gets a cursor in FF
29957                 document.body.appendChild(dlg.el.dom);
29958                 d.animateTarget = null;
29959                 d.show(options.animEl);
29960             }
29961             return this;
29962         },
29963
29964         /**
29965          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29966          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29967          * and closing the message box when the process is complete.
29968          * @param {String} title The title bar text
29969          * @param {String} msg The message box body text
29970          * @return {Roo.MessageBox} This message box
29971          */
29972         progress : function(title, msg){
29973             this.show({
29974                 title : title,
29975                 msg : msg,
29976                 buttons: false,
29977                 progress:true,
29978                 closable:false,
29979                 minWidth: this.minProgressWidth,
29980                 modal : true
29981             });
29982             return this;
29983         },
29984
29985         /**
29986          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29987          * If a callback function is passed it will be called after the user clicks the button, and the
29988          * id of the button that was clicked will be passed as the only parameter to the callback
29989          * (could also be the top-right close button).
29990          * @param {String} title The title bar text
29991          * @param {String} msg The message box body text
29992          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29993          * @param {Object} scope (optional) The scope of the callback function
29994          * @return {Roo.MessageBox} This message box
29995          */
29996         alert : function(title, msg, fn, scope){
29997             this.show({
29998                 title : title,
29999                 msg : msg,
30000                 buttons: this.OK,
30001                 fn: fn,
30002                 scope : scope,
30003                 modal : true
30004             });
30005             return this;
30006         },
30007
30008         /**
30009          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30010          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30011          * You are responsible for closing the message box when the process is complete.
30012          * @param {String} msg The message box body text
30013          * @param {String} title (optional) The title bar text
30014          * @return {Roo.MessageBox} This message box
30015          */
30016         wait : function(msg, title){
30017             this.show({
30018                 title : title,
30019                 msg : msg,
30020                 buttons: false,
30021                 closable:false,
30022                 progress:true,
30023                 modal:true,
30024                 width:300,
30025                 wait:true
30026             });
30027             waitTimer = Roo.TaskMgr.start({
30028                 run: function(i){
30029                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30030                 },
30031                 interval: 1000
30032             });
30033             return this;
30034         },
30035
30036         /**
30037          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30038          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30039          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30040          * @param {String} title The title bar text
30041          * @param {String} msg The message box body text
30042          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30043          * @param {Object} scope (optional) The scope of the callback function
30044          * @return {Roo.MessageBox} This message box
30045          */
30046         confirm : function(title, msg, fn, scope){
30047             this.show({
30048                 title : title,
30049                 msg : msg,
30050                 buttons: this.YESNO,
30051                 fn: fn,
30052                 scope : scope,
30053                 modal : true
30054             });
30055             return this;
30056         },
30057
30058         /**
30059          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30060          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30061          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30062          * (could also be the top-right close button) and the text that was entered will be passed as the two
30063          * parameters to the callback.
30064          * @param {String} title The title bar text
30065          * @param {String} msg The message box body text
30066          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30067          * @param {Object} scope (optional) The scope of the callback function
30068          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30069          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30070          * @return {Roo.MessageBox} This message box
30071          */
30072         prompt : function(title, msg, fn, scope, multiline){
30073             this.show({
30074                 title : title,
30075                 msg : msg,
30076                 buttons: this.OKCANCEL,
30077                 fn: fn,
30078                 minWidth:250,
30079                 scope : scope,
30080                 prompt:true,
30081                 multiline: multiline,
30082                 modal : true
30083             });
30084             return this;
30085         },
30086
30087         /**
30088          * Button config that displays a single OK button
30089          * @type Object
30090          */
30091         OK : {ok:true},
30092         /**
30093          * Button config that displays Yes and No buttons
30094          * @type Object
30095          */
30096         YESNO : {yes:true, no:true},
30097         /**
30098          * Button config that displays OK and Cancel buttons
30099          * @type Object
30100          */
30101         OKCANCEL : {ok:true, cancel:true},
30102         /**
30103          * Button config that displays Yes, No and Cancel buttons
30104          * @type Object
30105          */
30106         YESNOCANCEL : {yes:true, no:true, cancel:true},
30107
30108         /**
30109          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30110          * @type Number
30111          */
30112         defaultTextHeight : 75,
30113         /**
30114          * The maximum width in pixels of the message box (defaults to 600)
30115          * @type Number
30116          */
30117         maxWidth : 600,
30118         /**
30119          * The minimum width in pixels of the message box (defaults to 100)
30120          * @type Number
30121          */
30122         minWidth : 100,
30123         /**
30124          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30125          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30126          * @type Number
30127          */
30128         minProgressWidth : 250,
30129         /**
30130          * An object containing the default button text strings that can be overriden for localized language support.
30131          * Supported properties are: ok, cancel, yes and no.
30132          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30133          * @type Object
30134          */
30135         buttonText : {
30136             ok : "OK",
30137             cancel : "Cancel",
30138             yes : "Yes",
30139             no : "No"
30140         }
30141     };
30142 }();
30143
30144 /**
30145  * Shorthand for {@link Roo.MessageBox}
30146  */
30147 Roo.Msg = Roo.MessageBox;/*
30148  * Based on:
30149  * Ext JS Library 1.1.1
30150  * Copyright(c) 2006-2007, Ext JS, LLC.
30151  *
30152  * Originally Released Under LGPL - original licence link has changed is not relivant.
30153  *
30154  * Fork - LGPL
30155  * <script type="text/javascript">
30156  */
30157 /**
30158  * @class Roo.QuickTips
30159  * Provides attractive and customizable tooltips for any element.
30160  * @singleton
30161  */
30162 Roo.QuickTips = function(){
30163     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30164     var ce, bd, xy, dd;
30165     var visible = false, disabled = true, inited = false;
30166     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30167     
30168     var onOver = function(e){
30169         if(disabled){
30170             return;
30171         }
30172         var t = e.getTarget();
30173         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30174             return;
30175         }
30176         if(ce && t == ce.el){
30177             clearTimeout(hideProc);
30178             return;
30179         }
30180         if(t && tagEls[t.id]){
30181             tagEls[t.id].el = t;
30182             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30183             return;
30184         }
30185         var ttp, et = Roo.fly(t);
30186         var ns = cfg.namespace;
30187         if(tm.interceptTitles && t.title){
30188             ttp = t.title;
30189             t.qtip = ttp;
30190             t.removeAttribute("title");
30191             e.preventDefault();
30192         }else{
30193             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30194         }
30195         if(ttp){
30196             showProc = show.defer(tm.showDelay, tm, [{
30197                 el: t, 
30198                 text: ttp, 
30199                 width: et.getAttributeNS(ns, cfg.width),
30200                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30201                 title: et.getAttributeNS(ns, cfg.title),
30202                     cls: et.getAttributeNS(ns, cfg.cls)
30203             }]);
30204         }
30205     };
30206     
30207     var onOut = function(e){
30208         clearTimeout(showProc);
30209         var t = e.getTarget();
30210         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30211             hideProc = setTimeout(hide, tm.hideDelay);
30212         }
30213     };
30214     
30215     var onMove = function(e){
30216         if(disabled){
30217             return;
30218         }
30219         xy = e.getXY();
30220         xy[1] += 18;
30221         if(tm.trackMouse && ce){
30222             el.setXY(xy);
30223         }
30224     };
30225     
30226     var onDown = function(e){
30227         clearTimeout(showProc);
30228         clearTimeout(hideProc);
30229         if(!e.within(el)){
30230             if(tm.hideOnClick){
30231                 hide();
30232                 tm.disable();
30233                 tm.enable.defer(100, tm);
30234             }
30235         }
30236     };
30237     
30238     var getPad = function(){
30239         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30240     };
30241
30242     var show = function(o){
30243         if(disabled){
30244             return;
30245         }
30246         clearTimeout(dismissProc);
30247         ce = o;
30248         if(removeCls){ // in case manually hidden
30249             el.removeClass(removeCls);
30250             removeCls = null;
30251         }
30252         if(ce.cls){
30253             el.addClass(ce.cls);
30254             removeCls = ce.cls;
30255         }
30256         if(ce.title){
30257             tipTitle.update(ce.title);
30258             tipTitle.show();
30259         }else{
30260             tipTitle.update('');
30261             tipTitle.hide();
30262         }
30263         el.dom.style.width  = tm.maxWidth+'px';
30264         //tipBody.dom.style.width = '';
30265         tipBodyText.update(o.text);
30266         var p = getPad(), w = ce.width;
30267         if(!w){
30268             var td = tipBodyText.dom;
30269             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30270             if(aw > tm.maxWidth){
30271                 w = tm.maxWidth;
30272             }else if(aw < tm.minWidth){
30273                 w = tm.minWidth;
30274             }else{
30275                 w = aw;
30276             }
30277         }
30278         //tipBody.setWidth(w);
30279         el.setWidth(parseInt(w, 10) + p);
30280         if(ce.autoHide === false){
30281             close.setDisplayed(true);
30282             if(dd){
30283                 dd.unlock();
30284             }
30285         }else{
30286             close.setDisplayed(false);
30287             if(dd){
30288                 dd.lock();
30289             }
30290         }
30291         if(xy){
30292             el.avoidY = xy[1]-18;
30293             el.setXY(xy);
30294         }
30295         if(tm.animate){
30296             el.setOpacity(.1);
30297             el.setStyle("visibility", "visible");
30298             el.fadeIn({callback: afterShow});
30299         }else{
30300             afterShow();
30301         }
30302     };
30303     
30304     var afterShow = function(){
30305         if(ce){
30306             el.show();
30307             esc.enable();
30308             if(tm.autoDismiss && ce.autoHide !== false){
30309                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30310             }
30311         }
30312     };
30313     
30314     var hide = function(noanim){
30315         clearTimeout(dismissProc);
30316         clearTimeout(hideProc);
30317         ce = null;
30318         if(el.isVisible()){
30319             esc.disable();
30320             if(noanim !== true && tm.animate){
30321                 el.fadeOut({callback: afterHide});
30322             }else{
30323                 afterHide();
30324             } 
30325         }
30326     };
30327     
30328     var afterHide = function(){
30329         el.hide();
30330         if(removeCls){
30331             el.removeClass(removeCls);
30332             removeCls = null;
30333         }
30334     };
30335     
30336     return {
30337         /**
30338         * @cfg {Number} minWidth
30339         * The minimum width of the quick tip (defaults to 40)
30340         */
30341        minWidth : 40,
30342         /**
30343         * @cfg {Number} maxWidth
30344         * The maximum width of the quick tip (defaults to 300)
30345         */
30346        maxWidth : 300,
30347         /**
30348         * @cfg {Boolean} interceptTitles
30349         * True to automatically use the element's DOM title value if available (defaults to false)
30350         */
30351        interceptTitles : false,
30352         /**
30353         * @cfg {Boolean} trackMouse
30354         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30355         */
30356        trackMouse : false,
30357         /**
30358         * @cfg {Boolean} hideOnClick
30359         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30360         */
30361        hideOnClick : true,
30362         /**
30363         * @cfg {Number} showDelay
30364         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30365         */
30366        showDelay : 500,
30367         /**
30368         * @cfg {Number} hideDelay
30369         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30370         */
30371        hideDelay : 200,
30372         /**
30373         * @cfg {Boolean} autoHide
30374         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30375         * Used in conjunction with hideDelay.
30376         */
30377        autoHide : true,
30378         /**
30379         * @cfg {Boolean}
30380         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30381         * (defaults to true).  Used in conjunction with autoDismissDelay.
30382         */
30383        autoDismiss : true,
30384         /**
30385         * @cfg {Number}
30386         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30387         */
30388        autoDismissDelay : 5000,
30389        /**
30390         * @cfg {Boolean} animate
30391         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30392         */
30393        animate : false,
30394
30395        /**
30396         * @cfg {String} title
30397         * Title text to display (defaults to '').  This can be any valid HTML markup.
30398         */
30399         title: '',
30400        /**
30401         * @cfg {String} text
30402         * Body text to display (defaults to '').  This can be any valid HTML markup.
30403         */
30404         text : '',
30405        /**
30406         * @cfg {String} cls
30407         * A CSS class to apply to the base quick tip element (defaults to '').
30408         */
30409         cls : '',
30410        /**
30411         * @cfg {Number} width
30412         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30413         * minWidth or maxWidth.
30414         */
30415         width : null,
30416
30417     /**
30418      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30419      * or display QuickTips in a page.
30420      */
30421        init : function(){
30422           tm = Roo.QuickTips;
30423           cfg = tm.tagConfig;
30424           if(!inited){
30425               if(!Roo.isReady){ // allow calling of init() before onReady
30426                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30427                   return;
30428               }
30429               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30430               el.fxDefaults = {stopFx: true};
30431               // maximum custom styling
30432               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
30433               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
30434               tipTitle = el.child('h3');
30435               tipTitle.enableDisplayMode("block");
30436               tipBody = el.child('div.x-tip-bd');
30437               tipBodyText = el.child('div.x-tip-bd-inner');
30438               //bdLeft = el.child('div.x-tip-bd-left');
30439               //bdRight = el.child('div.x-tip-bd-right');
30440               close = el.child('div.x-tip-close');
30441               close.enableDisplayMode("block");
30442               close.on("click", hide);
30443               var d = Roo.get(document);
30444               d.on("mousedown", onDown);
30445               d.on("mouseover", onOver);
30446               d.on("mouseout", onOut);
30447               d.on("mousemove", onMove);
30448               esc = d.addKeyListener(27, hide);
30449               esc.disable();
30450               if(Roo.dd.DD){
30451                   dd = el.initDD("default", null, {
30452                       onDrag : function(){
30453                           el.sync();  
30454                       }
30455                   });
30456                   dd.setHandleElId(tipTitle.id);
30457                   dd.lock();
30458               }
30459               inited = true;
30460           }
30461           this.enable(); 
30462        },
30463
30464     /**
30465      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30466      * are supported:
30467      * <pre>
30468 Property    Type                   Description
30469 ----------  ---------------------  ------------------------------------------------------------------------
30470 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30471      * </ul>
30472      * @param {Object} config The config object
30473      */
30474        register : function(config){
30475            var cs = config instanceof Array ? config : arguments;
30476            for(var i = 0, len = cs.length; i < len; i++) {
30477                var c = cs[i];
30478                var target = c.target;
30479                if(target){
30480                    if(target instanceof Array){
30481                        for(var j = 0, jlen = target.length; j < jlen; j++){
30482                            tagEls[target[j]] = c;
30483                        }
30484                    }else{
30485                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30486                    }
30487                }
30488            }
30489        },
30490
30491     /**
30492      * Removes this quick tip from its element and destroys it.
30493      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30494      */
30495        unregister : function(el){
30496            delete tagEls[Roo.id(el)];
30497        },
30498
30499     /**
30500      * Enable this quick tip.
30501      */
30502        enable : function(){
30503            if(inited && disabled){
30504                locks.pop();
30505                if(locks.length < 1){
30506                    disabled = false;
30507                }
30508            }
30509        },
30510
30511     /**
30512      * Disable this quick tip.
30513      */
30514        disable : function(){
30515           disabled = true;
30516           clearTimeout(showProc);
30517           clearTimeout(hideProc);
30518           clearTimeout(dismissProc);
30519           if(ce){
30520               hide(true);
30521           }
30522           locks.push(1);
30523        },
30524
30525     /**
30526      * Returns true if the quick tip is enabled, else false.
30527      */
30528        isEnabled : function(){
30529             return !disabled;
30530        },
30531
30532         // private
30533        tagConfig : {
30534            namespace : "ext",
30535            attribute : "qtip",
30536            width : "width",
30537            target : "target",
30538            title : "qtitle",
30539            hide : "hide",
30540            cls : "qclass"
30541        }
30542    };
30543 }();
30544
30545 // backwards compat
30546 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30547  * Based on:
30548  * Ext JS Library 1.1.1
30549  * Copyright(c) 2006-2007, Ext JS, LLC.
30550  *
30551  * Originally Released Under LGPL - original licence link has changed is not relivant.
30552  *
30553  * Fork - LGPL
30554  * <script type="text/javascript">
30555  */
30556  
30557
30558 /**
30559  * @class Roo.tree.TreePanel
30560  * @extends Roo.data.Tree
30561
30562  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30563  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30564  * @cfg {Boolean} enableDD true to enable drag and drop
30565  * @cfg {Boolean} enableDrag true to enable just drag
30566  * @cfg {Boolean} enableDrop true to enable just drop
30567  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30568  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30569  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30570  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30571  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30572  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30573  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30574  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30575  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30576  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30577  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30578  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30579  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30580  * @cfg {Function} renderer Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30581  * @cfg {Function} rendererTip Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
30582  * 
30583  * @constructor
30584  * @param {String/HTMLElement/Element} el The container element
30585  * @param {Object} config
30586  */
30587 Roo.tree.TreePanel = function(el, config){
30588     var root = false;
30589     var loader = false;
30590     if (config.root) {
30591         root = config.root;
30592         delete config.root;
30593     }
30594     if (config.loader) {
30595         loader = config.loader;
30596         delete config.loader;
30597     }
30598     
30599     Roo.apply(this, config);
30600     Roo.tree.TreePanel.superclass.constructor.call(this);
30601     this.el = Roo.get(el);
30602     this.el.addClass('x-tree');
30603     //console.log(root);
30604     if (root) {
30605         this.setRootNode( Roo.factory(root, Roo.tree));
30606     }
30607     if (loader) {
30608         this.loader = Roo.factory(loader, Roo.tree);
30609     }
30610    /**
30611     * Read-only. The id of the container element becomes this TreePanel's id.
30612     */
30613    this.id = this.el.id;
30614    this.addEvents({
30615         /**
30616         * @event beforeload
30617         * Fires before a node is loaded, return false to cancel
30618         * @param {Node} node The node being loaded
30619         */
30620         "beforeload" : true,
30621         /**
30622         * @event load
30623         * Fires when a node is loaded
30624         * @param {Node} node The node that was loaded
30625         */
30626         "load" : true,
30627         /**
30628         * @event textchange
30629         * Fires when the text for a node is changed
30630         * @param {Node} node The node
30631         * @param {String} text The new text
30632         * @param {String} oldText The old text
30633         */
30634         "textchange" : true,
30635         /**
30636         * @event beforeexpand
30637         * Fires before a node is expanded, return false to cancel.
30638         * @param {Node} node The node
30639         * @param {Boolean} deep
30640         * @param {Boolean} anim
30641         */
30642         "beforeexpand" : true,
30643         /**
30644         * @event beforecollapse
30645         * Fires before a node is collapsed, return false to cancel.
30646         * @param {Node} node The node
30647         * @param {Boolean} deep
30648         * @param {Boolean} anim
30649         */
30650         "beforecollapse" : true,
30651         /**
30652         * @event expand
30653         * Fires when a node is expanded
30654         * @param {Node} node The node
30655         */
30656         "expand" : true,
30657         /**
30658         * @event disabledchange
30659         * Fires when the disabled status of a node changes
30660         * @param {Node} node The node
30661         * @param {Boolean} disabled
30662         */
30663         "disabledchange" : true,
30664         /**
30665         * @event collapse
30666         * Fires when a node is collapsed
30667         * @param {Node} node The node
30668         */
30669         "collapse" : true,
30670         /**
30671         * @event beforeclick
30672         * Fires before click processing on a node. Return false to cancel the default action.
30673         * @param {Node} node The node
30674         * @param {Roo.EventObject} e The event object
30675         */
30676         "beforeclick":true,
30677         /**
30678         * @event checkchange
30679         * Fires when a node with a checkbox's checked property changes
30680         * @param {Node} this This node
30681         * @param {Boolean} checked
30682         */
30683         "checkchange":true,
30684         /**
30685         * @event click
30686         * Fires when a node is clicked
30687         * @param {Node} node The node
30688         * @param {Roo.EventObject} e The event object
30689         */
30690         "click":true,
30691         /**
30692         * @event dblclick
30693         * Fires when a node is double clicked
30694         * @param {Node} node The node
30695         * @param {Roo.EventObject} e The event object
30696         */
30697         "dblclick":true,
30698         /**
30699         * @event contextmenu
30700         * Fires when a node is right clicked
30701         * @param {Node} node The node
30702         * @param {Roo.EventObject} e The event object
30703         */
30704         "contextmenu":true,
30705         /**
30706         * @event beforechildrenrendered
30707         * Fires right before the child nodes for a node are rendered
30708         * @param {Node} node The node
30709         */
30710         "beforechildrenrendered":true,
30711        /**
30712              * @event startdrag
30713              * Fires when a node starts being dragged
30714              * @param {Roo.tree.TreePanel} this
30715              * @param {Roo.tree.TreeNode} node
30716              * @param {event} e The raw browser event
30717              */ 
30718             "startdrag" : true,
30719             /**
30720              * @event enddrag
30721              * Fires when a drag operation is complete
30722              * @param {Roo.tree.TreePanel} this
30723              * @param {Roo.tree.TreeNode} node
30724              * @param {event} e The raw browser event
30725              */
30726             "enddrag" : true,
30727             /**
30728              * @event dragdrop
30729              * Fires when a dragged node is dropped on a valid DD target
30730              * @param {Roo.tree.TreePanel} this
30731              * @param {Roo.tree.TreeNode} node
30732              * @param {DD} dd The dd it was dropped on
30733              * @param {event} e The raw browser event
30734              */
30735             "dragdrop" : true,
30736             /**
30737              * @event beforenodedrop
30738              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30739              * passed to handlers has the following properties:<br />
30740              * <ul style="padding:5px;padding-left:16px;">
30741              * <li>tree - The TreePanel</li>
30742              * <li>target - The node being targeted for the drop</li>
30743              * <li>data - The drag data from the drag source</li>
30744              * <li>point - The point of the drop - append, above or below</li>
30745              * <li>source - The drag source</li>
30746              * <li>rawEvent - Raw mouse event</li>
30747              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30748              * to be inserted by setting them on this object.</li>
30749              * <li>cancel - Set this to true to cancel the drop.</li>
30750              * </ul>
30751              * @param {Object} dropEvent
30752              */
30753             "beforenodedrop" : true,
30754             /**
30755              * @event nodedrop
30756              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30757              * passed to handlers has the following properties:<br />
30758              * <ul style="padding:5px;padding-left:16px;">
30759              * <li>tree - The TreePanel</li>
30760              * <li>target - The node being targeted for the drop</li>
30761              * <li>data - The drag data from the drag source</li>
30762              * <li>point - The point of the drop - append, above or below</li>
30763              * <li>source - The drag source</li>
30764              * <li>rawEvent - Raw mouse event</li>
30765              * <li>dropNode - Dropped node(s).</li>
30766              * </ul>
30767              * @param {Object} dropEvent
30768              */
30769             "nodedrop" : true,
30770              /**
30771              * @event nodedragover
30772              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30773              * passed to handlers has the following properties:<br />
30774              * <ul style="padding:5px;padding-left:16px;">
30775              * <li>tree - The TreePanel</li>
30776              * <li>target - The node being targeted for the drop</li>
30777              * <li>data - The drag data from the drag source</li>
30778              * <li>point - The point of the drop - append, above or below</li>
30779              * <li>source - The drag source</li>
30780              * <li>rawEvent - Raw mouse event</li>
30781              * <li>dropNode - Drop node(s) provided by the source.</li>
30782              * <li>cancel - Set this to true to signal drop not allowed.</li>
30783              * </ul>
30784              * @param {Object} dragOverEvent
30785              */
30786             "nodedragover" : true
30787         
30788    });
30789    if(this.singleExpand){
30790        this.on("beforeexpand", this.restrictExpand, this);
30791    }
30792 };
30793 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30794     rootVisible : true,
30795     animate: Roo.enableFx,
30796     lines : true,
30797     enableDD : false,
30798     hlDrop : Roo.enableFx,
30799   
30800     renderer: false,
30801     
30802     rendererTip: false,
30803     // private
30804     restrictExpand : function(node){
30805         var p = node.parentNode;
30806         if(p){
30807             if(p.expandedChild && p.expandedChild.parentNode == p){
30808                 p.expandedChild.collapse();
30809             }
30810             p.expandedChild = node;
30811         }
30812     },
30813
30814     // private override
30815     setRootNode : function(node){
30816         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30817         if(!this.rootVisible){
30818             node.ui = new Roo.tree.RootTreeNodeUI(node);
30819         }
30820         return node;
30821     },
30822
30823     /**
30824      * Returns the container element for this TreePanel
30825      */
30826     getEl : function(){
30827         return this.el;
30828     },
30829
30830     /**
30831      * Returns the default TreeLoader for this TreePanel
30832      */
30833     getLoader : function(){
30834         return this.loader;
30835     },
30836
30837     /**
30838      * Expand all nodes
30839      */
30840     expandAll : function(){
30841         this.root.expand(true);
30842     },
30843
30844     /**
30845      * Collapse all nodes
30846      */
30847     collapseAll : function(){
30848         this.root.collapse(true);
30849     },
30850
30851     /**
30852      * Returns the selection model used by this TreePanel
30853      */
30854     getSelectionModel : function(){
30855         if(!this.selModel){
30856             this.selModel = new Roo.tree.DefaultSelectionModel();
30857         }
30858         return this.selModel;
30859     },
30860
30861     /**
30862      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30863      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30864      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30865      * @return {Array}
30866      */
30867     getChecked : function(a, startNode){
30868         startNode = startNode || this.root;
30869         var r = [];
30870         var f = function(){
30871             if(this.attributes.checked){
30872                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30873             }
30874         }
30875         startNode.cascade(f);
30876         return r;
30877     },
30878
30879     /**
30880      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30881      * @param {String} path
30882      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30883      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30884      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30885      */
30886     expandPath : function(path, attr, callback){
30887         attr = attr || "id";
30888         var keys = path.split(this.pathSeparator);
30889         var curNode = this.root;
30890         if(curNode.attributes[attr] != keys[1]){ // invalid root
30891             if(callback){
30892                 callback(false, null);
30893             }
30894             return;
30895         }
30896         var index = 1;
30897         var f = function(){
30898             if(++index == keys.length){
30899                 if(callback){
30900                     callback(true, curNode);
30901                 }
30902                 return;
30903             }
30904             var c = curNode.findChild(attr, keys[index]);
30905             if(!c){
30906                 if(callback){
30907                     callback(false, curNode);
30908                 }
30909                 return;
30910             }
30911             curNode = c;
30912             c.expand(false, false, f);
30913         };
30914         curNode.expand(false, false, f);
30915     },
30916
30917     /**
30918      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30919      * @param {String} path
30920      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30921      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30922      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30923      */
30924     selectPath : function(path, attr, callback){
30925         attr = attr || "id";
30926         var keys = path.split(this.pathSeparator);
30927         var v = keys.pop();
30928         if(keys.length > 0){
30929             var f = function(success, node){
30930                 if(success && node){
30931                     var n = node.findChild(attr, v);
30932                     if(n){
30933                         n.select();
30934                         if(callback){
30935                             callback(true, n);
30936                         }
30937                     }else if(callback){
30938                         callback(false, n);
30939                     }
30940                 }else{
30941                     if(callback){
30942                         callback(false, n);
30943                     }
30944                 }
30945             };
30946             this.expandPath(keys.join(this.pathSeparator), attr, f);
30947         }else{
30948             this.root.select();
30949             if(callback){
30950                 callback(true, this.root);
30951             }
30952         }
30953     },
30954
30955     getTreeEl : function(){
30956         return this.el;
30957     },
30958
30959     /**
30960      * Trigger rendering of this TreePanel
30961      */
30962     render : function(){
30963         if (this.innerCt) {
30964             return this; // stop it rendering more than once!!
30965         }
30966         
30967         this.innerCt = this.el.createChild({tag:"ul",
30968                cls:"x-tree-root-ct " +
30969                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30970
30971         if(this.containerScroll){
30972             Roo.dd.ScrollManager.register(this.el);
30973         }
30974         if((this.enableDD || this.enableDrop) && !this.dropZone){
30975            /**
30976             * The dropZone used by this tree if drop is enabled
30977             * @type Roo.tree.TreeDropZone
30978             */
30979              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30980                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30981            });
30982         }
30983         if((this.enableDD || this.enableDrag) && !this.dragZone){
30984            /**
30985             * The dragZone used by this tree if drag is enabled
30986             * @type Roo.tree.TreeDragZone
30987             */
30988             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30989                ddGroup: this.ddGroup || "TreeDD",
30990                scroll: this.ddScroll
30991            });
30992         }
30993         this.getSelectionModel().init(this);
30994         if (!this.root) {
30995             console.log("ROOT not set in tree");
30996             return;
30997         }
30998         this.root.render();
30999         if(!this.rootVisible){
31000             this.root.renderChildren();
31001         }
31002         return this;
31003     }
31004 });/*
31005  * Based on:
31006  * Ext JS Library 1.1.1
31007  * Copyright(c) 2006-2007, Ext JS, LLC.
31008  *
31009  * Originally Released Under LGPL - original licence link has changed is not relivant.
31010  *
31011  * Fork - LGPL
31012  * <script type="text/javascript">
31013  */
31014  
31015
31016 /**
31017  * @class Roo.tree.DefaultSelectionModel
31018  * @extends Roo.util.Observable
31019  * The default single selection for a TreePanel.
31020  */
31021 Roo.tree.DefaultSelectionModel = function(){
31022    this.selNode = null;
31023    
31024    this.addEvents({
31025        /**
31026         * @event selectionchange
31027         * Fires when the selected node changes
31028         * @param {DefaultSelectionModel} this
31029         * @param {TreeNode} node the new selection
31030         */
31031        "selectionchange" : true,
31032
31033        /**
31034         * @event beforeselect
31035         * Fires before the selected node changes, return false to cancel the change
31036         * @param {DefaultSelectionModel} this
31037         * @param {TreeNode} node the new selection
31038         * @param {TreeNode} node the old selection
31039         */
31040        "beforeselect" : true
31041    });
31042 };
31043
31044 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31045     init : function(tree){
31046         this.tree = tree;
31047         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31048         tree.on("click", this.onNodeClick, this);
31049     },
31050     
31051     onNodeClick : function(node, e){
31052         if (e.ctrlKey && this.selNode == node)  {
31053             this.unselect(node);
31054             return;
31055         }
31056         this.select(node);
31057     },
31058     
31059     /**
31060      * Select a node.
31061      * @param {TreeNode} node The node to select
31062      * @return {TreeNode} The selected node
31063      */
31064     select : function(node){
31065         var last = this.selNode;
31066         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31067             if(last){
31068                 last.ui.onSelectedChange(false);
31069             }
31070             this.selNode = node;
31071             node.ui.onSelectedChange(true);
31072             this.fireEvent("selectionchange", this, node, last);
31073         }
31074         return node;
31075     },
31076     
31077     /**
31078      * Deselect a node.
31079      * @param {TreeNode} node The node to unselect
31080      */
31081     unselect : function(node){
31082         if(this.selNode == node){
31083             this.clearSelections();
31084         }    
31085     },
31086     
31087     /**
31088      * Clear all selections
31089      */
31090     clearSelections : function(){
31091         var n = this.selNode;
31092         if(n){
31093             n.ui.onSelectedChange(false);
31094             this.selNode = null;
31095             this.fireEvent("selectionchange", this, null);
31096         }
31097         return n;
31098     },
31099     
31100     /**
31101      * Get the selected node
31102      * @return {TreeNode} The selected node
31103      */
31104     getSelectedNode : function(){
31105         return this.selNode;    
31106     },
31107     
31108     /**
31109      * Returns true if the node is selected
31110      * @param {TreeNode} node The node to check
31111      * @return {Boolean}
31112      */
31113     isSelected : function(node){
31114         return this.selNode == node;  
31115     },
31116
31117     /**
31118      * Selects the node above the selected node in the tree, intelligently walking the nodes
31119      * @return TreeNode The new selection
31120      */
31121     selectPrevious : function(){
31122         var s = this.selNode || this.lastSelNode;
31123         if(!s){
31124             return null;
31125         }
31126         var ps = s.previousSibling;
31127         if(ps){
31128             if(!ps.isExpanded() || ps.childNodes.length < 1){
31129                 return this.select(ps);
31130             } else{
31131                 var lc = ps.lastChild;
31132                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31133                     lc = lc.lastChild;
31134                 }
31135                 return this.select(lc);
31136             }
31137         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31138             return this.select(s.parentNode);
31139         }
31140         return null;
31141     },
31142
31143     /**
31144      * Selects the node above the selected node in the tree, intelligently walking the nodes
31145      * @return TreeNode The new selection
31146      */
31147     selectNext : function(){
31148         var s = this.selNode || this.lastSelNode;
31149         if(!s){
31150             return null;
31151         }
31152         if(s.firstChild && s.isExpanded()){
31153              return this.select(s.firstChild);
31154          }else if(s.nextSibling){
31155              return this.select(s.nextSibling);
31156          }else if(s.parentNode){
31157             var newS = null;
31158             s.parentNode.bubble(function(){
31159                 if(this.nextSibling){
31160                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31161                     return false;
31162                 }
31163             });
31164             return newS;
31165          }
31166         return null;
31167     },
31168
31169     onKeyDown : function(e){
31170         var s = this.selNode || this.lastSelNode;
31171         // undesirable, but required
31172         var sm = this;
31173         if(!s){
31174             return;
31175         }
31176         var k = e.getKey();
31177         switch(k){
31178              case e.DOWN:
31179                  e.stopEvent();
31180                  this.selectNext();
31181              break;
31182              case e.UP:
31183                  e.stopEvent();
31184                  this.selectPrevious();
31185              break;
31186              case e.RIGHT:
31187                  e.preventDefault();
31188                  if(s.hasChildNodes()){
31189                      if(!s.isExpanded()){
31190                          s.expand();
31191                      }else if(s.firstChild){
31192                          this.select(s.firstChild, e);
31193                      }
31194                  }
31195              break;
31196              case e.LEFT:
31197                  e.preventDefault();
31198                  if(s.hasChildNodes() && s.isExpanded()){
31199                      s.collapse();
31200                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31201                      this.select(s.parentNode, e);
31202                  }
31203              break;
31204         };
31205     }
31206 });
31207
31208 /**
31209  * @class Roo.tree.MultiSelectionModel
31210  * @extends Roo.util.Observable
31211  * Multi selection for a TreePanel.
31212  */
31213 Roo.tree.MultiSelectionModel = function(){
31214    this.selNodes = [];
31215    this.selMap = {};
31216    this.addEvents({
31217        /**
31218         * @event selectionchange
31219         * Fires when the selected nodes change
31220         * @param {MultiSelectionModel} this
31221         * @param {Array} nodes Array of the selected nodes
31222         */
31223        "selectionchange" : true
31224    });
31225 };
31226
31227 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31228     init : function(tree){
31229         this.tree = tree;
31230         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31231         tree.on("click", this.onNodeClick, this);
31232     },
31233     
31234     onNodeClick : function(node, e){
31235         this.select(node, e, e.ctrlKey);
31236     },
31237     
31238     /**
31239      * Select a node.
31240      * @param {TreeNode} node The node to select
31241      * @param {EventObject} e (optional) An event associated with the selection
31242      * @param {Boolean} keepExisting True to retain existing selections
31243      * @return {TreeNode} The selected node
31244      */
31245     select : function(node, e, keepExisting){
31246         if(keepExisting !== true){
31247             this.clearSelections(true);
31248         }
31249         if(this.isSelected(node)){
31250             this.lastSelNode = node;
31251             return node;
31252         }
31253         this.selNodes.push(node);
31254         this.selMap[node.id] = node;
31255         this.lastSelNode = node;
31256         node.ui.onSelectedChange(true);
31257         this.fireEvent("selectionchange", this, this.selNodes);
31258         return node;
31259     },
31260     
31261     /**
31262      * Deselect a node.
31263      * @param {TreeNode} node The node to unselect
31264      */
31265     unselect : function(node){
31266         if(this.selMap[node.id]){
31267             node.ui.onSelectedChange(false);
31268             var sn = this.selNodes;
31269             var index = -1;
31270             if(sn.indexOf){
31271                 index = sn.indexOf(node);
31272             }else{
31273                 for(var i = 0, len = sn.length; i < len; i++){
31274                     if(sn[i] == node){
31275                         index = i;
31276                         break;
31277                     }
31278                 }
31279             }
31280             if(index != -1){
31281                 this.selNodes.splice(index, 1);
31282             }
31283             delete this.selMap[node.id];
31284             this.fireEvent("selectionchange", this, this.selNodes);
31285         }
31286     },
31287     
31288     /**
31289      * Clear all selections
31290      */
31291     clearSelections : function(suppressEvent){
31292         var sn = this.selNodes;
31293         if(sn.length > 0){
31294             for(var i = 0, len = sn.length; i < len; i++){
31295                 sn[i].ui.onSelectedChange(false);
31296             }
31297             this.selNodes = [];
31298             this.selMap = {};
31299             if(suppressEvent !== true){
31300                 this.fireEvent("selectionchange", this, this.selNodes);
31301             }
31302         }
31303     },
31304     
31305     /**
31306      * Returns true if the node is selected
31307      * @param {TreeNode} node The node to check
31308      * @return {Boolean}
31309      */
31310     isSelected : function(node){
31311         return this.selMap[node.id] ? true : false;  
31312     },
31313     
31314     /**
31315      * Returns an array of the selected nodes
31316      * @return {Array}
31317      */
31318     getSelectedNodes : function(){
31319         return this.selNodes;    
31320     },
31321
31322     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31323
31324     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31325
31326     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31327 });/*
31328  * Based on:
31329  * Ext JS Library 1.1.1
31330  * Copyright(c) 2006-2007, Ext JS, LLC.
31331  *
31332  * Originally Released Under LGPL - original licence link has changed is not relivant.
31333  *
31334  * Fork - LGPL
31335  * <script type="text/javascript">
31336  */
31337  
31338 /**
31339  * @class Roo.tree.TreeNode
31340  * @extends Roo.data.Node
31341  * @cfg {String} text The text for this node
31342  * @cfg {Boolean} expanded true to start the node expanded
31343  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31344  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31345  * @cfg {Boolean} disabled true to start the node disabled
31346  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31347  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31348  * @cfg {String} cls A css class to be added to the node
31349  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31350  * @cfg {String} href URL of the link used for the node (defaults to #)
31351  * @cfg {String} hrefTarget target frame for the link
31352  * @cfg {String} qtip An Ext QuickTip for the node
31353  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31354  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31355  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31356  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31357  * (defaults to undefined with no checkbox rendered)
31358  * @constructor
31359  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31360  */
31361 Roo.tree.TreeNode = function(attributes){
31362     attributes = attributes || {};
31363     if(typeof attributes == "string"){
31364         attributes = {text: attributes};
31365     }
31366     this.childrenRendered = false;
31367     this.rendered = false;
31368     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31369     this.expanded = attributes.expanded === true;
31370     this.isTarget = attributes.isTarget !== false;
31371     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31372     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31373
31374     /**
31375      * Read-only. The text for this node. To change it use setText().
31376      * @type String
31377      */
31378     this.text = attributes.text;
31379     /**
31380      * True if this node is disabled.
31381      * @type Boolean
31382      */
31383     this.disabled = attributes.disabled === true;
31384
31385     this.addEvents({
31386         /**
31387         * @event textchange
31388         * Fires when the text for this node is changed
31389         * @param {Node} this This node
31390         * @param {String} text The new text
31391         * @param {String} oldText The old text
31392         */
31393         "textchange" : true,
31394         /**
31395         * @event beforeexpand
31396         * Fires before this node is expanded, return false to cancel.
31397         * @param {Node} this This node
31398         * @param {Boolean} deep
31399         * @param {Boolean} anim
31400         */
31401         "beforeexpand" : true,
31402         /**
31403         * @event beforecollapse
31404         * Fires before this node is collapsed, return false to cancel.
31405         * @param {Node} this This node
31406         * @param {Boolean} deep
31407         * @param {Boolean} anim
31408         */
31409         "beforecollapse" : true,
31410         /**
31411         * @event expand
31412         * Fires when this node is expanded
31413         * @param {Node} this This node
31414         */
31415         "expand" : true,
31416         /**
31417         * @event disabledchange
31418         * Fires when the disabled status of this node changes
31419         * @param {Node} this This node
31420         * @param {Boolean} disabled
31421         */
31422         "disabledchange" : true,
31423         /**
31424         * @event collapse
31425         * Fires when this node is collapsed
31426         * @param {Node} this This node
31427         */
31428         "collapse" : true,
31429         /**
31430         * @event beforeclick
31431         * Fires before click processing. Return false to cancel the default action.
31432         * @param {Node} this This node
31433         * @param {Roo.EventObject} e The event object
31434         */
31435         "beforeclick":true,
31436         /**
31437         * @event checkchange
31438         * Fires when a node with a checkbox's checked property changes
31439         * @param {Node} this This node
31440         * @param {Boolean} checked
31441         */
31442         "checkchange":true,
31443         /**
31444         * @event click
31445         * Fires when this node is clicked
31446         * @param {Node} this This node
31447         * @param {Roo.EventObject} e The event object
31448         */
31449         "click":true,
31450         /**
31451         * @event dblclick
31452         * Fires when this node is double clicked
31453         * @param {Node} this This node
31454         * @param {Roo.EventObject} e The event object
31455         */
31456         "dblclick":true,
31457         /**
31458         * @event contextmenu
31459         * Fires when this node is right clicked
31460         * @param {Node} this This node
31461         * @param {Roo.EventObject} e The event object
31462         */
31463         "contextmenu":true,
31464         /**
31465         * @event beforechildrenrendered
31466         * Fires right before the child nodes for this node are rendered
31467         * @param {Node} this This node
31468         */
31469         "beforechildrenrendered":true
31470     });
31471
31472     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31473
31474     /**
31475      * Read-only. The UI for this node
31476      * @type TreeNodeUI
31477      */
31478     this.ui = new uiClass(this);
31479 };
31480 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31481     preventHScroll: true,
31482     /**
31483      * Returns true if this node is expanded
31484      * @return {Boolean}
31485      */
31486     isExpanded : function(){
31487         return this.expanded;
31488     },
31489
31490     /**
31491      * Returns the UI object for this node
31492      * @return {TreeNodeUI}
31493      */
31494     getUI : function(){
31495         return this.ui;
31496     },
31497
31498     // private override
31499     setFirstChild : function(node){
31500         var of = this.firstChild;
31501         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31502         if(this.childrenRendered && of && node != of){
31503             of.renderIndent(true, true);
31504         }
31505         if(this.rendered){
31506             this.renderIndent(true, true);
31507         }
31508     },
31509
31510     // private override
31511     setLastChild : function(node){
31512         var ol = this.lastChild;
31513         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31514         if(this.childrenRendered && ol && node != ol){
31515             ol.renderIndent(true, true);
31516         }
31517         if(this.rendered){
31518             this.renderIndent(true, true);
31519         }
31520     },
31521
31522     // these methods are overridden to provide lazy rendering support
31523     // private override
31524     appendChild : function(){
31525         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31526         if(node && this.childrenRendered){
31527             node.render();
31528         }
31529         this.ui.updateExpandIcon();
31530         return node;
31531     },
31532
31533     // private override
31534     removeChild : function(node){
31535         this.ownerTree.getSelectionModel().unselect(node);
31536         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31537         // if it's been rendered remove dom node
31538         if(this.childrenRendered){
31539             node.ui.remove();
31540         }
31541         if(this.childNodes.length < 1){
31542             this.collapse(false, false);
31543         }else{
31544             this.ui.updateExpandIcon();
31545         }
31546         if(!this.firstChild) {
31547             this.childrenRendered = false;
31548         }
31549         return node;
31550     },
31551
31552     // private override
31553     insertBefore : function(node, refNode){
31554         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31555         if(newNode && refNode && this.childrenRendered){
31556             node.render();
31557         }
31558         this.ui.updateExpandIcon();
31559         return newNode;
31560     },
31561
31562     /**
31563      * Sets the text for this node
31564      * @param {String} text
31565      */
31566     setText : function(text){
31567         var oldText = this.text;
31568         this.text = text;
31569         this.attributes.text = text;
31570         if(this.rendered){ // event without subscribing
31571             this.ui.onTextChange(this, text, oldText);
31572         }
31573         this.fireEvent("textchange", this, text, oldText);
31574     },
31575
31576     /**
31577      * Triggers selection of this node
31578      */
31579     select : function(){
31580         this.getOwnerTree().getSelectionModel().select(this);
31581     },
31582
31583     /**
31584      * Triggers deselection of this node
31585      */
31586     unselect : function(){
31587         this.getOwnerTree().getSelectionModel().unselect(this);
31588     },
31589
31590     /**
31591      * Returns true if this node is selected
31592      * @return {Boolean}
31593      */
31594     isSelected : function(){
31595         return this.getOwnerTree().getSelectionModel().isSelected(this);
31596     },
31597
31598     /**
31599      * Expand this node.
31600      * @param {Boolean} deep (optional) True to expand all children as well
31601      * @param {Boolean} anim (optional) false to cancel the default animation
31602      * @param {Function} callback (optional) A callback to be called when
31603      * expanding this node completes (does not wait for deep expand to complete).
31604      * Called with 1 parameter, this node.
31605      */
31606     expand : function(deep, anim, callback){
31607         if(!this.expanded){
31608             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31609                 return;
31610             }
31611             if(!this.childrenRendered){
31612                 this.renderChildren();
31613             }
31614             this.expanded = true;
31615             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31616                 this.ui.animExpand(function(){
31617                     this.fireEvent("expand", this);
31618                     if(typeof callback == "function"){
31619                         callback(this);
31620                     }
31621                     if(deep === true){
31622                         this.expandChildNodes(true);
31623                     }
31624                 }.createDelegate(this));
31625                 return;
31626             }else{
31627                 this.ui.expand();
31628                 this.fireEvent("expand", this);
31629                 if(typeof callback == "function"){
31630                     callback(this);
31631                 }
31632             }
31633         }else{
31634            if(typeof callback == "function"){
31635                callback(this);
31636            }
31637         }
31638         if(deep === true){
31639             this.expandChildNodes(true);
31640         }
31641     },
31642
31643     isHiddenRoot : function(){
31644         return this.isRoot && !this.getOwnerTree().rootVisible;
31645     },
31646
31647     /**
31648      * Collapse this node.
31649      * @param {Boolean} deep (optional) True to collapse all children as well
31650      * @param {Boolean} anim (optional) false to cancel the default animation
31651      */
31652     collapse : function(deep, anim){
31653         if(this.expanded && !this.isHiddenRoot()){
31654             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31655                 return;
31656             }
31657             this.expanded = false;
31658             if((this.getOwnerTree().animate && anim !== false) || anim){
31659                 this.ui.animCollapse(function(){
31660                     this.fireEvent("collapse", this);
31661                     if(deep === true){
31662                         this.collapseChildNodes(true);
31663                     }
31664                 }.createDelegate(this));
31665                 return;
31666             }else{
31667                 this.ui.collapse();
31668                 this.fireEvent("collapse", this);
31669             }
31670         }
31671         if(deep === true){
31672             var cs = this.childNodes;
31673             for(var i = 0, len = cs.length; i < len; i++) {
31674                 cs[i].collapse(true, false);
31675             }
31676         }
31677     },
31678
31679     // private
31680     delayedExpand : function(delay){
31681         if(!this.expandProcId){
31682             this.expandProcId = this.expand.defer(delay, this);
31683         }
31684     },
31685
31686     // private
31687     cancelExpand : function(){
31688         if(this.expandProcId){
31689             clearTimeout(this.expandProcId);
31690         }
31691         this.expandProcId = false;
31692     },
31693
31694     /**
31695      * Toggles expanded/collapsed state of the node
31696      */
31697     toggle : function(){
31698         if(this.expanded){
31699             this.collapse();
31700         }else{
31701             this.expand();
31702         }
31703     },
31704
31705     /**
31706      * Ensures all parent nodes are expanded
31707      */
31708     ensureVisible : function(callback){
31709         var tree = this.getOwnerTree();
31710         tree.expandPath(this.parentNode.getPath(), false, function(){
31711             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31712             Roo.callback(callback);
31713         }.createDelegate(this));
31714     },
31715
31716     /**
31717      * Expand all child nodes
31718      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31719      */
31720     expandChildNodes : function(deep){
31721         var cs = this.childNodes;
31722         for(var i = 0, len = cs.length; i < len; i++) {
31723                 cs[i].expand(deep);
31724         }
31725     },
31726
31727     /**
31728      * Collapse all child nodes
31729      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31730      */
31731     collapseChildNodes : function(deep){
31732         var cs = this.childNodes;
31733         for(var i = 0, len = cs.length; i < len; i++) {
31734                 cs[i].collapse(deep);
31735         }
31736     },
31737
31738     /**
31739      * Disables this node
31740      */
31741     disable : function(){
31742         this.disabled = true;
31743         this.unselect();
31744         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31745             this.ui.onDisableChange(this, true);
31746         }
31747         this.fireEvent("disabledchange", this, true);
31748     },
31749
31750     /**
31751      * Enables this node
31752      */
31753     enable : function(){
31754         this.disabled = false;
31755         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31756             this.ui.onDisableChange(this, false);
31757         }
31758         this.fireEvent("disabledchange", this, false);
31759     },
31760
31761     // private
31762     renderChildren : function(suppressEvent){
31763         if(suppressEvent !== false){
31764             this.fireEvent("beforechildrenrendered", this);
31765         }
31766         var cs = this.childNodes;
31767         for(var i = 0, len = cs.length; i < len; i++){
31768             cs[i].render(true);
31769         }
31770         this.childrenRendered = true;
31771     },
31772
31773     // private
31774     sort : function(fn, scope){
31775         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31776         if(this.childrenRendered){
31777             var cs = this.childNodes;
31778             for(var i = 0, len = cs.length; i < len; i++){
31779                 cs[i].render(true);
31780             }
31781         }
31782     },
31783
31784     // private
31785     render : function(bulkRender){
31786         this.ui.render(bulkRender);
31787         if(!this.rendered){
31788             this.rendered = true;
31789             if(this.expanded){
31790                 this.expanded = false;
31791                 this.expand(false, false);
31792             }
31793         }
31794     },
31795
31796     // private
31797     renderIndent : function(deep, refresh){
31798         if(refresh){
31799             this.ui.childIndent = null;
31800         }
31801         this.ui.renderIndent();
31802         if(deep === true && this.childrenRendered){
31803             var cs = this.childNodes;
31804             for(var i = 0, len = cs.length; i < len; i++){
31805                 cs[i].renderIndent(true, refresh);
31806             }
31807         }
31808     }
31809 });/*
31810  * Based on:
31811  * Ext JS Library 1.1.1
31812  * Copyright(c) 2006-2007, Ext JS, LLC.
31813  *
31814  * Originally Released Under LGPL - original licence link has changed is not relivant.
31815  *
31816  * Fork - LGPL
31817  * <script type="text/javascript">
31818  */
31819  
31820 /**
31821  * @class Roo.tree.AsyncTreeNode
31822  * @extends Roo.tree.TreeNode
31823  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31824  * @constructor
31825  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31826  */
31827  Roo.tree.AsyncTreeNode = function(config){
31828     this.loaded = false;
31829     this.loading = false;
31830     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31831     /**
31832     * @event beforeload
31833     * Fires before this node is loaded, return false to cancel
31834     * @param {Node} this This node
31835     */
31836     this.addEvents({'beforeload':true, 'load': true});
31837     /**
31838     * @event load
31839     * Fires when this node is loaded
31840     * @param {Node} this This node
31841     */
31842     /**
31843      * The loader used by this node (defaults to using the tree's defined loader)
31844      * @type TreeLoader
31845      * @property loader
31846      */
31847 };
31848 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31849     expand : function(deep, anim, callback){
31850         if(this.loading){ // if an async load is already running, waiting til it's done
31851             var timer;
31852             var f = function(){
31853                 if(!this.loading){ // done loading
31854                     clearInterval(timer);
31855                     this.expand(deep, anim, callback);
31856                 }
31857             }.createDelegate(this);
31858             timer = setInterval(f, 200);
31859             return;
31860         }
31861         if(!this.loaded){
31862             if(this.fireEvent("beforeload", this) === false){
31863                 return;
31864             }
31865             this.loading = true;
31866             this.ui.beforeLoad(this);
31867             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31868             if(loader){
31869                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31870                 return;
31871             }
31872         }
31873         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31874     },
31875     
31876     /**
31877      * Returns true if this node is currently loading
31878      * @return {Boolean}
31879      */
31880     isLoading : function(){
31881         return this.loading;  
31882     },
31883     
31884     loadComplete : function(deep, anim, callback){
31885         this.loading = false;
31886         this.loaded = true;
31887         this.ui.afterLoad(this);
31888         this.fireEvent("load", this);
31889         this.expand(deep, anim, callback);
31890     },
31891     
31892     /**
31893      * Returns true if this node has been loaded
31894      * @return {Boolean}
31895      */
31896     isLoaded : function(){
31897         return this.loaded;
31898     },
31899     
31900     hasChildNodes : function(){
31901         if(!this.isLeaf() && !this.loaded){
31902             return true;
31903         }else{
31904             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31905         }
31906     },
31907
31908     /**
31909      * Trigger a reload for this node
31910      * @param {Function} callback
31911      */
31912     reload : function(callback){
31913         this.collapse(false, false);
31914         while(this.firstChild){
31915             this.removeChild(this.firstChild);
31916         }
31917         this.childrenRendered = false;
31918         this.loaded = false;
31919         if(this.isHiddenRoot()){
31920             this.expanded = false;
31921         }
31922         this.expand(false, false, callback);
31923     }
31924 });/*
31925  * Based on:
31926  * Ext JS Library 1.1.1
31927  * Copyright(c) 2006-2007, Ext JS, LLC.
31928  *
31929  * Originally Released Under LGPL - original licence link has changed is not relivant.
31930  *
31931  * Fork - LGPL
31932  * <script type="text/javascript">
31933  */
31934  
31935 /**
31936  * @class Roo.tree.TreeNodeUI
31937  * @constructor
31938  * @param {Object} node The node to render
31939  * The TreeNode UI implementation is separate from the
31940  * tree implementation. Unless you are customizing the tree UI,
31941  * you should never have to use this directly.
31942  */
31943 Roo.tree.TreeNodeUI = function(node){
31944     this.node = node;
31945     this.rendered = false;
31946     this.animating = false;
31947     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31948 };
31949
31950 Roo.tree.TreeNodeUI.prototype = {
31951     removeChild : function(node){
31952         if(this.rendered){
31953             this.ctNode.removeChild(node.ui.getEl());
31954         }
31955     },
31956
31957     beforeLoad : function(){
31958          this.addClass("x-tree-node-loading");
31959     },
31960
31961     afterLoad : function(){
31962          this.removeClass("x-tree-node-loading");
31963     },
31964
31965     onTextChange : function(node, text, oldText){
31966         if(this.rendered){
31967             this.textNode.innerHTML = text;
31968         }
31969     },
31970
31971     onDisableChange : function(node, state){
31972         this.disabled = state;
31973         if(state){
31974             this.addClass("x-tree-node-disabled");
31975         }else{
31976             this.removeClass("x-tree-node-disabled");
31977         }
31978     },
31979
31980     onSelectedChange : function(state){
31981         if(state){
31982             this.focus();
31983             this.addClass("x-tree-selected");
31984         }else{
31985             //this.blur();
31986             this.removeClass("x-tree-selected");
31987         }
31988     },
31989
31990     onMove : function(tree, node, oldParent, newParent, index, refNode){
31991         this.childIndent = null;
31992         if(this.rendered){
31993             var targetNode = newParent.ui.getContainer();
31994             if(!targetNode){//target not rendered
31995                 this.holder = document.createElement("div");
31996                 this.holder.appendChild(this.wrap);
31997                 return;
31998             }
31999             var insertBefore = refNode ? refNode.ui.getEl() : null;
32000             if(insertBefore){
32001                 targetNode.insertBefore(this.wrap, insertBefore);
32002             }else{
32003                 targetNode.appendChild(this.wrap);
32004             }
32005             this.node.renderIndent(true);
32006         }
32007     },
32008
32009     addClass : function(cls){
32010         if(this.elNode){
32011             Roo.fly(this.elNode).addClass(cls);
32012         }
32013     },
32014
32015     removeClass : function(cls){
32016         if(this.elNode){
32017             Roo.fly(this.elNode).removeClass(cls);
32018         }
32019     },
32020
32021     remove : function(){
32022         if(this.rendered){
32023             this.holder = document.createElement("div");
32024             this.holder.appendChild(this.wrap);
32025         }
32026     },
32027
32028     fireEvent : function(){
32029         return this.node.fireEvent.apply(this.node, arguments);
32030     },
32031
32032     initEvents : function(){
32033         this.node.on("move", this.onMove, this);
32034         var E = Roo.EventManager;
32035         var a = this.anchor;
32036
32037         var el = Roo.fly(a, '_treeui');
32038
32039         if(Roo.isOpera){ // opera render bug ignores the CSS
32040             el.setStyle("text-decoration", "none");
32041         }
32042
32043         el.on("click", this.onClick, this);
32044         el.on("dblclick", this.onDblClick, this);
32045
32046         if(this.checkbox){
32047             Roo.EventManager.on(this.checkbox,
32048                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32049         }
32050
32051         el.on("contextmenu", this.onContextMenu, this);
32052
32053         var icon = Roo.fly(this.iconNode);
32054         icon.on("click", this.onClick, this);
32055         icon.on("dblclick", this.onDblClick, this);
32056         icon.on("contextmenu", this.onContextMenu, this);
32057         E.on(this.ecNode, "click", this.ecClick, this, true);
32058
32059         if(this.node.disabled){
32060             this.addClass("x-tree-node-disabled");
32061         }
32062         if(this.node.hidden){
32063             this.addClass("x-tree-node-disabled");
32064         }
32065         var ot = this.node.getOwnerTree();
32066         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32067         if(dd && (!this.node.isRoot || ot.rootVisible)){
32068             Roo.dd.Registry.register(this.elNode, {
32069                 node: this.node,
32070                 handles: this.getDDHandles(),
32071                 isHandle: false
32072             });
32073         }
32074     },
32075
32076     getDDHandles : function(){
32077         return [this.iconNode, this.textNode];
32078     },
32079
32080     hide : function(){
32081         if(this.rendered){
32082             this.wrap.style.display = "none";
32083         }
32084     },
32085
32086     show : function(){
32087         if(this.rendered){
32088             this.wrap.style.display = "";
32089         }
32090     },
32091
32092     onContextMenu : function(e){
32093         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32094             e.preventDefault();
32095             this.focus();
32096             this.fireEvent("contextmenu", this.node, e);
32097         }
32098     },
32099
32100     onClick : function(e){
32101         if(this.dropping){
32102             e.stopEvent();
32103             return;
32104         }
32105         if(this.fireEvent("beforeclick", this.node, e) !== false){
32106             if(!this.disabled && this.node.attributes.href){
32107                 this.fireEvent("click", this.node, e);
32108                 return;
32109             }
32110             e.preventDefault();
32111             if(this.disabled){
32112                 return;
32113             }
32114
32115             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32116                 this.node.toggle();
32117             }
32118
32119             this.fireEvent("click", this.node, e);
32120         }else{
32121             e.stopEvent();
32122         }
32123     },
32124
32125     onDblClick : function(e){
32126         e.preventDefault();
32127         if(this.disabled){
32128             return;
32129         }
32130         if(this.checkbox){
32131             this.toggleCheck();
32132         }
32133         if(!this.animating && this.node.hasChildNodes()){
32134             this.node.toggle();
32135         }
32136         this.fireEvent("dblclick", this.node, e);
32137     },
32138
32139     onCheckChange : function(){
32140         var checked = this.checkbox.checked;
32141         this.node.attributes.checked = checked;
32142         this.fireEvent('checkchange', this.node, checked);
32143     },
32144
32145     ecClick : function(e){
32146         if(!this.animating && this.node.hasChildNodes()){
32147             this.node.toggle();
32148         }
32149     },
32150
32151     startDrop : function(){
32152         this.dropping = true;
32153     },
32154
32155     // delayed drop so the click event doesn't get fired on a drop
32156     endDrop : function(){
32157        setTimeout(function(){
32158            this.dropping = false;
32159        }.createDelegate(this), 50);
32160     },
32161
32162     expand : function(){
32163         this.updateExpandIcon();
32164         this.ctNode.style.display = "";
32165     },
32166
32167     focus : function(){
32168         if(!this.node.preventHScroll){
32169             try{this.anchor.focus();
32170             }catch(e){}
32171         }else if(!Roo.isIE){
32172             try{
32173                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32174                 var l = noscroll.scrollLeft;
32175                 this.anchor.focus();
32176                 noscroll.scrollLeft = l;
32177             }catch(e){}
32178         }
32179     },
32180
32181     toggleCheck : function(value){
32182         var cb = this.checkbox;
32183         if(cb){
32184             cb.checked = (value === undefined ? !cb.checked : value);
32185         }
32186     },
32187
32188     blur : function(){
32189         try{
32190             this.anchor.blur();
32191         }catch(e){}
32192     },
32193
32194     animExpand : function(callback){
32195         var ct = Roo.get(this.ctNode);
32196         ct.stopFx();
32197         if(!this.node.hasChildNodes()){
32198             this.updateExpandIcon();
32199             this.ctNode.style.display = "";
32200             Roo.callback(callback);
32201             return;
32202         }
32203         this.animating = true;
32204         this.updateExpandIcon();
32205
32206         ct.slideIn('t', {
32207            callback : function(){
32208                this.animating = false;
32209                Roo.callback(callback);
32210             },
32211             scope: this,
32212             duration: this.node.ownerTree.duration || .25
32213         });
32214     },
32215
32216     highlight : function(){
32217         var tree = this.node.getOwnerTree();
32218         Roo.fly(this.wrap).highlight(
32219             tree.hlColor || "C3DAF9",
32220             {endColor: tree.hlBaseColor}
32221         );
32222     },
32223
32224     collapse : function(){
32225         this.updateExpandIcon();
32226         this.ctNode.style.display = "none";
32227     },
32228
32229     animCollapse : function(callback){
32230         var ct = Roo.get(this.ctNode);
32231         ct.enableDisplayMode('block');
32232         ct.stopFx();
32233
32234         this.animating = true;
32235         this.updateExpandIcon();
32236
32237         ct.slideOut('t', {
32238             callback : function(){
32239                this.animating = false;
32240                Roo.callback(callback);
32241             },
32242             scope: this,
32243             duration: this.node.ownerTree.duration || .25
32244         });
32245     },
32246
32247     getContainer : function(){
32248         return this.ctNode;
32249     },
32250
32251     getEl : function(){
32252         return this.wrap;
32253     },
32254
32255     appendDDGhost : function(ghostNode){
32256         ghostNode.appendChild(this.elNode.cloneNode(true));
32257     },
32258
32259     getDDRepairXY : function(){
32260         return Roo.lib.Dom.getXY(this.iconNode);
32261     },
32262
32263     onRender : function(){
32264         this.render();
32265     },
32266
32267     render : function(bulkRender){
32268         var n = this.node, a = n.attributes;
32269         var targetNode = n.parentNode ?
32270               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32271
32272         if(!this.rendered){
32273             this.rendered = true;
32274
32275             this.renderElements(n, a, targetNode, bulkRender);
32276
32277             if(a.qtip){
32278                if(this.textNode.setAttributeNS){
32279                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32280                    if(a.qtipTitle){
32281                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32282                    }
32283                }else{
32284                    this.textNode.setAttribute("ext:qtip", a.qtip);
32285                    if(a.qtipTitle){
32286                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32287                    }
32288                }
32289             }else if(a.qtipCfg){
32290                 a.qtipCfg.target = Roo.id(this.textNode);
32291                 Roo.QuickTips.register(a.qtipCfg);
32292             }
32293             this.initEvents();
32294             if(!this.node.expanded){
32295                 this.updateExpandIcon();
32296             }
32297         }else{
32298             if(bulkRender === true) {
32299                 targetNode.appendChild(this.wrap);
32300             }
32301         }
32302     },
32303
32304     renderElements : function(n, a, targetNode, bulkRender){
32305         // add some indent caching, this helps performance when rendering a large tree
32306         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32307         var t = n.getOwnerTree();
32308         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32309         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32310         var cb = typeof a.checked == 'boolean';
32311         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32312         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32313             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32314             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32315             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32316             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32317             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32318              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32319                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32320             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32321             "</li>"];
32322
32323         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32324             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32325                                 n.nextSibling.ui.getEl(), buf.join(""));
32326         }else{
32327             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32328         }
32329
32330         this.elNode = this.wrap.childNodes[0];
32331         this.ctNode = this.wrap.childNodes[1];
32332         var cs = this.elNode.childNodes;
32333         this.indentNode = cs[0];
32334         this.ecNode = cs[1];
32335         this.iconNode = cs[2];
32336         var index = 3;
32337         if(cb){
32338             this.checkbox = cs[3];
32339             index++;
32340         }
32341         this.anchor = cs[index];
32342         this.textNode = cs[index].firstChild;
32343     },
32344
32345     getAnchor : function(){
32346         return this.anchor;
32347     },
32348
32349     getTextEl : function(){
32350         return this.textNode;
32351     },
32352
32353     getIconEl : function(){
32354         return this.iconNode;
32355     },
32356
32357     isChecked : function(){
32358         return this.checkbox ? this.checkbox.checked : false;
32359     },
32360
32361     updateExpandIcon : function(){
32362         if(this.rendered){
32363             var n = this.node, c1, c2;
32364             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32365             var hasChild = n.hasChildNodes();
32366             if(hasChild){
32367                 if(n.expanded){
32368                     cls += "-minus";
32369                     c1 = "x-tree-node-collapsed";
32370                     c2 = "x-tree-node-expanded";
32371                 }else{
32372                     cls += "-plus";
32373                     c1 = "x-tree-node-expanded";
32374                     c2 = "x-tree-node-collapsed";
32375                 }
32376                 if(this.wasLeaf){
32377                     this.removeClass("x-tree-node-leaf");
32378                     this.wasLeaf = false;
32379                 }
32380                 if(this.c1 != c1 || this.c2 != c2){
32381                     Roo.fly(this.elNode).replaceClass(c1, c2);
32382                     this.c1 = c1; this.c2 = c2;
32383                 }
32384             }else{
32385                 if(!this.wasLeaf){
32386                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32387                     delete this.c1;
32388                     delete this.c2;
32389                     this.wasLeaf = true;
32390                 }
32391             }
32392             var ecc = "x-tree-ec-icon "+cls;
32393             if(this.ecc != ecc){
32394                 this.ecNode.className = ecc;
32395                 this.ecc = ecc;
32396             }
32397         }
32398     },
32399
32400     getChildIndent : function(){
32401         if(!this.childIndent){
32402             var buf = [];
32403             var p = this.node;
32404             while(p){
32405                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32406                     if(!p.isLast()) {
32407                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32408                     } else {
32409                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32410                     }
32411                 }
32412                 p = p.parentNode;
32413             }
32414             this.childIndent = buf.join("");
32415         }
32416         return this.childIndent;
32417     },
32418
32419     renderIndent : function(){
32420         if(this.rendered){
32421             var indent = "";
32422             var p = this.node.parentNode;
32423             if(p){
32424                 indent = p.ui.getChildIndent();
32425             }
32426             if(this.indentMarkup != indent){ // don't rerender if not required
32427                 this.indentNode.innerHTML = indent;
32428                 this.indentMarkup = indent;
32429             }
32430             this.updateExpandIcon();
32431         }
32432     }
32433 };
32434
32435 Roo.tree.RootTreeNodeUI = function(){
32436     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32437 };
32438 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32439     render : function(){
32440         if(!this.rendered){
32441             var targetNode = this.node.ownerTree.innerCt.dom;
32442             this.node.expanded = true;
32443             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32444             this.wrap = this.ctNode = targetNode.firstChild;
32445         }
32446     },
32447     collapse : function(){
32448     },
32449     expand : function(){
32450     }
32451 });/*
32452  * Based on:
32453  * Ext JS Library 1.1.1
32454  * Copyright(c) 2006-2007, Ext JS, LLC.
32455  *
32456  * Originally Released Under LGPL - original licence link has changed is not relivant.
32457  *
32458  * Fork - LGPL
32459  * <script type="text/javascript">
32460  */
32461 /**
32462  * @class Roo.tree.TreeLoader
32463  * @extends Roo.util.Observable
32464  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32465  * nodes from a specified URL. The response must be a javascript Array definition
32466  * who's elements are node definition objects. eg:
32467  * <pre><code>
32468    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32469     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32470 </code></pre>
32471  * <br><br>
32472  * A server request is sent, and child nodes are loaded only when a node is expanded.
32473  * The loading node's id is passed to the server under the parameter name "node" to
32474  * enable the server to produce the correct child nodes.
32475  * <br><br>
32476  * To pass extra parameters, an event handler may be attached to the "beforeload"
32477  * event, and the parameters specified in the TreeLoader's baseParams property:
32478  * <pre><code>
32479     myTreeLoader.on("beforeload", function(treeLoader, node) {
32480         this.baseParams.category = node.attributes.category;
32481     }, this);
32482 </code></pre><
32483  * This would pass an HTTP parameter called "category" to the server containing
32484  * the value of the Node's "category" attribute.
32485  * @constructor
32486  * Creates a new Treeloader.
32487  * @param {Object} config A config object containing config properties.
32488  */
32489 Roo.tree.TreeLoader = function(config){
32490     this.baseParams = {};
32491     this.requestMethod = "POST";
32492     Roo.apply(this, config);
32493
32494     this.addEvents({
32495     
32496         /**
32497          * @event beforeload
32498          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32499          * @param {Object} This TreeLoader object.
32500          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32501          * @param {Object} callback The callback function specified in the {@link #load} call.
32502          */
32503         beforeload : true,
32504         /**
32505          * @event load
32506          * Fires when the node has been successfuly loaded.
32507          * @param {Object} This TreeLoader object.
32508          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32509          * @param {Object} response The response object containing the data from the server.
32510          */
32511         load : true,
32512         /**
32513          * @event loadexception
32514          * Fires if the network request failed.
32515          * @param {Object} This TreeLoader object.
32516          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32517          * @param {Object} response The response object containing the data from the server.
32518          */
32519         loadexception : true,
32520         /**
32521          * @event create
32522          * Fires before a node is created, enabling you to return custom Node types 
32523          * @param {Object} This TreeLoader object.
32524          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32525          */
32526         create : true
32527     });
32528
32529     Roo.tree.TreeLoader.superclass.constructor.call(this);
32530 };
32531
32532 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32533     /**
32534     * @cfg {String} dataUrl The URL from which to request a Json string which
32535     * specifies an array of node definition object representing the child nodes
32536     * to be loaded.
32537     */
32538     /**
32539     * @cfg {Object} baseParams (optional) An object containing properties which
32540     * specify HTTP parameters to be passed to each request for child nodes.
32541     */
32542     /**
32543     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32544     * created by this loader. If the attributes sent by the server have an attribute in this object,
32545     * they take priority.
32546     */
32547     /**
32548     * @cfg {Object} uiProviders (optional) An object containing properties which
32549     * 
32550     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32551     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32552     * <i>uiProvider</i> attribute of a returned child node is a string rather
32553     * than a reference to a TreeNodeUI implementation, this that string value
32554     * is used as a property name in the uiProviders object. You can define the provider named
32555     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32556     */
32557     uiProviders : {},
32558
32559     /**
32560     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32561     * child nodes before loading.
32562     */
32563     clearOnLoad : true,
32564
32565     /**
32566     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32567     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32568     * Grid query { data : [ .....] }
32569     */
32570     
32571     root : false,
32572      /**
32573     * @cfg {String} queryParam (optional) 
32574     * Name of the query as it will be passed on the querystring (defaults to 'node')
32575     * eg. the request will be ?node=[id]
32576     */
32577     
32578     
32579     queryParam: false,
32580     
32581     /**
32582      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32583      * This is called automatically when a node is expanded, but may be used to reload
32584      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32585      * @param {Roo.tree.TreeNode} node
32586      * @param {Function} callback
32587      */
32588     load : function(node, callback){
32589         if(this.clearOnLoad){
32590             while(node.firstChild){
32591                 node.removeChild(node.firstChild);
32592             }
32593         }
32594         if(node.attributes.children){ // preloaded json children
32595             var cs = node.attributes.children;
32596             for(var i = 0, len = cs.length; i < len; i++){
32597                 node.appendChild(this.createNode(cs[i]));
32598             }
32599             if(typeof callback == "function"){
32600                 callback();
32601             }
32602         }else if(this.dataUrl){
32603             this.requestData(node, callback);
32604         }
32605     },
32606
32607     getParams: function(node){
32608         var buf = [], bp = this.baseParams;
32609         for(var key in bp){
32610             if(typeof bp[key] != "function"){
32611                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32612             }
32613         }
32614         var n = this.queryParam === false ? 'node' : this.queryParam;
32615         buf.push(n + "=", encodeURIComponent(node.id));
32616         return buf.join("");
32617     },
32618
32619     requestData : function(node, callback){
32620         if(this.fireEvent("beforeload", this, node, callback) !== false){
32621             this.transId = Roo.Ajax.request({
32622                 method:this.requestMethod,
32623                 url: this.dataUrl||this.url,
32624                 success: this.handleResponse,
32625                 failure: this.handleFailure,
32626                 scope: this,
32627                 argument: {callback: callback, node: node},
32628                 params: this.getParams(node)
32629             });
32630         }else{
32631             // if the load is cancelled, make sure we notify
32632             // the node that we are done
32633             if(typeof callback == "function"){
32634                 callback();
32635             }
32636         }
32637     },
32638
32639     isLoading : function(){
32640         return this.transId ? true : false;
32641     },
32642
32643     abort : function(){
32644         if(this.isLoading()){
32645             Roo.Ajax.abort(this.transId);
32646         }
32647     },
32648
32649     // private
32650     createNode : function(attr){
32651         // apply baseAttrs, nice idea Corey!
32652         if(this.baseAttrs){
32653             Roo.applyIf(attr, this.baseAttrs);
32654         }
32655         if(this.applyLoader !== false){
32656             attr.loader = this;
32657         }
32658         // uiProvider = depreciated..
32659         
32660         if(typeof(attr.uiProvider) == 'string'){
32661            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32662                 /**  eval:var:attr */ eval(attr.uiProvider);
32663         }
32664         if(typeof(this.uiProviders['default']) != 'undefined') {
32665             attr.uiProvider = this.uiProviders['default'];
32666         }
32667         
32668         this.fireEvent('create', this, attr);
32669         
32670         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32671         return(attr.leaf ?
32672                         new Roo.tree.TreeNode(attr) :
32673                         new Roo.tree.AsyncTreeNode(attr));
32674     },
32675
32676     processResponse : function(response, node, callback){
32677         var json = response.responseText;
32678         try {
32679             
32680             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32681             if (this.root !== false) {
32682                 o = o[this.root];
32683             }
32684             
32685             for(var i = 0, len = o.length; i < len; i++){
32686                 var n = this.createNode(o[i]);
32687                 if(n){
32688                     node.appendChild(n);
32689                 }
32690             }
32691             if(typeof callback == "function"){
32692                 callback(this, node);
32693             }
32694         }catch(e){
32695             this.handleFailure(response);
32696         }
32697     },
32698
32699     handleResponse : function(response){
32700         this.transId = false;
32701         var a = response.argument;
32702         this.processResponse(response, a.node, a.callback);
32703         this.fireEvent("load", this, a.node, response);
32704     },
32705
32706     handleFailure : function(response){
32707         this.transId = false;
32708         var a = response.argument;
32709         this.fireEvent("loadexception", this, a.node, response);
32710         if(typeof a.callback == "function"){
32711             a.callback(this, a.node);
32712         }
32713     }
32714 });/*
32715  * Based on:
32716  * Ext JS Library 1.1.1
32717  * Copyright(c) 2006-2007, Ext JS, LLC.
32718  *
32719  * Originally Released Under LGPL - original licence link has changed is not relivant.
32720  *
32721  * Fork - LGPL
32722  * <script type="text/javascript">
32723  */
32724
32725 /**
32726 * @class Roo.tree.TreeFilter
32727 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32728 * @param {TreePanel} tree
32729 * @param {Object} config (optional)
32730  */
32731 Roo.tree.TreeFilter = function(tree, config){
32732     this.tree = tree;
32733     this.filtered = {};
32734     Roo.apply(this, config);
32735 };
32736
32737 Roo.tree.TreeFilter.prototype = {
32738     clearBlank:false,
32739     reverse:false,
32740     autoClear:false,
32741     remove:false,
32742
32743      /**
32744      * Filter the data by a specific attribute.
32745      * @param {String/RegExp} value Either string that the attribute value
32746      * should start with or a RegExp to test against the attribute
32747      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32748      * @param {TreeNode} startNode (optional) The node to start the filter at.
32749      */
32750     filter : function(value, attr, startNode){
32751         attr = attr || "text";
32752         var f;
32753         if(typeof value == "string"){
32754             var vlen = value.length;
32755             // auto clear empty filter
32756             if(vlen == 0 && this.clearBlank){
32757                 this.clear();
32758                 return;
32759             }
32760             value = value.toLowerCase();
32761             f = function(n){
32762                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32763             };
32764         }else if(value.exec){ // regex?
32765             f = function(n){
32766                 return value.test(n.attributes[attr]);
32767             };
32768         }else{
32769             throw 'Illegal filter type, must be string or regex';
32770         }
32771         this.filterBy(f, null, startNode);
32772         },
32773
32774     /**
32775      * Filter by a function. The passed function will be called with each
32776      * node in the tree (or from the startNode). If the function returns true, the node is kept
32777      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32778      * @param {Function} fn The filter function
32779      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32780      */
32781     filterBy : function(fn, scope, startNode){
32782         startNode = startNode || this.tree.root;
32783         if(this.autoClear){
32784             this.clear();
32785         }
32786         var af = this.filtered, rv = this.reverse;
32787         var f = function(n){
32788             if(n == startNode){
32789                 return true;
32790             }
32791             if(af[n.id]){
32792                 return false;
32793             }
32794             var m = fn.call(scope || n, n);
32795             if(!m || rv){
32796                 af[n.id] = n;
32797                 n.ui.hide();
32798                 return false;
32799             }
32800             return true;
32801         };
32802         startNode.cascade(f);
32803         if(this.remove){
32804            for(var id in af){
32805                if(typeof id != "function"){
32806                    var n = af[id];
32807                    if(n && n.parentNode){
32808                        n.parentNode.removeChild(n);
32809                    }
32810                }
32811            }
32812         }
32813     },
32814
32815     /**
32816      * Clears the current filter. Note: with the "remove" option
32817      * set a filter cannot be cleared.
32818      */
32819     clear : function(){
32820         var t = this.tree;
32821         var af = this.filtered;
32822         for(var id in af){
32823             if(typeof id != "function"){
32824                 var n = af[id];
32825                 if(n){
32826                     n.ui.show();
32827                 }
32828             }
32829         }
32830         this.filtered = {};
32831     }
32832 };
32833 /*
32834  * Based on:
32835  * Ext JS Library 1.1.1
32836  * Copyright(c) 2006-2007, Ext JS, LLC.
32837  *
32838  * Originally Released Under LGPL - original licence link has changed is not relivant.
32839  *
32840  * Fork - LGPL
32841  * <script type="text/javascript">
32842  */
32843  
32844
32845 /**
32846  * @class Roo.tree.TreeSorter
32847  * Provides sorting of nodes in a TreePanel
32848  * 
32849  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32850  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32851  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32852  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32853  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32854  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32855  * @constructor
32856  * @param {TreePanel} tree
32857  * @param {Object} config
32858  */
32859 Roo.tree.TreeSorter = function(tree, config){
32860     Roo.apply(this, config);
32861     tree.on("beforechildrenrendered", this.doSort, this);
32862     tree.on("append", this.updateSort, this);
32863     tree.on("insert", this.updateSort, this);
32864     
32865     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32866     var p = this.property || "text";
32867     var sortType = this.sortType;
32868     var fs = this.folderSort;
32869     var cs = this.caseSensitive === true;
32870     var leafAttr = this.leafAttr || 'leaf';
32871
32872     this.sortFn = function(n1, n2){
32873         if(fs){
32874             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32875                 return 1;
32876             }
32877             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32878                 return -1;
32879             }
32880         }
32881         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32882         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32883         if(v1 < v2){
32884                         return dsc ? +1 : -1;
32885                 }else if(v1 > v2){
32886                         return dsc ? -1 : +1;
32887         }else{
32888                 return 0;
32889         }
32890     };
32891 };
32892
32893 Roo.tree.TreeSorter.prototype = {
32894     doSort : function(node){
32895         node.sort(this.sortFn);
32896     },
32897     
32898     compareNodes : function(n1, n2){
32899         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32900     },
32901     
32902     updateSort : function(tree, node){
32903         if(node.childrenRendered){
32904             this.doSort.defer(1, this, [node]);
32905         }
32906     }
32907 };/*
32908  * Based on:
32909  * Ext JS Library 1.1.1
32910  * Copyright(c) 2006-2007, Ext JS, LLC.
32911  *
32912  * Originally Released Under LGPL - original licence link has changed is not relivant.
32913  *
32914  * Fork - LGPL
32915  * <script type="text/javascript">
32916  */
32917
32918 if(Roo.dd.DropZone){
32919     
32920 Roo.tree.TreeDropZone = function(tree, config){
32921     this.allowParentInsert = false;
32922     this.allowContainerDrop = false;
32923     this.appendOnly = false;
32924     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32925     this.tree = tree;
32926     this.lastInsertClass = "x-tree-no-status";
32927     this.dragOverData = {};
32928 };
32929
32930 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32931     ddGroup : "TreeDD",
32932     
32933     expandDelay : 1000,
32934     
32935     expandNode : function(node){
32936         if(node.hasChildNodes() && !node.isExpanded()){
32937             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32938         }
32939     },
32940     
32941     queueExpand : function(node){
32942         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32943     },
32944     
32945     cancelExpand : function(){
32946         if(this.expandProcId){
32947             clearTimeout(this.expandProcId);
32948             this.expandProcId = false;
32949         }
32950     },
32951     
32952     isValidDropPoint : function(n, pt, dd, e, data){
32953         if(!n || !data){ return false; }
32954         var targetNode = n.node;
32955         var dropNode = data.node;
32956         // default drop rules
32957         if(!(targetNode && targetNode.isTarget && pt)){
32958             return false;
32959         }
32960         if(pt == "append" && targetNode.allowChildren === false){
32961             return false;
32962         }
32963         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32964             return false;
32965         }
32966         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32967             return false;
32968         }
32969         // reuse the object
32970         var overEvent = this.dragOverData;
32971         overEvent.tree = this.tree;
32972         overEvent.target = targetNode;
32973         overEvent.data = data;
32974         overEvent.point = pt;
32975         overEvent.source = dd;
32976         overEvent.rawEvent = e;
32977         overEvent.dropNode = dropNode;
32978         overEvent.cancel = false;  
32979         var result = this.tree.fireEvent("nodedragover", overEvent);
32980         return overEvent.cancel === false && result !== false;
32981     },
32982     
32983     getDropPoint : function(e, n, dd){
32984         var tn = n.node;
32985         if(tn.isRoot){
32986             return tn.allowChildren !== false ? "append" : false; // always append for root
32987         }
32988         var dragEl = n.ddel;
32989         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32990         var y = Roo.lib.Event.getPageY(e);
32991         //var noAppend = tn.allowChildren === false || tn.isLeaf();
32992         
32993         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
32994         var noAppend = tn.allowChildren === false;
32995         if(this.appendOnly || tn.parentNode.allowChildren === false){
32996             return noAppend ? false : "append";
32997         }
32998         var noBelow = false;
32999         if(!this.allowParentInsert){
33000             noBelow = tn.hasChildNodes() && tn.isExpanded();
33001         }
33002         var q = (b - t) / (noAppend ? 2 : 3);
33003         if(y >= t && y < (t + q)){
33004             return "above";
33005         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33006             return "below";
33007         }else{
33008             return "append";
33009         }
33010     },
33011     
33012     onNodeEnter : function(n, dd, e, data){
33013         this.cancelExpand();
33014     },
33015     
33016     onNodeOver : function(n, dd, e, data){
33017         var pt = this.getDropPoint(e, n, dd);
33018         var node = n.node;
33019         
33020         // auto node expand check
33021         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33022             this.queueExpand(node);
33023         }else if(pt != "append"){
33024             this.cancelExpand();
33025         }
33026         
33027         // set the insert point style on the target node
33028         var returnCls = this.dropNotAllowed;
33029         if(this.isValidDropPoint(n, pt, dd, e, data)){
33030            if(pt){
33031                var el = n.ddel;
33032                var cls;
33033                if(pt == "above"){
33034                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33035                    cls = "x-tree-drag-insert-above";
33036                }else if(pt == "below"){
33037                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33038                    cls = "x-tree-drag-insert-below";
33039                }else{
33040                    returnCls = "x-tree-drop-ok-append";
33041                    cls = "x-tree-drag-append";
33042                }
33043                if(this.lastInsertClass != cls){
33044                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33045                    this.lastInsertClass = cls;
33046                }
33047            }
33048        }
33049        return returnCls;
33050     },
33051     
33052     onNodeOut : function(n, dd, e, data){
33053         this.cancelExpand();
33054         this.removeDropIndicators(n);
33055     },
33056     
33057     onNodeDrop : function(n, dd, e, data){
33058         var point = this.getDropPoint(e, n, dd);
33059         var targetNode = n.node;
33060         targetNode.ui.startDrop();
33061         if(!this.isValidDropPoint(n, point, dd, e, data)){
33062             targetNode.ui.endDrop();
33063             return false;
33064         }
33065         // first try to find the drop node
33066         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33067         var dropEvent = {
33068             tree : this.tree,
33069             target: targetNode,
33070             data: data,
33071             point: point,
33072             source: dd,
33073             rawEvent: e,
33074             dropNode: dropNode,
33075             cancel: !dropNode   
33076         };
33077         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33078         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33079             targetNode.ui.endDrop();
33080             return false;
33081         }
33082         // allow target changing
33083         targetNode = dropEvent.target;
33084         if(point == "append" && !targetNode.isExpanded()){
33085             targetNode.expand(false, null, function(){
33086                 this.completeDrop(dropEvent);
33087             }.createDelegate(this));
33088         }else{
33089             this.completeDrop(dropEvent);
33090         }
33091         return true;
33092     },
33093     
33094     completeDrop : function(de){
33095         var ns = de.dropNode, p = de.point, t = de.target;
33096         if(!(ns instanceof Array)){
33097             ns = [ns];
33098         }
33099         var n;
33100         for(var i = 0, len = ns.length; i < len; i++){
33101             n = ns[i];
33102             if(p == "above"){
33103                 t.parentNode.insertBefore(n, t);
33104             }else if(p == "below"){
33105                 t.parentNode.insertBefore(n, t.nextSibling);
33106             }else{
33107                 t.appendChild(n);
33108             }
33109         }
33110         n.ui.focus();
33111         if(this.tree.hlDrop){
33112             n.ui.highlight();
33113         }
33114         t.ui.endDrop();
33115         this.tree.fireEvent("nodedrop", de);
33116     },
33117     
33118     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33119         if(this.tree.hlDrop){
33120             dropNode.ui.focus();
33121             dropNode.ui.highlight();
33122         }
33123         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33124     },
33125     
33126     getTree : function(){
33127         return this.tree;
33128     },
33129     
33130     removeDropIndicators : function(n){
33131         if(n && n.ddel){
33132             var el = n.ddel;
33133             Roo.fly(el).removeClass([
33134                     "x-tree-drag-insert-above",
33135                     "x-tree-drag-insert-below",
33136                     "x-tree-drag-append"]);
33137             this.lastInsertClass = "_noclass";
33138         }
33139     },
33140     
33141     beforeDragDrop : function(target, e, id){
33142         this.cancelExpand();
33143         return true;
33144     },
33145     
33146     afterRepair : function(data){
33147         if(data && Roo.enableFx){
33148             data.node.ui.highlight();
33149         }
33150         this.hideProxy();
33151     }    
33152 });
33153
33154 }
33155 /*
33156  * Based on:
33157  * Ext JS Library 1.1.1
33158  * Copyright(c) 2006-2007, Ext JS, LLC.
33159  *
33160  * Originally Released Under LGPL - original licence link has changed is not relivant.
33161  *
33162  * Fork - LGPL
33163  * <script type="text/javascript">
33164  */
33165  
33166
33167 if(Roo.dd.DragZone){
33168 Roo.tree.TreeDragZone = function(tree, config){
33169     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33170     this.tree = tree;
33171 };
33172
33173 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33174     ddGroup : "TreeDD",
33175     
33176     onBeforeDrag : function(data, e){
33177         var n = data.node;
33178         return n && n.draggable && !n.disabled;
33179     },
33180     
33181     onInitDrag : function(e){
33182         var data = this.dragData;
33183         this.tree.getSelectionModel().select(data.node);
33184         this.proxy.update("");
33185         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33186         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33187     },
33188     
33189     getRepairXY : function(e, data){
33190         return data.node.ui.getDDRepairXY();
33191     },
33192     
33193     onEndDrag : function(data, e){
33194         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33195     },
33196     
33197     onValidDrop : function(dd, e, id){
33198         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33199         this.hideProxy();
33200     },
33201     
33202     beforeInvalidDrop : function(e, id){
33203         // this scrolls the original position back into view
33204         var sm = this.tree.getSelectionModel();
33205         sm.clearSelections();
33206         sm.select(this.dragData.node);
33207     }
33208 });
33209 }/*
33210  * Based on:
33211  * Ext JS Library 1.1.1
33212  * Copyright(c) 2006-2007, Ext JS, LLC.
33213  *
33214  * Originally Released Under LGPL - original licence link has changed is not relivant.
33215  *
33216  * Fork - LGPL
33217  * <script type="text/javascript">
33218  */
33219 /**
33220  * @class Roo.tree.TreeEditor
33221  * @extends Roo.Editor
33222  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33223  * as the editor field.
33224  * @constructor
33225  * @param {TreePanel} tree
33226  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33227  */
33228 Roo.tree.TreeEditor = function(tree, config){
33229     config = config || {};
33230     var field = config.events ? config : new Roo.form.TextField(config);
33231     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33232
33233     this.tree = tree;
33234
33235     tree.on('beforeclick', this.beforeNodeClick, this);
33236     tree.getTreeEl().on('mousedown', this.hide, this);
33237     this.on('complete', this.updateNode, this);
33238     this.on('beforestartedit', this.fitToTree, this);
33239     this.on('startedit', this.bindScroll, this, {delay:10});
33240     this.on('specialkey', this.onSpecialKey, this);
33241 };
33242
33243 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33244     /**
33245      * @cfg {String} alignment
33246      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33247      */
33248     alignment: "l-l",
33249     // inherit
33250     autoSize: false,
33251     /**
33252      * @cfg {Boolean} hideEl
33253      * True to hide the bound element while the editor is displayed (defaults to false)
33254      */
33255     hideEl : false,
33256     /**
33257      * @cfg {String} cls
33258      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33259      */
33260     cls: "x-small-editor x-tree-editor",
33261     /**
33262      * @cfg {Boolean} shim
33263      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33264      */
33265     shim:false,
33266     // inherit
33267     shadow:"frame",
33268     /**
33269      * @cfg {Number} maxWidth
33270      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33271      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33272      * scroll and client offsets into account prior to each edit.
33273      */
33274     maxWidth: 250,
33275
33276     editDelay : 350,
33277
33278     // private
33279     fitToTree : function(ed, el){
33280         var td = this.tree.getTreeEl().dom, nd = el.dom;
33281         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33282             td.scrollLeft = nd.offsetLeft;
33283         }
33284         var w = Math.min(
33285                 this.maxWidth,
33286                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33287         this.setSize(w, '');
33288     },
33289
33290     // private
33291     triggerEdit : function(node){
33292         this.completeEdit();
33293         this.editNode = node;
33294         this.startEdit(node.ui.textNode, node.text);
33295     },
33296
33297     // private
33298     bindScroll : function(){
33299         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33300     },
33301
33302     // private
33303     beforeNodeClick : function(node, e){
33304         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33305         this.lastClick = new Date();
33306         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33307             e.stopEvent();
33308             this.triggerEdit(node);
33309             return false;
33310         }
33311     },
33312
33313     // private
33314     updateNode : function(ed, value){
33315         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33316         this.editNode.setText(value);
33317     },
33318
33319     // private
33320     onHide : function(){
33321         Roo.tree.TreeEditor.superclass.onHide.call(this);
33322         if(this.editNode){
33323             this.editNode.ui.focus();
33324         }
33325     },
33326
33327     // private
33328     onSpecialKey : function(field, e){
33329         var k = e.getKey();
33330         if(k == e.ESC){
33331             e.stopEvent();
33332             this.cancelEdit();
33333         }else if(k == e.ENTER && !e.hasModifier()){
33334             e.stopEvent();
33335             this.completeEdit();
33336         }
33337     }
33338 });//<Script type="text/javascript">
33339 /*
33340  * Based on:
33341  * Ext JS Library 1.1.1
33342  * Copyright(c) 2006-2007, Ext JS, LLC.
33343  *
33344  * Originally Released Under LGPL - original licence link has changed is not relivant.
33345  *
33346  * Fork - LGPL
33347  * <script type="text/javascript">
33348  */
33349  
33350 /**
33351  * Not documented??? - probably should be...
33352  */
33353
33354 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33355     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33356     
33357     renderElements : function(n, a, targetNode, bulkRender){
33358         //consel.log("renderElements?");
33359         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33360
33361         var t = n.getOwnerTree();
33362         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33363         
33364         var cols = t.columns;
33365         var bw = t.borderWidth;
33366         var c = cols[0];
33367         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33368          var cb = typeof a.checked == "boolean";
33369         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33370         var colcls = 'x-t-' + tid + '-c0';
33371         var buf = [
33372             '<li class="x-tree-node">',
33373             
33374                 
33375                 '<div class="x-tree-node-el ', a.cls,'">',
33376                     // extran...
33377                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33378                 
33379                 
33380                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33381                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33382                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33383                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33384                            (a.iconCls ? ' '+a.iconCls : ''),
33385                            '" unselectable="on" />',
33386                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33387                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33388                              
33389                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33390                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33391                             '<span unselectable="on" qtip="' + tx + '">',
33392                              tx,
33393                              '</span></a>' ,
33394                     '</div>',
33395                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33396                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33397                  ];
33398         for(var i = 1, len = cols.length; i < len; i++){
33399             c = cols[i];
33400             colcls = 'x-t-' + tid + '-c' +i;
33401             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33402             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33403                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33404                       "</div>");
33405          }
33406          
33407          buf.push(
33408             '</a>',
33409             '<div class="x-clear"></div></div>',
33410             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33411             "</li>");
33412         
33413         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33414             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33415                                 n.nextSibling.ui.getEl(), buf.join(""));
33416         }else{
33417             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33418         }
33419         var el = this.wrap.firstChild;
33420         this.elRow = el;
33421         this.elNode = el.firstChild;
33422         this.ranchor = el.childNodes[1];
33423         this.ctNode = this.wrap.childNodes[1];
33424         var cs = el.firstChild.childNodes;
33425         this.indentNode = cs[0];
33426         this.ecNode = cs[1];
33427         this.iconNode = cs[2];
33428         var index = 3;
33429         if(cb){
33430             this.checkbox = cs[3];
33431             index++;
33432         }
33433         this.anchor = cs[index];
33434         
33435         this.textNode = cs[index].firstChild;
33436         
33437         //el.on("click", this.onClick, this);
33438         //el.on("dblclick", this.onDblClick, this);
33439         
33440         
33441        // console.log(this);
33442     },
33443     initEvents : function(){
33444         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33445         
33446             
33447         var a = this.ranchor;
33448
33449         var el = Roo.get(a);
33450
33451         if(Roo.isOpera){ // opera render bug ignores the CSS
33452             el.setStyle("text-decoration", "none");
33453         }
33454
33455         el.on("click", this.onClick, this);
33456         el.on("dblclick", this.onDblClick, this);
33457         el.on("contextmenu", this.onContextMenu, this);
33458         
33459     },
33460     
33461     /*onSelectedChange : function(state){
33462         if(state){
33463             this.focus();
33464             this.addClass("x-tree-selected");
33465         }else{
33466             //this.blur();
33467             this.removeClass("x-tree-selected");
33468         }
33469     },*/
33470     addClass : function(cls){
33471         if(this.elRow){
33472             Roo.fly(this.elRow).addClass(cls);
33473         }
33474         
33475     },
33476     
33477     
33478     removeClass : function(cls){
33479         if(this.elRow){
33480             Roo.fly(this.elRow).removeClass(cls);
33481         }
33482     }
33483
33484     
33485     
33486 });//<Script type="text/javascript">
33487
33488 /*
33489  * Based on:
33490  * Ext JS Library 1.1.1
33491  * Copyright(c) 2006-2007, Ext JS, LLC.
33492  *
33493  * Originally Released Under LGPL - original licence link has changed is not relivant.
33494  *
33495  * Fork - LGPL
33496  * <script type="text/javascript">
33497  */
33498  
33499
33500 /**
33501  * @class Roo.tree.ColumnTree
33502  * @extends Roo.data.TreePanel
33503  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33504  * @cfg {int} borderWidth  compined right/left border allowance
33505  * @constructor
33506  * @param {String/HTMLElement/Element} el The container element
33507  * @param {Object} config
33508  */
33509 Roo.tree.ColumnTree =  function(el, config)
33510 {
33511    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33512    this.addEvents({
33513         /**
33514         * @event resize
33515         * Fire this event on a container when it resizes
33516         * @param {int} w Width
33517         * @param {int} h Height
33518         */
33519        "resize" : true
33520     });
33521     this.on('resize', this.onResize, this);
33522 };
33523
33524 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33525     //lines:false,
33526     
33527     
33528     borderWidth: Roo.isBorderBox ? 0 : 2, 
33529     headEls : false,
33530     
33531     render : function(){
33532         // add the header.....
33533        
33534         Roo.tree.ColumnTree.superclass.render.apply(this);
33535         
33536         this.el.addClass('x-column-tree');
33537         
33538         this.headers = this.el.createChild(
33539             {cls:'x-tree-headers'},this.innerCt.dom);
33540    
33541         var cols = this.columns, c;
33542         var totalWidth = 0;
33543         this.headEls = [];
33544         var  len = cols.length;
33545         for(var i = 0; i < len; i++){
33546              c = cols[i];
33547              totalWidth += c.width;
33548             this.headEls.push(this.headers.createChild({
33549                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33550                  cn: {
33551                      cls:'x-tree-hd-text',
33552                      html: c.header
33553                  },
33554                  style:'width:'+(c.width-this.borderWidth)+'px;'
33555              }));
33556         }
33557         this.headers.createChild({cls:'x-clear'});
33558         // prevent floats from wrapping when clipped
33559         this.headers.setWidth(totalWidth);
33560         //this.innerCt.setWidth(totalWidth);
33561         this.innerCt.setStyle({ overflow: 'auto' });
33562         this.onResize(this.width, this.height);
33563              
33564         
33565     },
33566     onResize : function(w,h)
33567     {
33568         this.height = h;
33569         this.width = w;
33570         // resize cols..
33571         this.innerCt.setWidth(this.width);
33572         this.innerCt.setHeight(this.height-20);
33573         
33574         // headers...
33575         var cols = this.columns, c;
33576         var totalWidth = 0;
33577         var expEl = false;
33578         var len = cols.length;
33579         for(var i = 0; i < len; i++){
33580             c = cols[i];
33581             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33582                 // it's the expander..
33583                 expEl  = this.headEls[i];
33584                 continue;
33585             }
33586             totalWidth += c.width;
33587             
33588         }
33589         if (expEl) {
33590             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33591         }
33592         this.headers.setWidth(w-20);
33593
33594         
33595         
33596         
33597     }
33598 });
33599 /*
33600  * Based on:
33601  * Ext JS Library 1.1.1
33602  * Copyright(c) 2006-2007, Ext JS, LLC.
33603  *
33604  * Originally Released Under LGPL - original licence link has changed is not relivant.
33605  *
33606  * Fork - LGPL
33607  * <script type="text/javascript">
33608  */
33609  
33610 /**
33611  * @class Roo.menu.Menu
33612  * @extends Roo.util.Observable
33613  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33614  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33615  * @constructor
33616  * Creates a new Menu
33617  * @param {Object} config Configuration options
33618  */
33619 Roo.menu.Menu = function(config){
33620     Roo.apply(this, config);
33621     this.id = this.id || Roo.id();
33622     this.addEvents({
33623         /**
33624          * @event beforeshow
33625          * Fires before this menu is displayed
33626          * @param {Roo.menu.Menu} this
33627          */
33628         beforeshow : true,
33629         /**
33630          * @event beforehide
33631          * Fires before this menu is hidden
33632          * @param {Roo.menu.Menu} this
33633          */
33634         beforehide : true,
33635         /**
33636          * @event show
33637          * Fires after this menu is displayed
33638          * @param {Roo.menu.Menu} this
33639          */
33640         show : true,
33641         /**
33642          * @event hide
33643          * Fires after this menu is hidden
33644          * @param {Roo.menu.Menu} this
33645          */
33646         hide : true,
33647         /**
33648          * @event click
33649          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33650          * @param {Roo.menu.Menu} this
33651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33652          * @param {Roo.EventObject} e
33653          */
33654         click : true,
33655         /**
33656          * @event mouseover
33657          * Fires when the mouse is hovering over this menu
33658          * @param {Roo.menu.Menu} this
33659          * @param {Roo.EventObject} e
33660          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33661          */
33662         mouseover : true,
33663         /**
33664          * @event mouseout
33665          * Fires when the mouse exits this menu
33666          * @param {Roo.menu.Menu} this
33667          * @param {Roo.EventObject} e
33668          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33669          */
33670         mouseout : true,
33671         /**
33672          * @event itemclick
33673          * Fires when a menu item contained in this menu is clicked
33674          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33675          * @param {Roo.EventObject} e
33676          */
33677         itemclick: true
33678     });
33679     if (this.registerMenu) {
33680         Roo.menu.MenuMgr.register(this);
33681     }
33682     
33683     var mis = this.items;
33684     this.items = new Roo.util.MixedCollection();
33685     if(mis){
33686         this.add.apply(this, mis);
33687     }
33688 };
33689
33690 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33691     /**
33692      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33693      */
33694     minWidth : 120,
33695     /**
33696      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33697      * for bottom-right shadow (defaults to "sides")
33698      */
33699     shadow : "sides",
33700     /**
33701      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33702      * this menu (defaults to "tl-tr?")
33703      */
33704     subMenuAlign : "tl-tr?",
33705     /**
33706      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33707      * relative to its element of origin (defaults to "tl-bl?")
33708      */
33709     defaultAlign : "tl-bl?",
33710     /**
33711      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33712      */
33713     allowOtherMenus : false,
33714     /**
33715      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33716      */
33717     registerMenu : true,
33718
33719     hidden:true,
33720
33721     // private
33722     render : function(){
33723         if(this.el){
33724             return;
33725         }
33726         var el = this.el = new Roo.Layer({
33727             cls: "x-menu",
33728             shadow:this.shadow,
33729             constrain: false,
33730             parentEl: this.parentEl || document.body,
33731             zindex:15000
33732         });
33733
33734         this.keyNav = new Roo.menu.MenuNav(this);
33735
33736         if(this.plain){
33737             el.addClass("x-menu-plain");
33738         }
33739         if(this.cls){
33740             el.addClass(this.cls);
33741         }
33742         // generic focus element
33743         this.focusEl = el.createChild({
33744             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33745         });
33746         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33747         ul.on("click", this.onClick, this);
33748         ul.on("mouseover", this.onMouseOver, this);
33749         ul.on("mouseout", this.onMouseOut, this);
33750         this.items.each(function(item){
33751             var li = document.createElement("li");
33752             li.className = "x-menu-list-item";
33753             ul.dom.appendChild(li);
33754             item.render(li, this);
33755         }, this);
33756         this.ul = ul;
33757         this.autoWidth();
33758     },
33759
33760     // private
33761     autoWidth : function(){
33762         var el = this.el, ul = this.ul;
33763         if(!el){
33764             return;
33765         }
33766         var w = this.width;
33767         if(w){
33768             el.setWidth(w);
33769         }else if(Roo.isIE){
33770             el.setWidth(this.minWidth);
33771             var t = el.dom.offsetWidth; // force recalc
33772             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33773         }
33774     },
33775
33776     // private
33777     delayAutoWidth : function(){
33778         if(this.rendered){
33779             if(!this.awTask){
33780                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33781             }
33782             this.awTask.delay(20);
33783         }
33784     },
33785
33786     // private
33787     findTargetItem : function(e){
33788         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33789         if(t && t.menuItemId){
33790             return this.items.get(t.menuItemId);
33791         }
33792     },
33793
33794     // private
33795     onClick : function(e){
33796         var t;
33797         if(t = this.findTargetItem(e)){
33798             t.onClick(e);
33799             this.fireEvent("click", this, t, e);
33800         }
33801     },
33802
33803     // private
33804     setActiveItem : function(item, autoExpand){
33805         if(item != this.activeItem){
33806             if(this.activeItem){
33807                 this.activeItem.deactivate();
33808             }
33809             this.activeItem = item;
33810             item.activate(autoExpand);
33811         }else if(autoExpand){
33812             item.expandMenu();
33813         }
33814     },
33815
33816     // private
33817     tryActivate : function(start, step){
33818         var items = this.items;
33819         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33820             var item = items.get(i);
33821             if(!item.disabled && item.canActivate){
33822                 this.setActiveItem(item, false);
33823                 return item;
33824             }
33825         }
33826         return false;
33827     },
33828
33829     // private
33830     onMouseOver : function(e){
33831         var t;
33832         if(t = this.findTargetItem(e)){
33833             if(t.canActivate && !t.disabled){
33834                 this.setActiveItem(t, true);
33835             }
33836         }
33837         this.fireEvent("mouseover", this, e, t);
33838     },
33839
33840     // private
33841     onMouseOut : function(e){
33842         var t;
33843         if(t = this.findTargetItem(e)){
33844             if(t == this.activeItem && t.shouldDeactivate(e)){
33845                 this.activeItem.deactivate();
33846                 delete this.activeItem;
33847             }
33848         }
33849         this.fireEvent("mouseout", this, e, t);
33850     },
33851
33852     /**
33853      * Read-only.  Returns true if the menu is currently displayed, else false.
33854      * @type Boolean
33855      */
33856     isVisible : function(){
33857         return this.el && !this.hidden;
33858     },
33859
33860     /**
33861      * Displays this menu relative to another element
33862      * @param {String/HTMLElement/Roo.Element} element The element to align to
33863      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33864      * the element (defaults to this.defaultAlign)
33865      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33866      */
33867     show : function(el, pos, parentMenu){
33868         this.parentMenu = parentMenu;
33869         if(!this.el){
33870             this.render();
33871         }
33872         this.fireEvent("beforeshow", this);
33873         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33874     },
33875
33876     /**
33877      * Displays this menu at a specific xy position
33878      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33879      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33880      */
33881     showAt : function(xy, parentMenu, /* private: */_e){
33882         this.parentMenu = parentMenu;
33883         if(!this.el){
33884             this.render();
33885         }
33886         if(_e !== false){
33887             this.fireEvent("beforeshow", this);
33888             xy = this.el.adjustForConstraints(xy);
33889         }
33890         this.el.setXY(xy);
33891         this.el.show();
33892         this.hidden = false;
33893         this.focus();
33894         this.fireEvent("show", this);
33895     },
33896
33897     focus : function(){
33898         if(!this.hidden){
33899             this.doFocus.defer(50, this);
33900         }
33901     },
33902
33903     doFocus : function(){
33904         if(!this.hidden){
33905             this.focusEl.focus();
33906         }
33907     },
33908
33909     /**
33910      * Hides this menu and optionally all parent menus
33911      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33912      */
33913     hide : function(deep){
33914         if(this.el && this.isVisible()){
33915             this.fireEvent("beforehide", this);
33916             if(this.activeItem){
33917                 this.activeItem.deactivate();
33918                 this.activeItem = null;
33919             }
33920             this.el.hide();
33921             this.hidden = true;
33922             this.fireEvent("hide", this);
33923         }
33924         if(deep === true && this.parentMenu){
33925             this.parentMenu.hide(true);
33926         }
33927     },
33928
33929     /**
33930      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33931      * Any of the following are valid:
33932      * <ul>
33933      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33934      * <li>An HTMLElement object which will be converted to a menu item</li>
33935      * <li>A menu item config object that will be created as a new menu item</li>
33936      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33937      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33938      * </ul>
33939      * Usage:
33940      * <pre><code>
33941 // Create the menu
33942 var menu = new Roo.menu.Menu();
33943
33944 // Create a menu item to add by reference
33945 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33946
33947 // Add a bunch of items at once using different methods.
33948 // Only the last item added will be returned.
33949 var item = menu.add(
33950     menuItem,                // add existing item by ref
33951     'Dynamic Item',          // new TextItem
33952     '-',                     // new separator
33953     { text: 'Config Item' }  // new item by config
33954 );
33955 </code></pre>
33956      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33957      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33958      */
33959     add : function(){
33960         var a = arguments, l = a.length, item;
33961         for(var i = 0; i < l; i++){
33962             var el = a[i];
33963             if ((typeof(el) == "object") && el.xtype && el.xns) {
33964                 el = Roo.factory(el, Roo.menu);
33965             }
33966             
33967             if(el.render){ // some kind of Item
33968                 item = this.addItem(el);
33969             }else if(typeof el == "string"){ // string
33970                 if(el == "separator" || el == "-"){
33971                     item = this.addSeparator();
33972                 }else{
33973                     item = this.addText(el);
33974                 }
33975             }else if(el.tagName || el.el){ // element
33976                 item = this.addElement(el);
33977             }else if(typeof el == "object"){ // must be menu item config?
33978                 item = this.addMenuItem(el);
33979             }
33980         }
33981         return item;
33982     },
33983
33984     /**
33985      * Returns this menu's underlying {@link Roo.Element} object
33986      * @return {Roo.Element} The element
33987      */
33988     getEl : function(){
33989         if(!this.el){
33990             this.render();
33991         }
33992         return this.el;
33993     },
33994
33995     /**
33996      * Adds a separator bar to the menu
33997      * @return {Roo.menu.Item} The menu item that was added
33998      */
33999     addSeparator : function(){
34000         return this.addItem(new Roo.menu.Separator());
34001     },
34002
34003     /**
34004      * Adds an {@link Roo.Element} object to the menu
34005      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34006      * @return {Roo.menu.Item} The menu item that was added
34007      */
34008     addElement : function(el){
34009         return this.addItem(new Roo.menu.BaseItem(el));
34010     },
34011
34012     /**
34013      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34014      * @param {Roo.menu.Item} item The menu item to add
34015      * @return {Roo.menu.Item} The menu item that was added
34016      */
34017     addItem : function(item){
34018         this.items.add(item);
34019         if(this.ul){
34020             var li = document.createElement("li");
34021             li.className = "x-menu-list-item";
34022             this.ul.dom.appendChild(li);
34023             item.render(li, this);
34024             this.delayAutoWidth();
34025         }
34026         return item;
34027     },
34028
34029     /**
34030      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34031      * @param {Object} config A MenuItem config object
34032      * @return {Roo.menu.Item} The menu item that was added
34033      */
34034     addMenuItem : function(config){
34035         if(!(config instanceof Roo.menu.Item)){
34036             if(typeof config.checked == "boolean"){ // must be check menu item config?
34037                 config = new Roo.menu.CheckItem(config);
34038             }else{
34039                 config = new Roo.menu.Item(config);
34040             }
34041         }
34042         return this.addItem(config);
34043     },
34044
34045     /**
34046      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34047      * @param {String} text The text to display in the menu item
34048      * @return {Roo.menu.Item} The menu item that was added
34049      */
34050     addText : function(text){
34051         return this.addItem(new Roo.menu.TextItem({ text : text }));
34052     },
34053
34054     /**
34055      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34056      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34057      * @param {Roo.menu.Item} item The menu item to add
34058      * @return {Roo.menu.Item} The menu item that was added
34059      */
34060     insert : function(index, item){
34061         this.items.insert(index, item);
34062         if(this.ul){
34063             var li = document.createElement("li");
34064             li.className = "x-menu-list-item";
34065             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34066             item.render(li, this);
34067             this.delayAutoWidth();
34068         }
34069         return item;
34070     },
34071
34072     /**
34073      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34074      * @param {Roo.menu.Item} item The menu item to remove
34075      */
34076     remove : function(item){
34077         this.items.removeKey(item.id);
34078         item.destroy();
34079     },
34080
34081     /**
34082      * Removes and destroys all items in the menu
34083      */
34084     removeAll : function(){
34085         var f;
34086         while(f = this.items.first()){
34087             this.remove(f);
34088         }
34089     }
34090 });
34091
34092 // MenuNav is a private utility class used internally by the Menu
34093 Roo.menu.MenuNav = function(menu){
34094     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34095     this.scope = this.menu = menu;
34096 };
34097
34098 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34099     doRelay : function(e, h){
34100         var k = e.getKey();
34101         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34102             this.menu.tryActivate(0, 1);
34103             return false;
34104         }
34105         return h.call(this.scope || this, e, this.menu);
34106     },
34107
34108     up : function(e, m){
34109         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34110             m.tryActivate(m.items.length-1, -1);
34111         }
34112     },
34113
34114     down : function(e, m){
34115         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34116             m.tryActivate(0, 1);
34117         }
34118     },
34119
34120     right : function(e, m){
34121         if(m.activeItem){
34122             m.activeItem.expandMenu(true);
34123         }
34124     },
34125
34126     left : function(e, m){
34127         m.hide();
34128         if(m.parentMenu && m.parentMenu.activeItem){
34129             m.parentMenu.activeItem.activate();
34130         }
34131     },
34132
34133     enter : function(e, m){
34134         if(m.activeItem){
34135             e.stopPropagation();
34136             m.activeItem.onClick(e);
34137             m.fireEvent("click", this, m.activeItem);
34138             return true;
34139         }
34140     }
34141 });/*
34142  * Based on:
34143  * Ext JS Library 1.1.1
34144  * Copyright(c) 2006-2007, Ext JS, LLC.
34145  *
34146  * Originally Released Under LGPL - original licence link has changed is not relivant.
34147  *
34148  * Fork - LGPL
34149  * <script type="text/javascript">
34150  */
34151  
34152 /**
34153  * @class Roo.menu.MenuMgr
34154  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34155  * @singleton
34156  */
34157 Roo.menu.MenuMgr = function(){
34158    var menus, active, groups = {}, attached = false, lastShow = new Date();
34159
34160    // private - called when first menu is created
34161    function init(){
34162        menus = {};
34163        active = new Roo.util.MixedCollection();
34164        Roo.get(document).addKeyListener(27, function(){
34165            if(active.length > 0){
34166                hideAll();
34167            }
34168        });
34169    }
34170
34171    // private
34172    function hideAll(){
34173        if(active && active.length > 0){
34174            var c = active.clone();
34175            c.each(function(m){
34176                m.hide();
34177            });
34178        }
34179    }
34180
34181    // private
34182    function onHide(m){
34183        active.remove(m);
34184        if(active.length < 1){
34185            Roo.get(document).un("mousedown", onMouseDown);
34186            attached = false;
34187        }
34188    }
34189
34190    // private
34191    function onShow(m){
34192        var last = active.last();
34193        lastShow = new Date();
34194        active.add(m);
34195        if(!attached){
34196            Roo.get(document).on("mousedown", onMouseDown);
34197            attached = true;
34198        }
34199        if(m.parentMenu){
34200           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34201           m.parentMenu.activeChild = m;
34202        }else if(last && last.isVisible()){
34203           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34204        }
34205    }
34206
34207    // private
34208    function onBeforeHide(m){
34209        if(m.activeChild){
34210            m.activeChild.hide();
34211        }
34212        if(m.autoHideTimer){
34213            clearTimeout(m.autoHideTimer);
34214            delete m.autoHideTimer;
34215        }
34216    }
34217
34218    // private
34219    function onBeforeShow(m){
34220        var pm = m.parentMenu;
34221        if(!pm && !m.allowOtherMenus){
34222            hideAll();
34223        }else if(pm && pm.activeChild && active != m){
34224            pm.activeChild.hide();
34225        }
34226    }
34227
34228    // private
34229    function onMouseDown(e){
34230        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34231            hideAll();
34232        }
34233    }
34234
34235    // private
34236    function onBeforeCheck(mi, state){
34237        if(state){
34238            var g = groups[mi.group];
34239            for(var i = 0, l = g.length; i < l; i++){
34240                if(g[i] != mi){
34241                    g[i].setChecked(false);
34242                }
34243            }
34244        }
34245    }
34246
34247    return {
34248
34249        /**
34250         * Hides all menus that are currently visible
34251         */
34252        hideAll : function(){
34253             hideAll();  
34254        },
34255
34256        // private
34257        register : function(menu){
34258            if(!menus){
34259                init();
34260            }
34261            menus[menu.id] = menu;
34262            menu.on("beforehide", onBeforeHide);
34263            menu.on("hide", onHide);
34264            menu.on("beforeshow", onBeforeShow);
34265            menu.on("show", onShow);
34266            var g = menu.group;
34267            if(g && menu.events["checkchange"]){
34268                if(!groups[g]){
34269                    groups[g] = [];
34270                }
34271                groups[g].push(menu);
34272                menu.on("checkchange", onCheck);
34273            }
34274        },
34275
34276         /**
34277          * Returns a {@link Roo.menu.Menu} object
34278          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34279          * be used to generate and return a new Menu instance.
34280          */
34281        get : function(menu){
34282            if(typeof menu == "string"){ // menu id
34283                return menus[menu];
34284            }else if(menu.events){  // menu instance
34285                return menu;
34286            }else if(typeof menu.length == 'number'){ // array of menu items?
34287                return new Roo.menu.Menu({items:menu});
34288            }else{ // otherwise, must be a config
34289                return new Roo.menu.Menu(menu);
34290            }
34291        },
34292
34293        // private
34294        unregister : function(menu){
34295            delete menus[menu.id];
34296            menu.un("beforehide", onBeforeHide);
34297            menu.un("hide", onHide);
34298            menu.un("beforeshow", onBeforeShow);
34299            menu.un("show", onShow);
34300            var g = menu.group;
34301            if(g && menu.events["checkchange"]){
34302                groups[g].remove(menu);
34303                menu.un("checkchange", onCheck);
34304            }
34305        },
34306
34307        // private
34308        registerCheckable : function(menuItem){
34309            var g = menuItem.group;
34310            if(g){
34311                if(!groups[g]){
34312                    groups[g] = [];
34313                }
34314                groups[g].push(menuItem);
34315                menuItem.on("beforecheckchange", onBeforeCheck);
34316            }
34317        },
34318
34319        // private
34320        unregisterCheckable : function(menuItem){
34321            var g = menuItem.group;
34322            if(g){
34323                groups[g].remove(menuItem);
34324                menuItem.un("beforecheckchange", onBeforeCheck);
34325            }
34326        }
34327    };
34328 }();/*
34329  * Based on:
34330  * Ext JS Library 1.1.1
34331  * Copyright(c) 2006-2007, Ext JS, LLC.
34332  *
34333  * Originally Released Under LGPL - original licence link has changed is not relivant.
34334  *
34335  * Fork - LGPL
34336  * <script type="text/javascript">
34337  */
34338  
34339
34340 /**
34341  * @class Roo.menu.BaseItem
34342  * @extends Roo.Component
34343  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34344  * management and base configuration options shared by all menu components.
34345  * @constructor
34346  * Creates a new BaseItem
34347  * @param {Object} config Configuration options
34348  */
34349 Roo.menu.BaseItem = function(config){
34350     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34351
34352     this.addEvents({
34353         /**
34354          * @event click
34355          * Fires when this item is clicked
34356          * @param {Roo.menu.BaseItem} this
34357          * @param {Roo.EventObject} e
34358          */
34359         click: true,
34360         /**
34361          * @event activate
34362          * Fires when this item is activated
34363          * @param {Roo.menu.BaseItem} this
34364          */
34365         activate : true,
34366         /**
34367          * @event deactivate
34368          * Fires when this item is deactivated
34369          * @param {Roo.menu.BaseItem} this
34370          */
34371         deactivate : true
34372     });
34373
34374     if(this.handler){
34375         this.on("click", this.handler, this.scope, true);
34376     }
34377 };
34378
34379 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34380     /**
34381      * @cfg {Function} handler
34382      * A function that will handle the click event of this menu item (defaults to undefined)
34383      */
34384     /**
34385      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34386      */
34387     canActivate : false,
34388     /**
34389      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34390      */
34391     activeClass : "x-menu-item-active",
34392     /**
34393      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34394      */
34395     hideOnClick : true,
34396     /**
34397      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34398      */
34399     hideDelay : 100,
34400
34401     // private
34402     ctype: "Roo.menu.BaseItem",
34403
34404     // private
34405     actionMode : "container",
34406
34407     // private
34408     render : function(container, parentMenu){
34409         this.parentMenu = parentMenu;
34410         Roo.menu.BaseItem.superclass.render.call(this, container);
34411         this.container.menuItemId = this.id;
34412     },
34413
34414     // private
34415     onRender : function(container, position){
34416         this.el = Roo.get(this.el);
34417         container.dom.appendChild(this.el.dom);
34418     },
34419
34420     // private
34421     onClick : function(e){
34422         if(!this.disabled && this.fireEvent("click", this, e) !== false
34423                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34424             this.handleClick(e);
34425         }else{
34426             e.stopEvent();
34427         }
34428     },
34429
34430     // private
34431     activate : function(){
34432         if(this.disabled){
34433             return false;
34434         }
34435         var li = this.container;
34436         li.addClass(this.activeClass);
34437         this.region = li.getRegion().adjust(2, 2, -2, -2);
34438         this.fireEvent("activate", this);
34439         return true;
34440     },
34441
34442     // private
34443     deactivate : function(){
34444         this.container.removeClass(this.activeClass);
34445         this.fireEvent("deactivate", this);
34446     },
34447
34448     // private
34449     shouldDeactivate : function(e){
34450         return !this.region || !this.region.contains(e.getPoint());
34451     },
34452
34453     // private
34454     handleClick : function(e){
34455         if(this.hideOnClick){
34456             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34457         }
34458     },
34459
34460     // private
34461     expandMenu : function(autoActivate){
34462         // do nothing
34463     },
34464
34465     // private
34466     hideMenu : function(){
34467         // do nothing
34468     }
34469 });/*
34470  * Based on:
34471  * Ext JS Library 1.1.1
34472  * Copyright(c) 2006-2007, Ext JS, LLC.
34473  *
34474  * Originally Released Under LGPL - original licence link has changed is not relivant.
34475  *
34476  * Fork - LGPL
34477  * <script type="text/javascript">
34478  */
34479  
34480 /**
34481  * @class Roo.menu.Adapter
34482  * @extends Roo.menu.BaseItem
34483  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
34484  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34485  * @constructor
34486  * Creates a new Adapter
34487  * @param {Object} config Configuration options
34488  */
34489 Roo.menu.Adapter = function(component, config){
34490     Roo.menu.Adapter.superclass.constructor.call(this, config);
34491     this.component = component;
34492 };
34493 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34494     // private
34495     canActivate : true,
34496
34497     // private
34498     onRender : function(container, position){
34499         this.component.render(container);
34500         this.el = this.component.getEl();
34501     },
34502
34503     // private
34504     activate : function(){
34505         if(this.disabled){
34506             return false;
34507         }
34508         this.component.focus();
34509         this.fireEvent("activate", this);
34510         return true;
34511     },
34512
34513     // private
34514     deactivate : function(){
34515         this.fireEvent("deactivate", this);
34516     },
34517
34518     // private
34519     disable : function(){
34520         this.component.disable();
34521         Roo.menu.Adapter.superclass.disable.call(this);
34522     },
34523
34524     // private
34525     enable : function(){
34526         this.component.enable();
34527         Roo.menu.Adapter.superclass.enable.call(this);
34528     }
34529 });/*
34530  * Based on:
34531  * Ext JS Library 1.1.1
34532  * Copyright(c) 2006-2007, Ext JS, LLC.
34533  *
34534  * Originally Released Under LGPL - original licence link has changed is not relivant.
34535  *
34536  * Fork - LGPL
34537  * <script type="text/javascript">
34538  */
34539
34540 /**
34541  * @class Roo.menu.TextItem
34542  * @extends Roo.menu.BaseItem
34543  * Adds a static text string to a menu, usually used as either a heading or group separator.
34544  * Note: old style constructor with text is still supported.
34545  * 
34546  * @constructor
34547  * Creates a new TextItem
34548  * @param {Object} cfg Configuration
34549  */
34550 Roo.menu.TextItem = function(cfg){
34551     if (typeof(cfg) == 'string') {
34552         this.text = cfg;
34553     } else {
34554         Roo.apply(this,cfg);
34555     }
34556     
34557     Roo.menu.TextItem.superclass.constructor.call(this);
34558 };
34559
34560 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34561     /**
34562      * @cfg {Boolean} text Text to show on item.
34563      */
34564     text : '',
34565     
34566     /**
34567      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34568      */
34569     hideOnClick : false,
34570     /**
34571      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34572      */
34573     itemCls : "x-menu-text",
34574
34575     // private
34576     onRender : function(){
34577         var s = document.createElement("span");
34578         s.className = this.itemCls;
34579         s.innerHTML = this.text;
34580         this.el = s;
34581         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34582     }
34583 });/*
34584  * Based on:
34585  * Ext JS Library 1.1.1
34586  * Copyright(c) 2006-2007, Ext JS, LLC.
34587  *
34588  * Originally Released Under LGPL - original licence link has changed is not relivant.
34589  *
34590  * Fork - LGPL
34591  * <script type="text/javascript">
34592  */
34593
34594 /**
34595  * @class Roo.menu.Separator
34596  * @extends Roo.menu.BaseItem
34597  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34598  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34599  * @constructor
34600  * @param {Object} config Configuration options
34601  */
34602 Roo.menu.Separator = function(config){
34603     Roo.menu.Separator.superclass.constructor.call(this, config);
34604 };
34605
34606 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34607     /**
34608      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34609      */
34610     itemCls : "x-menu-sep",
34611     /**
34612      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34613      */
34614     hideOnClick : false,
34615
34616     // private
34617     onRender : function(li){
34618         var s = document.createElement("span");
34619         s.className = this.itemCls;
34620         s.innerHTML = "&#160;";
34621         this.el = s;
34622         li.addClass("x-menu-sep-li");
34623         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34624     }
34625 });/*
34626  * Based on:
34627  * Ext JS Library 1.1.1
34628  * Copyright(c) 2006-2007, Ext JS, LLC.
34629  *
34630  * Originally Released Under LGPL - original licence link has changed is not relivant.
34631  *
34632  * Fork - LGPL
34633  * <script type="text/javascript">
34634  */
34635 /**
34636  * @class Roo.menu.Item
34637  * @extends Roo.menu.BaseItem
34638  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34639  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34640  * activation and click handling.
34641  * @constructor
34642  * Creates a new Item
34643  * @param {Object} config Configuration options
34644  */
34645 Roo.menu.Item = function(config){
34646     Roo.menu.Item.superclass.constructor.call(this, config);
34647     if(this.menu){
34648         this.menu = Roo.menu.MenuMgr.get(this.menu);
34649     }
34650 };
34651 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34652     
34653     /**
34654      * @cfg {String} text
34655      * The text to show on the menu item.
34656      */
34657     text: '',
34658      /**
34659      * @cfg {String} HTML to render in menu
34660      * The text to show on the menu item (HTML version).
34661      */
34662     html: '',
34663     /**
34664      * @cfg {String} icon
34665      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34666      */
34667     icon: undefined,
34668     /**
34669      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34670      */
34671     itemCls : "x-menu-item",
34672     /**
34673      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34674      */
34675     canActivate : true,
34676     /**
34677      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34678      */
34679     showDelay: 200,
34680     // doc'd in BaseItem
34681     hideDelay: 200,
34682
34683     // private
34684     ctype: "Roo.menu.Item",
34685     
34686     // private
34687     onRender : function(container, position){
34688         var el = document.createElement("a");
34689         el.hideFocus = true;
34690         el.unselectable = "on";
34691         el.href = this.href || "#";
34692         if(this.hrefTarget){
34693             el.target = this.hrefTarget;
34694         }
34695         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34696         
34697         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34698         
34699         el.innerHTML = String.format(
34700                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34701                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34702         this.el = el;
34703         Roo.menu.Item.superclass.onRender.call(this, container, position);
34704     },
34705
34706     /**
34707      * Sets the text to display in this menu item
34708      * @param {String} text The text to display
34709      * @param {Boolean} isHTML true to indicate text is pure html.
34710      */
34711     setText : function(text, isHTML){
34712         if (isHTML) {
34713             this.html = text;
34714         } else {
34715             this.text = text;
34716             this.html = '';
34717         }
34718         if(this.rendered){
34719             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34720      
34721             this.el.update(String.format(
34722                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34723                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34724             this.parentMenu.autoWidth();
34725         }
34726     },
34727
34728     // private
34729     handleClick : function(e){
34730         if(!this.href){ // if no link defined, stop the event automatically
34731             e.stopEvent();
34732         }
34733         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34734     },
34735
34736     // private
34737     activate : function(autoExpand){
34738         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34739             this.focus();
34740             if(autoExpand){
34741                 this.expandMenu();
34742             }
34743         }
34744         return true;
34745     },
34746
34747     // private
34748     shouldDeactivate : function(e){
34749         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34750             if(this.menu && this.menu.isVisible()){
34751                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34752             }
34753             return true;
34754         }
34755         return false;
34756     },
34757
34758     // private
34759     deactivate : function(){
34760         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34761         this.hideMenu();
34762     },
34763
34764     // private
34765     expandMenu : function(autoActivate){
34766         if(!this.disabled && this.menu){
34767             clearTimeout(this.hideTimer);
34768             delete this.hideTimer;
34769             if(!this.menu.isVisible() && !this.showTimer){
34770                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34771             }else if (this.menu.isVisible() && autoActivate){
34772                 this.menu.tryActivate(0, 1);
34773             }
34774         }
34775     },
34776
34777     // private
34778     deferExpand : function(autoActivate){
34779         delete this.showTimer;
34780         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34781         if(autoActivate){
34782             this.menu.tryActivate(0, 1);
34783         }
34784     },
34785
34786     // private
34787     hideMenu : function(){
34788         clearTimeout(this.showTimer);
34789         delete this.showTimer;
34790         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34791             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34792         }
34793     },
34794
34795     // private
34796     deferHide : function(){
34797         delete this.hideTimer;
34798         this.menu.hide();
34799     }
34800 });/*
34801  * Based on:
34802  * Ext JS Library 1.1.1
34803  * Copyright(c) 2006-2007, Ext JS, LLC.
34804  *
34805  * Originally Released Under LGPL - original licence link has changed is not relivant.
34806  *
34807  * Fork - LGPL
34808  * <script type="text/javascript">
34809  */
34810  
34811 /**
34812  * @class Roo.menu.CheckItem
34813  * @extends Roo.menu.Item
34814  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34815  * @constructor
34816  * Creates a new CheckItem
34817  * @param {Object} config Configuration options
34818  */
34819 Roo.menu.CheckItem = function(config){
34820     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34821     this.addEvents({
34822         /**
34823          * @event beforecheckchange
34824          * Fires before the checked value is set, providing an opportunity to cancel if needed
34825          * @param {Roo.menu.CheckItem} this
34826          * @param {Boolean} checked The new checked value that will be set
34827          */
34828         "beforecheckchange" : true,
34829         /**
34830          * @event checkchange
34831          * Fires after the checked value has been set
34832          * @param {Roo.menu.CheckItem} this
34833          * @param {Boolean} checked The checked value that was set
34834          */
34835         "checkchange" : true
34836     });
34837     if(this.checkHandler){
34838         this.on('checkchange', this.checkHandler, this.scope);
34839     }
34840 };
34841 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34842     /**
34843      * @cfg {String} group
34844      * All check items with the same group name will automatically be grouped into a single-select
34845      * radio button group (defaults to '')
34846      */
34847     /**
34848      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34849      */
34850     itemCls : "x-menu-item x-menu-check-item",
34851     /**
34852      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34853      */
34854     groupClass : "x-menu-group-item",
34855
34856     /**
34857      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34858      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34859      * initialized with checked = true will be rendered as checked.
34860      */
34861     checked: false,
34862
34863     // private
34864     ctype: "Roo.menu.CheckItem",
34865
34866     // private
34867     onRender : function(c){
34868         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34869         if(this.group){
34870             this.el.addClass(this.groupClass);
34871         }
34872         Roo.menu.MenuMgr.registerCheckable(this);
34873         if(this.checked){
34874             this.checked = false;
34875             this.setChecked(true, true);
34876         }
34877     },
34878
34879     // private
34880     destroy : function(){
34881         if(this.rendered){
34882             Roo.menu.MenuMgr.unregisterCheckable(this);
34883         }
34884         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34885     },
34886
34887     /**
34888      * Set the checked state of this item
34889      * @param {Boolean} checked The new checked value
34890      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34891      */
34892     setChecked : function(state, suppressEvent){
34893         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34894             if(this.container){
34895                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34896             }
34897             this.checked = state;
34898             if(suppressEvent !== true){
34899                 this.fireEvent("checkchange", this, state);
34900             }
34901         }
34902     },
34903
34904     // private
34905     handleClick : function(e){
34906        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34907            this.setChecked(!this.checked);
34908        }
34909        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34910     }
34911 });/*
34912  * Based on:
34913  * Ext JS Library 1.1.1
34914  * Copyright(c) 2006-2007, Ext JS, LLC.
34915  *
34916  * Originally Released Under LGPL - original licence link has changed is not relivant.
34917  *
34918  * Fork - LGPL
34919  * <script type="text/javascript">
34920  */
34921  
34922 /**
34923  * @class Roo.menu.DateItem
34924  * @extends Roo.menu.Adapter
34925  * A menu item that wraps the {@link Roo.DatPicker} component.
34926  * @constructor
34927  * Creates a new DateItem
34928  * @param {Object} config Configuration options
34929  */
34930 Roo.menu.DateItem = function(config){
34931     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34932     /** The Roo.DatePicker object @type Roo.DatePicker */
34933     this.picker = this.component;
34934     this.addEvents({select: true});
34935     
34936     this.picker.on("render", function(picker){
34937         picker.getEl().swallowEvent("click");
34938         picker.container.addClass("x-menu-date-item");
34939     });
34940
34941     this.picker.on("select", this.onSelect, this);
34942 };
34943
34944 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34945     // private
34946     onSelect : function(picker, date){
34947         this.fireEvent("select", this, date, picker);
34948         Roo.menu.DateItem.superclass.handleClick.call(this);
34949     }
34950 });/*
34951  * Based on:
34952  * Ext JS Library 1.1.1
34953  * Copyright(c) 2006-2007, Ext JS, LLC.
34954  *
34955  * Originally Released Under LGPL - original licence link has changed is not relivant.
34956  *
34957  * Fork - LGPL
34958  * <script type="text/javascript">
34959  */
34960  
34961 /**
34962  * @class Roo.menu.ColorItem
34963  * @extends Roo.menu.Adapter
34964  * A menu item that wraps the {@link Roo.ColorPalette} component.
34965  * @constructor
34966  * Creates a new ColorItem
34967  * @param {Object} config Configuration options
34968  */
34969 Roo.menu.ColorItem = function(config){
34970     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34971     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34972     this.palette = this.component;
34973     this.relayEvents(this.palette, ["select"]);
34974     if(this.selectHandler){
34975         this.on('select', this.selectHandler, this.scope);
34976     }
34977 };
34978 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34979  * Based on:
34980  * Ext JS Library 1.1.1
34981  * Copyright(c) 2006-2007, Ext JS, LLC.
34982  *
34983  * Originally Released Under LGPL - original licence link has changed is not relivant.
34984  *
34985  * Fork - LGPL
34986  * <script type="text/javascript">
34987  */
34988  
34989
34990 /**
34991  * @class Roo.menu.DateMenu
34992  * @extends Roo.menu.Menu
34993  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34994  * @constructor
34995  * Creates a new DateMenu
34996  * @param {Object} config Configuration options
34997  */
34998 Roo.menu.DateMenu = function(config){
34999     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35000     this.plain = true;
35001     var di = new Roo.menu.DateItem(config);
35002     this.add(di);
35003     /**
35004      * The {@link Roo.DatePicker} instance for this DateMenu
35005      * @type DatePicker
35006      */
35007     this.picker = di.picker;
35008     /**
35009      * @event select
35010      * @param {DatePicker} picker
35011      * @param {Date} date
35012      */
35013     this.relayEvents(di, ["select"]);
35014
35015     this.on('beforeshow', function(){
35016         if(this.picker){
35017             this.picker.hideMonthPicker(true);
35018         }
35019     }, this);
35020 };
35021 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35022     cls:'x-date-menu'
35023 });/*
35024  * Based on:
35025  * Ext JS Library 1.1.1
35026  * Copyright(c) 2006-2007, Ext JS, LLC.
35027  *
35028  * Originally Released Under LGPL - original licence link has changed is not relivant.
35029  *
35030  * Fork - LGPL
35031  * <script type="text/javascript">
35032  */
35033  
35034
35035 /**
35036  * @class Roo.menu.ColorMenu
35037  * @extends Roo.menu.Menu
35038  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35039  * @constructor
35040  * Creates a new ColorMenu
35041  * @param {Object} config Configuration options
35042  */
35043 Roo.menu.ColorMenu = function(config){
35044     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35045     this.plain = true;
35046     var ci = new Roo.menu.ColorItem(config);
35047     this.add(ci);
35048     /**
35049      * The {@link Roo.ColorPalette} instance for this ColorMenu
35050      * @type ColorPalette
35051      */
35052     this.palette = ci.palette;
35053     /**
35054      * @event select
35055      * @param {ColorPalette} palette
35056      * @param {String} color
35057      */
35058     this.relayEvents(ci, ["select"]);
35059 };
35060 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35061  * Based on:
35062  * Ext JS Library 1.1.1
35063  * Copyright(c) 2006-2007, Ext JS, LLC.
35064  *
35065  * Originally Released Under LGPL - original licence link has changed is not relivant.
35066  *
35067  * Fork - LGPL
35068  * <script type="text/javascript">
35069  */
35070  
35071 /**
35072  * @class Roo.form.Field
35073  * @extends Roo.BoxComponent
35074  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35075  * @constructor
35076  * Creates a new Field
35077  * @param {Object} config Configuration options
35078  */
35079 Roo.form.Field = function(config){
35080     Roo.form.Field.superclass.constructor.call(this, config);
35081 };
35082
35083 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35084     /**
35085      * @cfg {String} fieldLabel Label to use when rendering a form.
35086      */
35087        /**
35088      * @cfg {String} qtip Mouse over tip
35089      */
35090      
35091     /**
35092      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35093      */
35094     invalidClass : "x-form-invalid",
35095     /**
35096      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
35097      */
35098     invalidText : "The value in this field is invalid",
35099     /**
35100      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35101      */
35102     focusClass : "x-form-focus",
35103     /**
35104      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35105       automatic validation (defaults to "keyup").
35106      */
35107     validationEvent : "keyup",
35108     /**
35109      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35110      */
35111     validateOnBlur : true,
35112     /**
35113      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35114      */
35115     validationDelay : 250,
35116     /**
35117      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35118      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35119      */
35120     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35121     /**
35122      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35123      */
35124     fieldClass : "x-form-field",
35125     /**
35126      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35127      *<pre>
35128 Value         Description
35129 -----------   ----------------------------------------------------------------------
35130 qtip          Display a quick tip when the user hovers over the field
35131 title         Display a default browser title attribute popup
35132 under         Add a block div beneath the field containing the error text
35133 side          Add an error icon to the right of the field with a popup on hover
35134 [element id]  Add the error text directly to the innerHTML of the specified element
35135 </pre>
35136      */
35137     msgTarget : 'qtip',
35138     /**
35139      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35140      */
35141     msgFx : 'normal',
35142
35143     /**
35144      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
35145      */
35146     readOnly : false,
35147
35148     /**
35149      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35150      */
35151     disabled : false,
35152
35153     /**
35154      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35155      */
35156     inputType : undefined,
35157     
35158     /**
35159      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
35160          */
35161         tabIndex : undefined,
35162         
35163     // private
35164     isFormField : true,
35165
35166     // private
35167     hasFocus : false,
35168     /**
35169      * @property {Roo.Element} fieldEl
35170      * Element Containing the rendered Field (with label etc.)
35171      */
35172     /**
35173      * @cfg {Mixed} value A value to initialize this field with.
35174      */
35175     value : undefined,
35176
35177     /**
35178      * @cfg {String} name The field's HTML name attribute.
35179      */
35180     /**
35181      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35182      */
35183
35184         // private ??
35185         initComponent : function(){
35186         Roo.form.Field.superclass.initComponent.call(this);
35187         this.addEvents({
35188             /**
35189              * @event focus
35190              * Fires when this field receives input focus.
35191              * @param {Roo.form.Field} this
35192              */
35193             focus : true,
35194             /**
35195              * @event blur
35196              * Fires when this field loses input focus.
35197              * @param {Roo.form.Field} this
35198              */
35199             blur : true,
35200             /**
35201              * @event specialkey
35202              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35203              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35204              * @param {Roo.form.Field} this
35205              * @param {Roo.EventObject} e The event object
35206              */
35207             specialkey : true,
35208             /**
35209              * @event change
35210              * Fires just before the field blurs if the field value has changed.
35211              * @param {Roo.form.Field} this
35212              * @param {Mixed} newValue The new value
35213              * @param {Mixed} oldValue The original value
35214              */
35215             change : true,
35216             /**
35217              * @event invalid
35218              * Fires after the field has been marked as invalid.
35219              * @param {Roo.form.Field} this
35220              * @param {String} msg The validation message
35221              */
35222             invalid : true,
35223             /**
35224              * @event valid
35225              * Fires after the field has been validated with no errors.
35226              * @param {Roo.form.Field} this
35227              */
35228             valid : true
35229         });
35230     },
35231
35232     /**
35233      * Returns the name attribute of the field if available
35234      * @return {String} name The field name
35235      */
35236     getName: function(){
35237          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35238     },
35239
35240     // private
35241     onRender : function(ct, position){
35242         Roo.form.Field.superclass.onRender.call(this, ct, position);
35243         if(!this.el){
35244             var cfg = this.getAutoCreate();
35245             if(!cfg.name){
35246                 cfg.name = this.name || this.id;
35247             }
35248             if(this.inputType){
35249                 cfg.type = this.inputType;
35250             }
35251             this.el = ct.createChild(cfg, position);
35252         }
35253         var type = this.el.dom.type;
35254         if(type){
35255             if(type == 'password'){
35256                 type = 'text';
35257             }
35258             this.el.addClass('x-form-'+type);
35259         }
35260         if(this.readOnly){
35261             this.el.dom.readOnly = true;
35262         }
35263         if(this.tabIndex !== undefined){
35264             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35265         }
35266
35267         this.el.addClass([this.fieldClass, this.cls]);
35268         this.initValue();
35269     },
35270
35271     /**
35272      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35273      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35274      * @return {Roo.form.Field} this
35275      */
35276     applyTo : function(target){
35277         this.allowDomMove = false;
35278         this.el = Roo.get(target);
35279         this.render(this.el.dom.parentNode);
35280         return this;
35281     },
35282
35283     // private
35284     initValue : function(){
35285         if(this.value !== undefined){
35286             this.setValue(this.value);
35287         }else if(this.el.dom.value.length > 0){
35288             this.setValue(this.el.dom.value);
35289         }
35290     },
35291
35292     /**
35293      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35294      */
35295     isDirty : function() {
35296         if(this.disabled) {
35297             return false;
35298         }
35299         return String(this.getValue()) !== String(this.originalValue);
35300     },
35301
35302     // private
35303     afterRender : function(){
35304         Roo.form.Field.superclass.afterRender.call(this);
35305         this.initEvents();
35306     },
35307
35308     // private
35309     fireKey : function(e){
35310         //Roo.log('field ' + e.getKey());
35311         if(e.isNavKeyPress()){
35312             this.fireEvent("specialkey", this, e);
35313         }
35314     },
35315
35316     /**
35317      * Resets the current field value to the originally loaded value and clears any validation messages
35318      */
35319     reset : function(){
35320         this.setValue(this.originalValue);
35321         this.clearInvalid();
35322     },
35323
35324     // private
35325     initEvents : function(){
35326         // safari killled keypress - so keydown is now used..
35327         this.el.on("keydown" , this.fireKey,  this);
35328         this.el.on("focus", this.onFocus,  this);
35329         this.el.on("blur", this.onBlur,  this);
35330
35331         // reference to original value for reset
35332         this.originalValue = this.getValue();
35333     },
35334
35335     // private
35336     onFocus : function(){
35337         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35338             this.el.addClass(this.focusClass);
35339         }
35340         if(!this.hasFocus){
35341             this.hasFocus = true;
35342             this.startValue = this.getValue();
35343             this.fireEvent("focus", this);
35344         }
35345     },
35346
35347     beforeBlur : Roo.emptyFn,
35348
35349     // private
35350     onBlur : function(){
35351         this.beforeBlur();
35352         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35353             this.el.removeClass(this.focusClass);
35354         }
35355         this.hasFocus = false;
35356         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35357             this.validate();
35358         }
35359         var v = this.getValue();
35360         if(String(v) !== String(this.startValue)){
35361             this.fireEvent('change', this, v, this.startValue);
35362         }
35363         this.fireEvent("blur", this);
35364     },
35365
35366     /**
35367      * Returns whether or not the field value is currently valid
35368      * @param {Boolean} preventMark True to disable marking the field invalid
35369      * @return {Boolean} True if the value is valid, else false
35370      */
35371     isValid : function(preventMark){
35372         if(this.disabled){
35373             return true;
35374         }
35375         var restore = this.preventMark;
35376         this.preventMark = preventMark === true;
35377         var v = this.validateValue(this.processValue(this.getRawValue()));
35378         this.preventMark = restore;
35379         return v;
35380     },
35381
35382     /**
35383      * Validates the field value
35384      * @return {Boolean} True if the value is valid, else false
35385      */
35386     validate : function(){
35387         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35388             this.clearInvalid();
35389             return true;
35390         }
35391         return false;
35392     },
35393
35394     processValue : function(value){
35395         return value;
35396     },
35397
35398     // private
35399     // Subclasses should provide the validation implementation by overriding this
35400     validateValue : function(value){
35401         return true;
35402     },
35403
35404     /**
35405      * Mark this field as invalid
35406      * @param {String} msg The validation message
35407      */
35408     markInvalid : function(msg){
35409         if(!this.rendered || this.preventMark){ // not rendered
35410             return;
35411         }
35412         this.el.addClass(this.invalidClass);
35413         msg = msg || this.invalidText;
35414         switch(this.msgTarget){
35415             case 'qtip':
35416                 this.el.dom.qtip = msg;
35417                 this.el.dom.qclass = 'x-form-invalid-tip';
35418                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35419                     Roo.QuickTips.enable();
35420                 }
35421                 break;
35422             case 'title':
35423                 this.el.dom.title = msg;
35424                 break;
35425             case 'under':
35426                 if(!this.errorEl){
35427                     var elp = this.el.findParent('.x-form-element', 5, true);
35428                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35429                     this.errorEl.setWidth(elp.getWidth(true)-20);
35430                 }
35431                 this.errorEl.update(msg);
35432                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35433                 break;
35434             case 'side':
35435                 if(!this.errorIcon){
35436                     var elp = this.el.findParent('.x-form-element', 5, true);
35437                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35438                 }
35439                 this.alignErrorIcon();
35440                 this.errorIcon.dom.qtip = msg;
35441                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35442                 this.errorIcon.show();
35443                 this.on('resize', this.alignErrorIcon, this);
35444                 break;
35445             default:
35446                 var t = Roo.getDom(this.msgTarget);
35447                 t.innerHTML = msg;
35448                 t.style.display = this.msgDisplay;
35449                 break;
35450         }
35451         this.fireEvent('invalid', this, msg);
35452     },
35453
35454     // private
35455     alignErrorIcon : function(){
35456         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35457     },
35458
35459     /**
35460      * Clear any invalid styles/messages for this field
35461      */
35462     clearInvalid : function(){
35463         if(!this.rendered || this.preventMark){ // not rendered
35464             return;
35465         }
35466         this.el.removeClass(this.invalidClass);
35467         switch(this.msgTarget){
35468             case 'qtip':
35469                 this.el.dom.qtip = '';
35470                 break;
35471             case 'title':
35472                 this.el.dom.title = '';
35473                 break;
35474             case 'under':
35475                 if(this.errorEl){
35476                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35477                 }
35478                 break;
35479             case 'side':
35480                 if(this.errorIcon){
35481                     this.errorIcon.dom.qtip = '';
35482                     this.errorIcon.hide();
35483                     this.un('resize', this.alignErrorIcon, this);
35484                 }
35485                 break;
35486             default:
35487                 var t = Roo.getDom(this.msgTarget);
35488                 t.innerHTML = '';
35489                 t.style.display = 'none';
35490                 break;
35491         }
35492         this.fireEvent('valid', this);
35493     },
35494
35495     /**
35496      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35497      * @return {Mixed} value The field value
35498      */
35499     getRawValue : function(){
35500         var v = this.el.getValue();
35501         if(v === this.emptyText){
35502             v = '';
35503         }
35504         return v;
35505     },
35506
35507     /**
35508      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35509      * @return {Mixed} value The field value
35510      */
35511     getValue : function(){
35512         var v = this.el.getValue();
35513         if(v === this.emptyText || v === undefined){
35514             v = '';
35515         }
35516         return v;
35517     },
35518
35519     /**
35520      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35521      * @param {Mixed} value The value to set
35522      */
35523     setRawValue : function(v){
35524         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35525     },
35526
35527     /**
35528      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35529      * @param {Mixed} value The value to set
35530      */
35531     setValue : function(v){
35532         this.value = v;
35533         if(this.rendered){
35534             this.el.dom.value = (v === null || v === undefined ? '' : v);
35535             this.validate();
35536         }
35537     },
35538
35539     adjustSize : function(w, h){
35540         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35541         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35542         return s;
35543     },
35544
35545     adjustWidth : function(tag, w){
35546         tag = tag.toLowerCase();
35547         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35548             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35549                 if(tag == 'input'){
35550                     return w + 2;
35551                 }
35552                 if(tag = 'textarea'){
35553                     return w-2;
35554                 }
35555             }else if(Roo.isOpera){
35556                 if(tag == 'input'){
35557                     return w + 2;
35558                 }
35559                 if(tag = 'textarea'){
35560                     return w-2;
35561                 }
35562             }
35563         }
35564         return w;
35565     }
35566 });
35567
35568
35569 // anything other than normal should be considered experimental
35570 Roo.form.Field.msgFx = {
35571     normal : {
35572         show: function(msgEl, f){
35573             msgEl.setDisplayed('block');
35574         },
35575
35576         hide : function(msgEl, f){
35577             msgEl.setDisplayed(false).update('');
35578         }
35579     },
35580
35581     slide : {
35582         show: function(msgEl, f){
35583             msgEl.slideIn('t', {stopFx:true});
35584         },
35585
35586         hide : function(msgEl, f){
35587             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35588         }
35589     },
35590
35591     slideRight : {
35592         show: function(msgEl, f){
35593             msgEl.fixDisplay();
35594             msgEl.alignTo(f.el, 'tl-tr');
35595             msgEl.slideIn('l', {stopFx:true});
35596         },
35597
35598         hide : function(msgEl, f){
35599             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35600         }
35601     }
35602 };/*
35603  * Based on:
35604  * Ext JS Library 1.1.1
35605  * Copyright(c) 2006-2007, Ext JS, LLC.
35606  *
35607  * Originally Released Under LGPL - original licence link has changed is not relivant.
35608  *
35609  * Fork - LGPL
35610  * <script type="text/javascript">
35611  */
35612  
35613
35614 /**
35615  * @class Roo.form.TextField
35616  * @extends Roo.form.Field
35617  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35618  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35619  * @constructor
35620  * Creates a new TextField
35621  * @param {Object} config Configuration options
35622  */
35623 Roo.form.TextField = function(config){
35624     Roo.form.TextField.superclass.constructor.call(this, config);
35625     this.addEvents({
35626         /**
35627          * @event autosize
35628          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35629          * according to the default logic, but this event provides a hook for the developer to apply additional
35630          * logic at runtime to resize the field if needed.
35631              * @param {Roo.form.Field} this This text field
35632              * @param {Number} width The new field width
35633              */
35634         autosize : true
35635     });
35636 };
35637
35638 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35639     /**
35640      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35641      */
35642     grow : false,
35643     /**
35644      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35645      */
35646     growMin : 30,
35647     /**
35648      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35649      */
35650     growMax : 800,
35651     /**
35652      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35653      */
35654     vtype : null,
35655     /**
35656      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35657      */
35658     maskRe : null,
35659     /**
35660      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35661      */
35662     disableKeyFilter : false,
35663     /**
35664      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35665      */
35666     allowBlank : true,
35667     /**
35668      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35669      */
35670     minLength : 0,
35671     /**
35672      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35673      */
35674     maxLength : Number.MAX_VALUE,
35675     /**
35676      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35677      */
35678     minLengthText : "The minimum length for this field is {0}",
35679     /**
35680      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35681      */
35682     maxLengthText : "The maximum length for this field is {0}",
35683     /**
35684      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35685      */
35686     selectOnFocus : false,
35687     /**
35688      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35689      */
35690     blankText : "This field is required",
35691     /**
35692      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35693      * If available, this function will be called only after the basic validators all return true, and will be passed the
35694      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35695      */
35696     validator : null,
35697     /**
35698      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35699      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35700      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35701      */
35702     regex : null,
35703     /**
35704      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35705      */
35706     regexText : "",
35707     /**
35708      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35709      */
35710     emptyText : null,
35711     /**
35712      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35713      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35714      */
35715     emptyClass : 'x-form-empty-field',
35716
35717     // private
35718     initEvents : function(){
35719         Roo.form.TextField.superclass.initEvents.call(this);
35720         if(this.validationEvent == 'keyup'){
35721             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35722             this.el.on('keyup', this.filterValidation, this);
35723         }
35724         else if(this.validationEvent !== false){
35725             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35726         }
35727         if(this.selectOnFocus || this.emptyText){
35728             this.on("focus", this.preFocus, this);
35729             if(this.emptyText){
35730                 this.on('blur', this.postBlur, this);
35731                 this.applyEmptyText();
35732             }
35733         }
35734         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35735             this.el.on("keypress", this.filterKeys, this);
35736         }
35737         if(this.grow){
35738             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35739             this.el.on("click", this.autoSize,  this);
35740         }
35741     },
35742
35743     processValue : function(value){
35744         if(this.stripCharsRe){
35745             var newValue = value.replace(this.stripCharsRe, '');
35746             if(newValue !== value){
35747                 this.setRawValue(newValue);
35748                 return newValue;
35749             }
35750         }
35751         return value;
35752     },
35753
35754     filterValidation : function(e){
35755         if(!e.isNavKeyPress()){
35756             this.validationTask.delay(this.validationDelay);
35757         }
35758     },
35759
35760     // private
35761     onKeyUp : function(e){
35762         if(!e.isNavKeyPress()){
35763             this.autoSize();
35764         }
35765     },
35766
35767     /**
35768      * Resets the current field value to the originally-loaded value and clears any validation messages.
35769      * Also adds emptyText and emptyClass if the original value was blank.
35770      */
35771     reset : function(){
35772         Roo.form.TextField.superclass.reset.call(this);
35773         this.applyEmptyText();
35774     },
35775
35776     applyEmptyText : function(){
35777         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35778             this.setRawValue(this.emptyText);
35779             this.el.addClass(this.emptyClass);
35780         }
35781     },
35782
35783     // private
35784     preFocus : function(){
35785         if(this.emptyText){
35786             if(this.el.dom.value == this.emptyText){
35787                 this.setRawValue('');
35788             }
35789             this.el.removeClass(this.emptyClass);
35790         }
35791         if(this.selectOnFocus){
35792             this.el.dom.select();
35793         }
35794     },
35795
35796     // private
35797     postBlur : function(){
35798         this.applyEmptyText();
35799     },
35800
35801     // private
35802     filterKeys : function(e){
35803         var k = e.getKey();
35804         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35805             return;
35806         }
35807         var c = e.getCharCode(), cc = String.fromCharCode(c);
35808         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35809             return;
35810         }
35811         if(!this.maskRe.test(cc)){
35812             e.stopEvent();
35813         }
35814     },
35815
35816     setValue : function(v){
35817         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35818             this.el.removeClass(this.emptyClass);
35819         }
35820         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35821         this.applyEmptyText();
35822         this.autoSize();
35823     },
35824
35825     /**
35826      * Validates a value according to the field's validation rules and marks the field as invalid
35827      * if the validation fails
35828      * @param {Mixed} value The value to validate
35829      * @return {Boolean} True if the value is valid, else false
35830      */
35831     validateValue : function(value){
35832         if(value.length < 1 || value === this.emptyText){ // if it's blank
35833              if(this.allowBlank){
35834                 this.clearInvalid();
35835                 return true;
35836              }else{
35837                 this.markInvalid(this.blankText);
35838                 return false;
35839              }
35840         }
35841         if(value.length < this.minLength){
35842             this.markInvalid(String.format(this.minLengthText, this.minLength));
35843             return false;
35844         }
35845         if(value.length > this.maxLength){
35846             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35847             return false;
35848         }
35849         if(this.vtype){
35850             var vt = Roo.form.VTypes;
35851             if(!vt[this.vtype](value, this)){
35852                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35853                 return false;
35854             }
35855         }
35856         if(typeof this.validator == "function"){
35857             var msg = this.validator(value);
35858             if(msg !== true){
35859                 this.markInvalid(msg);
35860                 return false;
35861             }
35862         }
35863         if(this.regex && !this.regex.test(value)){
35864             this.markInvalid(this.regexText);
35865             return false;
35866         }
35867         return true;
35868     },
35869
35870     /**
35871      * Selects text in this field
35872      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35873      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35874      */
35875     selectText : function(start, end){
35876         var v = this.getRawValue();
35877         if(v.length > 0){
35878             start = start === undefined ? 0 : start;
35879             end = end === undefined ? v.length : end;
35880             var d = this.el.dom;
35881             if(d.setSelectionRange){
35882                 d.setSelectionRange(start, end);
35883             }else if(d.createTextRange){
35884                 var range = d.createTextRange();
35885                 range.moveStart("character", start);
35886                 range.moveEnd("character", v.length-end);
35887                 range.select();
35888             }
35889         }
35890     },
35891
35892     /**
35893      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35894      * This only takes effect if grow = true, and fires the autosize event.
35895      */
35896     autoSize : function(){
35897         if(!this.grow || !this.rendered){
35898             return;
35899         }
35900         if(!this.metrics){
35901             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35902         }
35903         var el = this.el;
35904         var v = el.dom.value;
35905         var d = document.createElement('div');
35906         d.appendChild(document.createTextNode(v));
35907         v = d.innerHTML;
35908         d = null;
35909         v += "&#160;";
35910         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35911         this.el.setWidth(w);
35912         this.fireEvent("autosize", this, w);
35913     }
35914 });/*
35915  * Based on:
35916  * Ext JS Library 1.1.1
35917  * Copyright(c) 2006-2007, Ext JS, LLC.
35918  *
35919  * Originally Released Under LGPL - original licence link has changed is not relivant.
35920  *
35921  * Fork - LGPL
35922  * <script type="text/javascript">
35923  */
35924  
35925 /**
35926  * @class Roo.form.Hidden
35927  * @extends Roo.form.TextField
35928  * Simple Hidden element used on forms 
35929  * 
35930  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35931  * 
35932  * @constructor
35933  * Creates a new Hidden form element.
35934  * @param {Object} config Configuration options
35935  */
35936
35937
35938
35939 // easy hidden field...
35940 Roo.form.Hidden = function(config){
35941     Roo.form.Hidden.superclass.constructor.call(this, config);
35942 };
35943   
35944 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35945     fieldLabel:      '',
35946     inputType:      'hidden',
35947     width:          50,
35948     allowBlank:     true,
35949     labelSeparator: '',
35950     hidden:         true,
35951     itemCls :       'x-form-item-display-none'
35952
35953
35954 });
35955
35956
35957 /*
35958  * Based on:
35959  * Ext JS Library 1.1.1
35960  * Copyright(c) 2006-2007, Ext JS, LLC.
35961  *
35962  * Originally Released Under LGPL - original licence link has changed is not relivant.
35963  *
35964  * Fork - LGPL
35965  * <script type="text/javascript">
35966  */
35967  
35968 /**
35969  * @class Roo.form.TriggerField
35970  * @extends Roo.form.TextField
35971  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35972  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35973  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35974  * for which you can provide a custom implementation.  For example:
35975  * <pre><code>
35976 var trigger = new Roo.form.TriggerField();
35977 trigger.onTriggerClick = myTriggerFn;
35978 trigger.applyTo('my-field');
35979 </code></pre>
35980  *
35981  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35982  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35983  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35984  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35985  * @constructor
35986  * Create a new TriggerField.
35987  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35988  * to the base TextField)
35989  */
35990 Roo.form.TriggerField = function(config){
35991     this.mimicing = false;
35992     Roo.form.TriggerField.superclass.constructor.call(this, config);
35993 };
35994
35995 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35996     /**
35997      * @cfg {String} triggerClass A CSS class to apply to the trigger
35998      */
35999     /**
36000      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36001      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36002      */
36003     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36004     /**
36005      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36006      */
36007     hideTrigger:false,
36008
36009     /** @cfg {Boolean} grow @hide */
36010     /** @cfg {Number} growMin @hide */
36011     /** @cfg {Number} growMax @hide */
36012
36013     /**
36014      * @hide 
36015      * @method
36016      */
36017     autoSize: Roo.emptyFn,
36018     // private
36019     monitorTab : true,
36020     // private
36021     deferHeight : true,
36022
36023     
36024     actionMode : 'wrap',
36025     // private
36026     onResize : function(w, h){
36027         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36028         if(typeof w == 'number'){
36029             var x = w - this.trigger.getWidth();
36030             this.el.setWidth(this.adjustWidth('input', x));
36031             this.trigger.setStyle('left', x+'px');
36032         }
36033     },
36034
36035     // private
36036     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36037
36038     // private
36039     getResizeEl : function(){
36040         return this.wrap;
36041     },
36042
36043     // private
36044     getPositionEl : function(){
36045         return this.wrap;
36046     },
36047
36048     // private
36049     alignErrorIcon : function(){
36050         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36051     },
36052
36053     // private
36054     onRender : function(ct, position){
36055         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36056         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36057         this.trigger = this.wrap.createChild(this.triggerConfig ||
36058                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36059         if(this.hideTrigger){
36060             this.trigger.setDisplayed(false);
36061         }
36062         this.initTrigger();
36063         if(!this.width){
36064             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36065         }
36066     },
36067
36068     // private
36069     initTrigger : function(){
36070         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36071         this.trigger.addClassOnOver('x-form-trigger-over');
36072         this.trigger.addClassOnClick('x-form-trigger-click');
36073     },
36074
36075     // private
36076     onDestroy : function(){
36077         if(this.trigger){
36078             this.trigger.removeAllListeners();
36079             this.trigger.remove();
36080         }
36081         if(this.wrap){
36082             this.wrap.remove();
36083         }
36084         Roo.form.TriggerField.superclass.onDestroy.call(this);
36085     },
36086
36087     // private
36088     onFocus : function(){
36089         Roo.form.TriggerField.superclass.onFocus.call(this);
36090         if(!this.mimicing){
36091             this.wrap.addClass('x-trigger-wrap-focus');
36092             this.mimicing = true;
36093             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36094             if(this.monitorTab){
36095                 this.el.on("keydown", this.checkTab, this);
36096             }
36097         }
36098     },
36099
36100     // private
36101     checkTab : function(e){
36102         if(e.getKey() == e.TAB){
36103             this.triggerBlur();
36104         }
36105     },
36106
36107     // private
36108     onBlur : function(){
36109         // do nothing
36110     },
36111
36112     // private
36113     mimicBlur : function(e, t){
36114         if(!this.wrap.contains(t) && this.validateBlur()){
36115             this.triggerBlur();
36116         }
36117     },
36118
36119     // private
36120     triggerBlur : function(){
36121         this.mimicing = false;
36122         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36123         if(this.monitorTab){
36124             this.el.un("keydown", this.checkTab, this);
36125         }
36126         this.wrap.removeClass('x-trigger-wrap-focus');
36127         Roo.form.TriggerField.superclass.onBlur.call(this);
36128     },
36129
36130     // private
36131     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36132     validateBlur : function(e, t){
36133         return true;
36134     },
36135
36136     // private
36137     onDisable : function(){
36138         Roo.form.TriggerField.superclass.onDisable.call(this);
36139         if(this.wrap){
36140             this.wrap.addClass('x-item-disabled');
36141         }
36142     },
36143
36144     // private
36145     onEnable : function(){
36146         Roo.form.TriggerField.superclass.onEnable.call(this);
36147         if(this.wrap){
36148             this.wrap.removeClass('x-item-disabled');
36149         }
36150     },
36151
36152     // private
36153     onShow : function(){
36154         var ae = this.getActionEl();
36155         
36156         if(ae){
36157             ae.dom.style.display = '';
36158             ae.dom.style.visibility = 'visible';
36159         }
36160     },
36161
36162     // private
36163     
36164     onHide : function(){
36165         var ae = this.getActionEl();
36166         ae.dom.style.display = 'none';
36167     },
36168
36169     /**
36170      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36171      * by an implementing function.
36172      * @method
36173      * @param {EventObject} e
36174      */
36175     onTriggerClick : Roo.emptyFn
36176 });
36177
36178 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36179 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36180 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36181 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36182     initComponent : function(){
36183         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36184
36185         this.triggerConfig = {
36186             tag:'span', cls:'x-form-twin-triggers', cn:[
36187             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36188             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36189         ]};
36190     },
36191
36192     getTrigger : function(index){
36193         return this.triggers[index];
36194     },
36195
36196     initTrigger : function(){
36197         var ts = this.trigger.select('.x-form-trigger', true);
36198         this.wrap.setStyle('overflow', 'hidden');
36199         var triggerField = this;
36200         ts.each(function(t, all, index){
36201             t.hide = function(){
36202                 var w = triggerField.wrap.getWidth();
36203                 this.dom.style.display = 'none';
36204                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36205             };
36206             t.show = function(){
36207                 var w = triggerField.wrap.getWidth();
36208                 this.dom.style.display = '';
36209                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36210             };
36211             var triggerIndex = 'Trigger'+(index+1);
36212
36213             if(this['hide'+triggerIndex]){
36214                 t.dom.style.display = 'none';
36215             }
36216             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36217             t.addClassOnOver('x-form-trigger-over');
36218             t.addClassOnClick('x-form-trigger-click');
36219         }, this);
36220         this.triggers = ts.elements;
36221     },
36222
36223     onTrigger1Click : Roo.emptyFn,
36224     onTrigger2Click : Roo.emptyFn
36225 });/*
36226  * Based on:
36227  * Ext JS Library 1.1.1
36228  * Copyright(c) 2006-2007, Ext JS, LLC.
36229  *
36230  * Originally Released Under LGPL - original licence link has changed is not relivant.
36231  *
36232  * Fork - LGPL
36233  * <script type="text/javascript">
36234  */
36235  
36236 /**
36237  * @class Roo.form.TextArea
36238  * @extends Roo.form.TextField
36239  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36240  * support for auto-sizing.
36241  * @constructor
36242  * Creates a new TextArea
36243  * @param {Object} config Configuration options
36244  */
36245 Roo.form.TextArea = function(config){
36246     Roo.form.TextArea.superclass.constructor.call(this, config);
36247     // these are provided exchanges for backwards compat
36248     // minHeight/maxHeight were replaced by growMin/growMax to be
36249     // compatible with TextField growing config values
36250     if(this.minHeight !== undefined){
36251         this.growMin = this.minHeight;
36252     }
36253     if(this.maxHeight !== undefined){
36254         this.growMax = this.maxHeight;
36255     }
36256 };
36257
36258 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36259     /**
36260      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36261      */
36262     growMin : 60,
36263     /**
36264      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36265      */
36266     growMax: 1000,
36267     /**
36268      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36269      * in the field (equivalent to setting overflow: hidden, defaults to false)
36270      */
36271     preventScrollbars: false,
36272     /**
36273      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36274      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36275      */
36276
36277     // private
36278     onRender : function(ct, position){
36279         if(!this.el){
36280             this.defaultAutoCreate = {
36281                 tag: "textarea",
36282                 style:"width:300px;height:60px;",
36283                 autocomplete: "off"
36284             };
36285         }
36286         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36287         if(this.grow){
36288             this.textSizeEl = Roo.DomHelper.append(document.body, {
36289                 tag: "pre", cls: "x-form-grow-sizer"
36290             });
36291             if(this.preventScrollbars){
36292                 this.el.setStyle("overflow", "hidden");
36293             }
36294             this.el.setHeight(this.growMin);
36295         }
36296     },
36297
36298     onDestroy : function(){
36299         if(this.textSizeEl){
36300             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36301         }
36302         Roo.form.TextArea.superclass.onDestroy.call(this);
36303     },
36304
36305     // private
36306     onKeyUp : function(e){
36307         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36308             this.autoSize();
36309         }
36310     },
36311
36312     /**
36313      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36314      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36315      */
36316     autoSize : function(){
36317         if(!this.grow || !this.textSizeEl){
36318             return;
36319         }
36320         var el = this.el;
36321         var v = el.dom.value;
36322         var ts = this.textSizeEl;
36323
36324         ts.innerHTML = '';
36325         ts.appendChild(document.createTextNode(v));
36326         v = ts.innerHTML;
36327
36328         Roo.fly(ts).setWidth(this.el.getWidth());
36329         if(v.length < 1){
36330             v = "&#160;&#160;";
36331         }else{
36332             if(Roo.isIE){
36333                 v = v.replace(/\n/g, '<p>&#160;</p>');
36334             }
36335             v += "&#160;\n&#160;";
36336         }
36337         ts.innerHTML = v;
36338         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36339         if(h != this.lastHeight){
36340             this.lastHeight = h;
36341             this.el.setHeight(h);
36342             this.fireEvent("autosize", this, h);
36343         }
36344     }
36345 });/*
36346  * Based on:
36347  * Ext JS Library 1.1.1
36348  * Copyright(c) 2006-2007, Ext JS, LLC.
36349  *
36350  * Originally Released Under LGPL - original licence link has changed is not relivant.
36351  *
36352  * Fork - LGPL
36353  * <script type="text/javascript">
36354  */
36355  
36356
36357 /**
36358  * @class Roo.form.NumberField
36359  * @extends Roo.form.TextField
36360  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36361  * @constructor
36362  * Creates a new NumberField
36363  * @param {Object} config Configuration options
36364  */
36365 Roo.form.NumberField = function(config){
36366     Roo.form.NumberField.superclass.constructor.call(this, config);
36367 };
36368
36369 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36370     /**
36371      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36372      */
36373     fieldClass: "x-form-field x-form-num-field",
36374     /**
36375      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36376      */
36377     allowDecimals : true,
36378     /**
36379      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36380      */
36381     decimalSeparator : ".",
36382     /**
36383      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36384      */
36385     decimalPrecision : 2,
36386     /**
36387      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36388      */
36389     allowNegative : true,
36390     /**
36391      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36392      */
36393     minValue : Number.NEGATIVE_INFINITY,
36394     /**
36395      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36396      */
36397     maxValue : Number.MAX_VALUE,
36398     /**
36399      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36400      */
36401     minText : "The minimum value for this field is {0}",
36402     /**
36403      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36404      */
36405     maxText : "The maximum value for this field is {0}",
36406     /**
36407      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36408      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36409      */
36410     nanText : "{0} is not a valid number",
36411
36412     // private
36413     initEvents : function(){
36414         Roo.form.NumberField.superclass.initEvents.call(this);
36415         var allowed = "0123456789";
36416         if(this.allowDecimals){
36417             allowed += this.decimalSeparator;
36418         }
36419         if(this.allowNegative){
36420             allowed += "-";
36421         }
36422         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36423         var keyPress = function(e){
36424             var k = e.getKey();
36425             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36426                 return;
36427             }
36428             var c = e.getCharCode();
36429             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36430                 e.stopEvent();
36431             }
36432         };
36433         this.el.on("keypress", keyPress, this);
36434     },
36435
36436     // private
36437     validateValue : function(value){
36438         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36439             return false;
36440         }
36441         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36442              return true;
36443         }
36444         var num = this.parseValue(value);
36445         if(isNaN(num)){
36446             this.markInvalid(String.format(this.nanText, value));
36447             return false;
36448         }
36449         if(num < this.minValue){
36450             this.markInvalid(String.format(this.minText, this.minValue));
36451             return false;
36452         }
36453         if(num > this.maxValue){
36454             this.markInvalid(String.format(this.maxText, this.maxValue));
36455             return false;
36456         }
36457         return true;
36458     },
36459
36460     getValue : function(){
36461         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36462     },
36463
36464     // private
36465     parseValue : function(value){
36466         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36467         return isNaN(value) ? '' : value;
36468     },
36469
36470     // private
36471     fixPrecision : function(value){
36472         var nan = isNaN(value);
36473         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36474             return nan ? '' : value;
36475         }
36476         return parseFloat(value).toFixed(this.decimalPrecision);
36477     },
36478
36479     setValue : function(v){
36480         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36481     },
36482
36483     // private
36484     decimalPrecisionFcn : function(v){
36485         return Math.floor(v);
36486     },
36487
36488     beforeBlur : function(){
36489         var v = this.parseValue(this.getRawValue());
36490         if(v){
36491             this.setValue(this.fixPrecision(v));
36492         }
36493     }
36494 });/*
36495  * Based on:
36496  * Ext JS Library 1.1.1
36497  * Copyright(c) 2006-2007, Ext JS, LLC.
36498  *
36499  * Originally Released Under LGPL - original licence link has changed is not relivant.
36500  *
36501  * Fork - LGPL
36502  * <script type="text/javascript">
36503  */
36504  
36505 /**
36506  * @class Roo.form.DateField
36507  * @extends Roo.form.TriggerField
36508  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36509 * @constructor
36510 * Create a new DateField
36511 * @param {Object} config
36512  */
36513 Roo.form.DateField = function(config){
36514     Roo.form.DateField.superclass.constructor.call(this, config);
36515     
36516       this.addEvents({
36517          
36518         /**
36519          * @event select
36520          * Fires when a date is selected
36521              * @param {Roo.form.DateField} combo This combo box
36522              * @param {Date} date The date selected
36523              */
36524         'select' : true
36525          
36526     });
36527     
36528     
36529     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36530     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36531     this.ddMatch = null;
36532     if(this.disabledDates){
36533         var dd = this.disabledDates;
36534         var re = "(?:";
36535         for(var i = 0; i < dd.length; i++){
36536             re += dd[i];
36537             if(i != dd.length-1) re += "|";
36538         }
36539         this.ddMatch = new RegExp(re + ")");
36540     }
36541 };
36542
36543 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36544     /**
36545      * @cfg {String} format
36546      * The default date format string which can be overriden for localization support.  The format must be
36547      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36548      */
36549     format : "m/d/y",
36550     /**
36551      * @cfg {String} altFormats
36552      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36553      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36554      */
36555     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36556     /**
36557      * @cfg {Array} disabledDays
36558      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36559      */
36560     disabledDays : null,
36561     /**
36562      * @cfg {String} disabledDaysText
36563      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36564      */
36565     disabledDaysText : "Disabled",
36566     /**
36567      * @cfg {Array} disabledDates
36568      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36569      * expression so they are very powerful. Some examples:
36570      * <ul>
36571      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36572      * <li>["03/08", "09/16"] would disable those days for every year</li>
36573      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36574      * <li>["03/../2006"] would disable every day in March 2006</li>
36575      * <li>["^03"] would disable every day in every March</li>
36576      * </ul>
36577      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36578      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36579      */
36580     disabledDates : null,
36581     /**
36582      * @cfg {String} disabledDatesText
36583      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36584      */
36585     disabledDatesText : "Disabled",
36586     /**
36587      * @cfg {Date/String} minValue
36588      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36589      * valid format (defaults to null).
36590      */
36591     minValue : null,
36592     /**
36593      * @cfg {Date/String} maxValue
36594      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36595      * valid format (defaults to null).
36596      */
36597     maxValue : null,
36598     /**
36599      * @cfg {String} minText
36600      * The error text to display when the date in the cell is before minValue (defaults to
36601      * 'The date in this field must be after {minValue}').
36602      */
36603     minText : "The date in this field must be equal to or after {0}",
36604     /**
36605      * @cfg {String} maxText
36606      * The error text to display when the date in the cell is after maxValue (defaults to
36607      * 'The date in this field must be before {maxValue}').
36608      */
36609     maxText : "The date in this field must be equal to or before {0}",
36610     /**
36611      * @cfg {String} invalidText
36612      * The error text to display when the date in the field is invalid (defaults to
36613      * '{value} is not a valid date - it must be in the format {format}').
36614      */
36615     invalidText : "{0} is not a valid date - it must be in the format {1}",
36616     /**
36617      * @cfg {String} triggerClass
36618      * An additional CSS class used to style the trigger button.  The trigger will always get the
36619      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36620      * which displays a calendar icon).
36621      */
36622     triggerClass : 'x-form-date-trigger',
36623     
36624
36625     /**
36626      * @cfg {bool} useIso
36627      * if enabled, then the date field will use a hidden field to store the 
36628      * real value as iso formated date. default (false)
36629      */ 
36630     useIso : false,
36631     /**
36632      * @cfg {String/Object} autoCreate
36633      * A DomHelper element spec, or true for a default element spec (defaults to
36634      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36635      */ 
36636     // private
36637     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36638     
36639     // private
36640     hiddenField: false,
36641     
36642     onRender : function(ct, position)
36643     {
36644         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36645         if (this.useIso) {
36646             this.el.dom.removeAttribute('name'); 
36647             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36648                     'before', true);
36649             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36650             // prevent input submission
36651             this.hiddenName = this.name;
36652         }
36653             
36654             
36655     },
36656     
36657     // private
36658     validateValue : function(value)
36659     {
36660         value = this.formatDate(value);
36661         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36662             return false;
36663         }
36664         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36665              return true;
36666         }
36667         var svalue = value;
36668         value = this.parseDate(value);
36669         if(!value){
36670             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36671             return false;
36672         }
36673         var time = value.getTime();
36674         if(this.minValue && time < this.minValue.getTime()){
36675             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36676             return false;
36677         }
36678         if(this.maxValue && time > this.maxValue.getTime()){
36679             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36680             return false;
36681         }
36682         if(this.disabledDays){
36683             var day = value.getDay();
36684             for(var i = 0; i < this.disabledDays.length; i++) {
36685                 if(day === this.disabledDays[i]){
36686                     this.markInvalid(this.disabledDaysText);
36687                     return false;
36688                 }
36689             }
36690         }
36691         var fvalue = this.formatDate(value);
36692         if(this.ddMatch && this.ddMatch.test(fvalue)){
36693             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36694             return false;
36695         }
36696         return true;
36697     },
36698
36699     // private
36700     // Provides logic to override the default TriggerField.validateBlur which just returns true
36701     validateBlur : function(){
36702         return !this.menu || !this.menu.isVisible();
36703     },
36704
36705     /**
36706      * Returns the current date value of the date field.
36707      * @return {Date} The date value
36708      */
36709     getValue : function(){
36710         
36711         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36712     },
36713
36714     /**
36715      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36716      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36717      * (the default format used is "m/d/y").
36718      * <br />Usage:
36719      * <pre><code>
36720 //All of these calls set the same date value (May 4, 2006)
36721
36722 //Pass a date object:
36723 var dt = new Date('5/4/06');
36724 dateField.setValue(dt);
36725
36726 //Pass a date string (default format):
36727 dateField.setValue('5/4/06');
36728
36729 //Pass a date string (custom format):
36730 dateField.format = 'Y-m-d';
36731 dateField.setValue('2006-5-4');
36732 </code></pre>
36733      * @param {String/Date} date The date or valid date string
36734      */
36735     setValue : function(date){
36736         if (this.hiddenField) {
36737             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36738         }
36739         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36740     },
36741
36742     // private
36743     parseDate : function(value){
36744         if(!value || value instanceof Date){
36745             return value;
36746         }
36747         var v = Date.parseDate(value, this.format);
36748         if(!v && this.altFormats){
36749             if(!this.altFormatsArray){
36750                 this.altFormatsArray = this.altFormats.split("|");
36751             }
36752             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36753                 v = Date.parseDate(value, this.altFormatsArray[i]);
36754             }
36755         }
36756         return v;
36757     },
36758
36759     // private
36760     formatDate : function(date, fmt){
36761         return (!date || !(date instanceof Date)) ?
36762                date : date.dateFormat(fmt || this.format);
36763     },
36764
36765     // private
36766     menuListeners : {
36767         select: function(m, d){
36768             this.setValue(d);
36769             this.fireEvent('select', this, d);
36770         },
36771         show : function(){ // retain focus styling
36772             this.onFocus();
36773         },
36774         hide : function(){
36775             this.focus.defer(10, this);
36776             var ml = this.menuListeners;
36777             this.menu.un("select", ml.select,  this);
36778             this.menu.un("show", ml.show,  this);
36779             this.menu.un("hide", ml.hide,  this);
36780         }
36781     },
36782
36783     // private
36784     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36785     onTriggerClick : function(){
36786         if(this.disabled){
36787             return;
36788         }
36789         if(this.menu == null){
36790             this.menu = new Roo.menu.DateMenu();
36791         }
36792         Roo.apply(this.menu.picker,  {
36793             showClear: this.allowBlank,
36794             minDate : this.minValue,
36795             maxDate : this.maxValue,
36796             disabledDatesRE : this.ddMatch,
36797             disabledDatesText : this.disabledDatesText,
36798             disabledDays : this.disabledDays,
36799             disabledDaysText : this.disabledDaysText,
36800             format : this.format,
36801             minText : String.format(this.minText, this.formatDate(this.minValue)),
36802             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36803         });
36804         this.menu.on(Roo.apply({}, this.menuListeners, {
36805             scope:this
36806         }));
36807         this.menu.picker.setValue(this.getValue() || new Date());
36808         this.menu.show(this.el, "tl-bl?");
36809     },
36810
36811     beforeBlur : function(){
36812         var v = this.parseDate(this.getRawValue());
36813         if(v){
36814             this.setValue(v);
36815         }
36816     }
36817
36818     /** @cfg {Boolean} grow @hide */
36819     /** @cfg {Number} growMin @hide */
36820     /** @cfg {Number} growMax @hide */
36821     /**
36822      * @hide
36823      * @method autoSize
36824      */
36825 });/*
36826  * Based on:
36827  * Ext JS Library 1.1.1
36828  * Copyright(c) 2006-2007, Ext JS, LLC.
36829  *
36830  * Originally Released Under LGPL - original licence link has changed is not relivant.
36831  *
36832  * Fork - LGPL
36833  * <script type="text/javascript">
36834  */
36835  
36836
36837 /**
36838  * @class Roo.form.ComboBox
36839  * @extends Roo.form.TriggerField
36840  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36841  * @constructor
36842  * Create a new ComboBox.
36843  * @param {Object} config Configuration options
36844  */
36845 Roo.form.ComboBox = function(config){
36846     Roo.form.ComboBox.superclass.constructor.call(this, config);
36847     this.addEvents({
36848         /**
36849          * @event expand
36850          * Fires when the dropdown list is expanded
36851              * @param {Roo.form.ComboBox} combo This combo box
36852              */
36853         'expand' : true,
36854         /**
36855          * @event collapse
36856          * Fires when the dropdown list is collapsed
36857              * @param {Roo.form.ComboBox} combo This combo box
36858              */
36859         'collapse' : true,
36860         /**
36861          * @event beforeselect
36862          * Fires before a list item is selected. Return false to cancel the selection.
36863              * @param {Roo.form.ComboBox} combo This combo box
36864              * @param {Roo.data.Record} record The data record returned from the underlying store
36865              * @param {Number} index The index of the selected item in the dropdown list
36866              */
36867         'beforeselect' : true,
36868         /**
36869          * @event select
36870          * Fires when a list item is selected
36871              * @param {Roo.form.ComboBox} combo This combo box
36872              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36873              * @param {Number} index The index of the selected item in the dropdown list
36874              */
36875         'select' : true,
36876         /**
36877          * @event beforequery
36878          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36879          * The event object passed has these properties:
36880              * @param {Roo.form.ComboBox} combo This combo box
36881              * @param {String} query The query
36882              * @param {Boolean} forceAll true to force "all" query
36883              * @param {Boolean} cancel true to cancel the query
36884              * @param {Object} e The query event object
36885              */
36886         'beforequery': true,
36887          /**
36888          * @event add
36889          * Fires when the 'add' icon is pressed (add a listener to enable add button)
36890              * @param {Roo.form.ComboBox} combo This combo box
36891              */
36892         'add' : true,
36893         /**
36894          * @event edit
36895          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
36896              * @param {Roo.form.ComboBox} combo This combo box
36897              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
36898              */
36899         'edit' : true
36900         
36901         
36902     });
36903     if(this.transform){
36904         this.allowDomMove = false;
36905         var s = Roo.getDom(this.transform);
36906         if(!this.hiddenName){
36907             this.hiddenName = s.name;
36908         }
36909         if(!this.store){
36910             this.mode = 'local';
36911             var d = [], opts = s.options;
36912             for(var i = 0, len = opts.length;i < len; i++){
36913                 var o = opts[i];
36914                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36915                 if(o.selected) {
36916                     this.value = value;
36917                 }
36918                 d.push([value, o.text]);
36919             }
36920             this.store = new Roo.data.SimpleStore({
36921                 'id': 0,
36922                 fields: ['value', 'text'],
36923                 data : d
36924             });
36925             this.valueField = 'value';
36926             this.displayField = 'text';
36927         }
36928         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36929         if(!this.lazyRender){
36930             this.target = true;
36931             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36932             s.parentNode.removeChild(s); // remove it
36933             this.render(this.el.parentNode);
36934         }else{
36935             s.parentNode.removeChild(s); // remove it
36936         }
36937
36938     }
36939     if (this.store) {
36940         this.store = Roo.factory(this.store, Roo.data);
36941     }
36942     
36943     this.selectedIndex = -1;
36944     if(this.mode == 'local'){
36945         if(config.queryDelay === undefined){
36946             this.queryDelay = 10;
36947         }
36948         if(config.minChars === undefined){
36949             this.minChars = 0;
36950         }
36951     }
36952 };
36953
36954 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36955     /**
36956      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36957      */
36958     /**
36959      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36960      * rendering into an Roo.Editor, defaults to false)
36961      */
36962     /**
36963      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36964      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36965      */
36966     /**
36967      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36968      */
36969     /**
36970      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36971      * the dropdown list (defaults to undefined, with no header element)
36972      */
36973
36974      /**
36975      * @cfg {String/Roo.Template} tpl The template to use to render the output
36976      */
36977      
36978     // private
36979     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36980     /**
36981      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36982      */
36983     listWidth: undefined,
36984     /**
36985      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36986      * mode = 'remote' or 'text' if mode = 'local')
36987      */
36988     displayField: undefined,
36989     /**
36990      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36991      * mode = 'remote' or 'value' if mode = 'local'). 
36992      * Note: use of a valueField requires the user make a selection
36993      * in order for a value to be mapped.
36994      */
36995     valueField: undefined,
36996     /**
36997      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36998      * field's data value (defaults to the underlying DOM element's name)
36999      */
37000     hiddenName: undefined,
37001     /**
37002      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37003      */
37004     listClass: '',
37005     /**
37006      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37007      */
37008     selectedClass: 'x-combo-selected',
37009     /**
37010      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37011      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37012      * which displays a downward arrow icon).
37013      */
37014     triggerClass : 'x-form-arrow-trigger',
37015     /**
37016      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37017      */
37018     shadow:'sides',
37019     /**
37020      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37021      * anchor positions (defaults to 'tl-bl')
37022      */
37023     listAlign: 'tl-bl?',
37024     /**
37025      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37026      */
37027     maxHeight: 300,
37028     /**
37029      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37030      * query specified by the allQuery config option (defaults to 'query')
37031      */
37032     triggerAction: 'query',
37033     /**
37034      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37035      * (defaults to 4, does not apply if editable = false)
37036      */
37037     minChars : 4,
37038     /**
37039      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37040      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37041      */
37042     typeAhead: false,
37043     /**
37044      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37045      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37046      */
37047     queryDelay: 500,
37048     /**
37049      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37050      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37051      */
37052     pageSize: 0,
37053     /**
37054      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37055      * when editable = true (defaults to false)
37056      */
37057     selectOnFocus:false,
37058     /**
37059      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37060      */
37061     queryParam: 'query',
37062     /**
37063      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37064      * when mode = 'remote' (defaults to 'Loading...')
37065      */
37066     loadingText: 'Loading...',
37067     /**
37068      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37069      */
37070     resizable: false,
37071     /**
37072      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37073      */
37074     handleHeight : 8,
37075     /**
37076      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37077      * traditional select (defaults to true)
37078      */
37079     editable: true,
37080     /**
37081      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37082      */
37083     allQuery: '',
37084     /**
37085      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37086      */
37087     mode: 'remote',
37088     /**
37089      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37090      * listWidth has a higher value)
37091      */
37092     minListWidth : 70,
37093     /**
37094      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37095      * allow the user to set arbitrary text into the field (defaults to false)
37096      */
37097     forceSelection:false,
37098     /**
37099      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37100      * if typeAhead = true (defaults to 250)
37101      */
37102     typeAheadDelay : 250,
37103     /**
37104      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37105      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37106      */
37107     valueNotFoundText : undefined,
37108     /**
37109      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37110      */
37111     blockFocus : false,
37112     
37113     /**
37114      * @cfg {bool} disableClear Disable showing of clear button.
37115      */
37116     disableClear : false,
37117     
37118     //private
37119     addicon : false,
37120     editicon: false,
37121     
37122     
37123     // private
37124     onRender : function(ct, position){
37125         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37126         if(this.hiddenName){
37127             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37128                     'before', true);
37129             this.hiddenField.value =
37130                 this.hiddenValue !== undefined ? this.hiddenValue :
37131                 this.value !== undefined ? this.value : '';
37132
37133             // prevent input submission
37134             this.el.dom.removeAttribute('name');
37135         }
37136         if(Roo.isGecko){
37137             this.el.dom.setAttribute('autocomplete', 'off');
37138         }
37139
37140         var cls = 'x-combo-list';
37141
37142         this.list = new Roo.Layer({
37143             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37144         });
37145
37146         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37147         this.list.setWidth(lw);
37148         this.list.swallowEvent('mousewheel');
37149         this.assetHeight = 0;
37150
37151         if(this.title){
37152             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37153             this.assetHeight += this.header.getHeight();
37154         }
37155
37156         this.innerList = this.list.createChild({cls:cls+'-inner'});
37157         this.innerList.on('mouseover', this.onViewOver, this);
37158         this.innerList.on('mousemove', this.onViewMove, this);
37159         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37160         
37161         if(this.allowBlank && !this.pageSize && !this.disableClear){
37162             this.footer = this.list.createChild({cls:cls+'-ft'});
37163             this.pageTb = new Roo.Toolbar(this.footer);
37164            
37165         }
37166         if(this.pageSize){
37167             this.footer = this.list.createChild({cls:cls+'-ft'});
37168             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37169                     {pageSize: this.pageSize});
37170             
37171         }
37172         
37173         if (this.pageTb && this.allowBlank && !this.disableClear) {
37174             var _this = this;
37175             this.pageTb.add(new Roo.Toolbar.Fill(), {
37176                 cls: 'x-btn-icon x-btn-clear',
37177                 text: '&#160;',
37178                 handler: function()
37179                 {
37180                     _this.collapse();
37181                     _this.clearValue();
37182                     _this.onSelect(false, -1);
37183                 }
37184             });
37185         }
37186         if (this.footer) {
37187             this.assetHeight += this.footer.getHeight();
37188         }
37189         
37190
37191         if(!this.tpl){
37192             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37193         }
37194
37195         this.view = new Roo.View(this.innerList, this.tpl, {
37196             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37197         });
37198
37199         this.view.on('click', this.onViewClick, this);
37200
37201         this.store.on('beforeload', this.onBeforeLoad, this);
37202         this.store.on('load', this.onLoad, this);
37203         this.store.on('loadexception', this.collapse, this);
37204
37205         if(this.resizable){
37206             this.resizer = new Roo.Resizable(this.list,  {
37207                pinned:true, handles:'se'
37208             });
37209             this.resizer.on('resize', function(r, w, h){
37210                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37211                 this.listWidth = w;
37212                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37213                 this.restrictHeight();
37214             }, this);
37215             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37216         }
37217         if(!this.editable){
37218             this.editable = true;
37219             this.setEditable(false);
37220         }  
37221         
37222         
37223         if (typeof(this.events.add.listeners) != 'undefined') {
37224             
37225             this.addicon = this.wrap.createChild(
37226                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37227        
37228             this.addicon.on('click', function(e) {
37229                 this.fireEvent('add', this);
37230             }, this);
37231         }
37232         if (typeof(this.events.edit.listeners) != 'undefined') {
37233             
37234             this.editicon = this.wrap.createChild(
37235                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37236             if (this.addicon) {
37237                 this.editicon.setStyle('margin-left', '40px');
37238             }
37239             this.editicon.on('click', function(e) {
37240                 
37241                 // we fire even  if inothing is selected..
37242                 this.fireEvent('edit', this, this.lastData );
37243                 
37244             }, this);
37245         }
37246         
37247         
37248         
37249     },
37250
37251     // private
37252     initEvents : function(){
37253         Roo.form.ComboBox.superclass.initEvents.call(this);
37254
37255         this.keyNav = new Roo.KeyNav(this.el, {
37256             "up" : function(e){
37257                 this.inKeyMode = true;
37258                 this.selectPrev();
37259             },
37260
37261             "down" : function(e){
37262                 if(!this.isExpanded()){
37263                     this.onTriggerClick();
37264                 }else{
37265                     this.inKeyMode = true;
37266                     this.selectNext();
37267                 }
37268             },
37269
37270             "enter" : function(e){
37271                 this.onViewClick();
37272                 //return true;
37273             },
37274
37275             "esc" : function(e){
37276                 this.collapse();
37277             },
37278
37279             "tab" : function(e){
37280                 this.onViewClick(false);
37281                 return true;
37282             },
37283
37284             scope : this,
37285
37286             doRelay : function(foo, bar, hname){
37287                 if(hname == 'down' || this.scope.isExpanded()){
37288                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37289                 }
37290                 return true;
37291             },
37292
37293             forceKeyDown: true
37294         });
37295         this.queryDelay = Math.max(this.queryDelay || 10,
37296                 this.mode == 'local' ? 10 : 250);
37297         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37298         if(this.typeAhead){
37299             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37300         }
37301         if(this.editable !== false){
37302             this.el.on("keyup", this.onKeyUp, this);
37303         }
37304         if(this.forceSelection){
37305             this.on('blur', this.doForce, this);
37306         }
37307     },
37308
37309     onDestroy : function(){
37310         if(this.view){
37311             this.view.setStore(null);
37312             this.view.el.removeAllListeners();
37313             this.view.el.remove();
37314             this.view.purgeListeners();
37315         }
37316         if(this.list){
37317             this.list.destroy();
37318         }
37319         if(this.store){
37320             this.store.un('beforeload', this.onBeforeLoad, this);
37321             this.store.un('load', this.onLoad, this);
37322             this.store.un('loadexception', this.collapse, this);
37323         }
37324         Roo.form.ComboBox.superclass.onDestroy.call(this);
37325     },
37326
37327     // private
37328     fireKey : function(e){
37329         if(e.isNavKeyPress() && !this.list.isVisible()){
37330             this.fireEvent("specialkey", this, e);
37331         }
37332     },
37333
37334     // private
37335     onResize: function(w, h){
37336         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37337         
37338         if(typeof w != 'number'){
37339             // we do not handle it!?!?
37340             return;
37341         }
37342         var tw = this.trigger.getWidth();
37343         tw += this.addicon ? this.addicon.getWidth() : 0;
37344         tw += this.editicon ? this.editicon.getWidth() : 0;
37345         var x = w - tw;
37346         this.el.setWidth( this.adjustWidth('input', x));
37347             
37348         this.trigger.setStyle('left', x+'px');
37349         
37350         if(this.list && this.listWidth === undefined){
37351             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37352             this.list.setWidth(lw);
37353             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37354         }
37355         
37356     
37357         
37358     },
37359
37360     /**
37361      * Allow or prevent the user from directly editing the field text.  If false is passed,
37362      * the user will only be able to select from the items defined in the dropdown list.  This method
37363      * is the runtime equivalent of setting the 'editable' config option at config time.
37364      * @param {Boolean} value True to allow the user to directly edit the field text
37365      */
37366     setEditable : function(value){
37367         if(value == this.editable){
37368             return;
37369         }
37370         this.editable = value;
37371         if(!value){
37372             this.el.dom.setAttribute('readOnly', true);
37373             this.el.on('mousedown', this.onTriggerClick,  this);
37374             this.el.addClass('x-combo-noedit');
37375         }else{
37376             this.el.dom.setAttribute('readOnly', false);
37377             this.el.un('mousedown', this.onTriggerClick,  this);
37378             this.el.removeClass('x-combo-noedit');
37379         }
37380     },
37381
37382     // private
37383     onBeforeLoad : function(){
37384         if(!this.hasFocus){
37385             return;
37386         }
37387         this.innerList.update(this.loadingText ?
37388                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37389         this.restrictHeight();
37390         this.selectedIndex = -1;
37391     },
37392
37393     // private
37394     onLoad : function(){
37395         if(!this.hasFocus){
37396             return;
37397         }
37398         if(this.store.getCount() > 0){
37399             this.expand();
37400             this.restrictHeight();
37401             if(this.lastQuery == this.allQuery){
37402                 if(this.editable){
37403                     this.el.dom.select();
37404                 }
37405                 if(!this.selectByValue(this.value, true)){
37406                     this.select(0, true);
37407                 }
37408             }else{
37409                 this.selectNext();
37410                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37411                     this.taTask.delay(this.typeAheadDelay);
37412                 }
37413             }
37414         }else{
37415             this.onEmptyResults();
37416         }
37417         //this.el.focus();
37418     },
37419
37420     // private
37421     onTypeAhead : function(){
37422         if(this.store.getCount() > 0){
37423             var r = this.store.getAt(0);
37424             var newValue = r.data[this.displayField];
37425             var len = newValue.length;
37426             var selStart = this.getRawValue().length;
37427             if(selStart != len){
37428                 this.setRawValue(newValue);
37429                 this.selectText(selStart, newValue.length);
37430             }
37431         }
37432     },
37433
37434     // private
37435     onSelect : function(record, index){
37436         if(this.fireEvent('beforeselect', this, record, index) !== false){
37437             this.setFromData(index > -1 ? record.data : false);
37438             this.collapse();
37439             this.fireEvent('select', this, record, index);
37440         }
37441     },
37442
37443     /**
37444      * Returns the currently selected field value or empty string if no value is set.
37445      * @return {String} value The selected value
37446      */
37447     getValue : function(){
37448         if(this.valueField){
37449             return typeof this.value != 'undefined' ? this.value : '';
37450         }else{
37451             return Roo.form.ComboBox.superclass.getValue.call(this);
37452         }
37453     },
37454
37455     /**
37456      * Clears any text/value currently set in the field
37457      */
37458     clearValue : function(){
37459         if(this.hiddenField){
37460             this.hiddenField.value = '';
37461         }
37462         this.value = '';
37463         this.setRawValue('');
37464         this.lastSelectionText = '';
37465         this.applyEmptyText();
37466     },
37467
37468     /**
37469      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37470      * will be displayed in the field.  If the value does not match the data value of an existing item,
37471      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37472      * Otherwise the field will be blank (although the value will still be set).
37473      * @param {String} value The value to match
37474      */
37475     setValue : function(v){
37476         var text = v;
37477         if(this.valueField){
37478             var r = this.findRecord(this.valueField, v);
37479             if(r){
37480                 text = r.data[this.displayField];
37481             }else if(this.valueNotFoundText !== undefined){
37482                 text = this.valueNotFoundText;
37483             }
37484         }
37485         this.lastSelectionText = text;
37486         if(this.hiddenField){
37487             this.hiddenField.value = v;
37488         }
37489         Roo.form.ComboBox.superclass.setValue.call(this, text);
37490         this.value = v;
37491     },
37492     /**
37493      * @property {Object} the last set data for the element
37494      */
37495     
37496     lastData : false,
37497     /**
37498      * Sets the value of the field based on a object which is related to the record format for the store.
37499      * @param {Object} value the value to set as. or false on reset?
37500      */
37501     setFromData : function(o){
37502         var dv = ''; // display value
37503         var vv = ''; // value value..
37504         this.lastData = o;
37505         if (this.displayField) {
37506             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37507         } else {
37508             // this is an error condition!!!
37509             console.log('no value field set for '+ this.name);
37510         }
37511         
37512         if(this.valueField){
37513             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37514         }
37515         if(this.hiddenField){
37516             this.hiddenField.value = vv;
37517             
37518             this.lastSelectionText = dv;
37519             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37520             this.value = vv;
37521             return;
37522         }
37523         // no hidden field.. - we store the value in 'value', but still display
37524         // display field!!!!
37525         this.lastSelectionText = dv;
37526         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37527         this.value = vv;
37528         
37529         
37530     },
37531     // private
37532     reset : function(){
37533         // overridden so that last data is reset..
37534         this.setValue(this.originalValue);
37535         this.clearInvalid();
37536         this.lastData = false;
37537     },
37538     // private
37539     findRecord : function(prop, value){
37540         var record;
37541         if(this.store.getCount() > 0){
37542             this.store.each(function(r){
37543                 if(r.data[prop] == value){
37544                     record = r;
37545                     return false;
37546                 }
37547             });
37548         }
37549         return record;
37550     },
37551
37552     // private
37553     onViewMove : function(e, t){
37554         this.inKeyMode = false;
37555     },
37556
37557     // private
37558     onViewOver : function(e, t){
37559         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37560             return;
37561         }
37562         var item = this.view.findItemFromChild(t);
37563         if(item){
37564             var index = this.view.indexOf(item);
37565             this.select(index, false);
37566         }
37567     },
37568
37569     // private
37570     onViewClick : function(doFocus){
37571         var index = this.view.getSelectedIndexes()[0];
37572         var r = this.store.getAt(index);
37573         if(r){
37574             this.onSelect(r, index);
37575         }
37576         if(doFocus !== false && !this.blockFocus){
37577             this.el.focus();
37578         }
37579     },
37580
37581     // private
37582     restrictHeight : function(){
37583         this.innerList.dom.style.height = '';
37584         var inner = this.innerList.dom;
37585         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37586         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37587         this.list.beginUpdate();
37588         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37589         this.list.alignTo(this.el, this.listAlign);
37590         this.list.endUpdate();
37591     },
37592
37593     // private
37594     onEmptyResults : function(){
37595         this.collapse();
37596     },
37597
37598     /**
37599      * Returns true if the dropdown list is expanded, else false.
37600      */
37601     isExpanded : function(){
37602         return this.list.isVisible();
37603     },
37604
37605     /**
37606      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37607      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37608      * @param {String} value The data value of the item to select
37609      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37610      * selected item if it is not currently in view (defaults to true)
37611      * @return {Boolean} True if the value matched an item in the list, else false
37612      */
37613     selectByValue : function(v, scrollIntoView){
37614         if(v !== undefined && v !== null){
37615             var r = this.findRecord(this.valueField || this.displayField, v);
37616             if(r){
37617                 this.select(this.store.indexOf(r), scrollIntoView);
37618                 return true;
37619             }
37620         }
37621         return false;
37622     },
37623
37624     /**
37625      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37626      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37627      * @param {Number} index The zero-based index of the list item to select
37628      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37629      * selected item if it is not currently in view (defaults to true)
37630      */
37631     select : function(index, scrollIntoView){
37632         this.selectedIndex = index;
37633         this.view.select(index);
37634         if(scrollIntoView !== false){
37635             var el = this.view.getNode(index);
37636             if(el){
37637                 this.innerList.scrollChildIntoView(el, false);
37638             }
37639         }
37640     },
37641
37642     // private
37643     selectNext : function(){
37644         var ct = this.store.getCount();
37645         if(ct > 0){
37646             if(this.selectedIndex == -1){
37647                 this.select(0);
37648             }else if(this.selectedIndex < ct-1){
37649                 this.select(this.selectedIndex+1);
37650             }
37651         }
37652     },
37653
37654     // private
37655     selectPrev : function(){
37656         var ct = this.store.getCount();
37657         if(ct > 0){
37658             if(this.selectedIndex == -1){
37659                 this.select(0);
37660             }else if(this.selectedIndex != 0){
37661                 this.select(this.selectedIndex-1);
37662             }
37663         }
37664     },
37665
37666     // private
37667     onKeyUp : function(e){
37668         if(this.editable !== false && !e.isSpecialKey()){
37669             this.lastKey = e.getKey();
37670             this.dqTask.delay(this.queryDelay);
37671         }
37672     },
37673
37674     // private
37675     validateBlur : function(){
37676         return !this.list || !this.list.isVisible();   
37677     },
37678
37679     // private
37680     initQuery : function(){
37681         this.doQuery(this.getRawValue());
37682     },
37683
37684     // private
37685     doForce : function(){
37686         if(this.el.dom.value.length > 0){
37687             this.el.dom.value =
37688                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37689             this.applyEmptyText();
37690         }
37691     },
37692
37693     /**
37694      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37695      * query allowing the query action to be canceled if needed.
37696      * @param {String} query The SQL query to execute
37697      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37698      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37699      * saved in the current store (defaults to false)
37700      */
37701     doQuery : function(q, forceAll){
37702         if(q === undefined || q === null){
37703             q = '';
37704         }
37705         var qe = {
37706             query: q,
37707             forceAll: forceAll,
37708             combo: this,
37709             cancel:false
37710         };
37711         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37712             return false;
37713         }
37714         q = qe.query;
37715         forceAll = qe.forceAll;
37716         if(forceAll === true || (q.length >= this.minChars)){
37717             if(this.lastQuery != q){
37718                 this.lastQuery = q;
37719                 if(this.mode == 'local'){
37720                     this.selectedIndex = -1;
37721                     if(forceAll){
37722                         this.store.clearFilter();
37723                     }else{
37724                         this.store.filter(this.displayField, q);
37725                     }
37726                     this.onLoad();
37727                 }else{
37728                     this.store.baseParams[this.queryParam] = q;
37729                     this.store.load({
37730                         params: this.getParams(q)
37731                     });
37732                     this.expand();
37733                 }
37734             }else{
37735                 this.selectedIndex = -1;
37736                 this.onLoad();   
37737             }
37738         }
37739     },
37740
37741     // private
37742     getParams : function(q){
37743         var p = {};
37744         //p[this.queryParam] = q;
37745         if(this.pageSize){
37746             p.start = 0;
37747             p.limit = this.pageSize;
37748         }
37749         return p;
37750     },
37751
37752     /**
37753      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37754      */
37755     collapse : function(){
37756         if(!this.isExpanded()){
37757             return;
37758         }
37759         this.list.hide();
37760         Roo.get(document).un('mousedown', this.collapseIf, this);
37761         Roo.get(document).un('mousewheel', this.collapseIf, this);
37762         if (!this.editable) {
37763             Roo.get(document).un('keydown', this.listKeyPress, this);
37764         }
37765         this.fireEvent('collapse', this);
37766     },
37767
37768     // private
37769     collapseIf : function(e){
37770         if(!e.within(this.wrap) && !e.within(this.list)){
37771             this.collapse();
37772         }
37773     },
37774
37775     /**
37776      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37777      */
37778     expand : function(){
37779         if(this.isExpanded() || !this.hasFocus){
37780             return;
37781         }
37782         this.list.alignTo(this.el, this.listAlign);
37783         this.list.show();
37784         Roo.get(document).on('mousedown', this.collapseIf, this);
37785         Roo.get(document).on('mousewheel', this.collapseIf, this);
37786         if (!this.editable) {
37787             Roo.get(document).on('keydown', this.listKeyPress, this);
37788         }
37789         
37790         this.fireEvent('expand', this);
37791     },
37792
37793     // private
37794     // Implements the default empty TriggerField.onTriggerClick function
37795     onTriggerClick : function(){
37796         if(this.disabled){
37797             return;
37798         }
37799         if(this.isExpanded()){
37800             this.collapse();
37801             if (!this.blockFocus) {
37802                 this.el.focus();
37803             }
37804             
37805         }else {
37806             this.hasFocus = true;
37807             if(this.triggerAction == 'all') {
37808                 this.doQuery(this.allQuery, true);
37809             } else {
37810                 this.doQuery(this.getRawValue());
37811             }
37812             if (!this.blockFocus) {
37813                 this.el.focus();
37814             }
37815         }
37816     },
37817     listKeyPress : function(e)
37818     {
37819         //Roo.log('listkeypress');
37820         // scroll to first matching element based on key pres..
37821         if (e.isSpecialKey()) {
37822             return false;
37823         }
37824         var k = String.fromCharCode(e.getKey()).toUpperCase();
37825         //Roo.log(k);
37826         var match  = false;
37827         var csel = this.view.getSelectedNodes();
37828         var cselitem = false;
37829         if (csel.length) {
37830             var ix = this.view.indexOf(csel[0]);
37831             var cselitem  = this.store.getAt(ix);
37832             
37833             
37834         }
37835         
37836         this.store.each(function(v) { 
37837             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
37838                 if (cselitem && cselitem.id = v.id) {
37839                     // if it's the currently selected ignore..
37840                     return;
37841                 }
37842                 match = this.store.indexOf(v);
37843                 return false;
37844             }
37845         }, this);
37846         
37847         if (match === false) {
37848             return true; // no more action?
37849         }
37850         // scroll to?
37851         this.view.select(match);
37852         var sn = Roo.get(this.view.getSelectedNodes()[0])
37853         sn.scrollIntoView(sn.dom.parentNode, false);
37854     }
37855
37856     /** 
37857     * @cfg {Boolean} grow 
37858     * @hide 
37859     */
37860     /** 
37861     * @cfg {Number} growMin 
37862     * @hide 
37863     */
37864     /** 
37865     * @cfg {Number} growMax 
37866     * @hide 
37867     */
37868     /**
37869      * @hide
37870      * @method autoSize
37871      */
37872 });/*
37873  * Based on:
37874  * Ext JS Library 1.1.1
37875  * Copyright(c) 2006-2007, Ext JS, LLC.
37876  *
37877  * Originally Released Under LGPL - original licence link has changed is not relivant.
37878  *
37879  * Fork - LGPL
37880  * <script type="text/javascript">
37881  */
37882 /**
37883  * @class Roo.form.Checkbox
37884  * @extends Roo.form.Field
37885  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37886  * @constructor
37887  * Creates a new Checkbox
37888  * @param {Object} config Configuration options
37889  */
37890 Roo.form.Checkbox = function(config){
37891     Roo.form.Checkbox.superclass.constructor.call(this, config);
37892     this.addEvents({
37893         /**
37894          * @event check
37895          * Fires when the checkbox is checked or unchecked.
37896              * @param {Roo.form.Checkbox} this This checkbox
37897              * @param {Boolean} checked The new checked value
37898              */
37899         check : true
37900     });
37901 };
37902
37903 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37904     /**
37905      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37906      */
37907     focusClass : undefined,
37908     /**
37909      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37910      */
37911     fieldClass: "x-form-field",
37912     /**
37913      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37914      */
37915     checked: false,
37916     /**
37917      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37918      * {tag: "input", type: "checkbox", autocomplete: "off"})
37919      */
37920     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37921     /**
37922      * @cfg {String} boxLabel The text that appears beside the checkbox
37923      */
37924     boxLabel : "",
37925     /**
37926      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37927      */  
37928     inputValue : '1',
37929     /**
37930      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37931      */
37932      valueOff: '0', // value when not checked..
37933
37934     actionMode : 'viewEl', 
37935     //
37936     // private
37937     itemCls : 'x-menu-check-item x-form-item',
37938     groupClass : 'x-menu-group-item',
37939     inputType : 'hidden',
37940     
37941     
37942     inSetChecked: false, // check that we are not calling self...
37943     
37944     inputElement: false, // real input element?
37945     basedOn: false, // ????
37946     
37947     isFormField: true, // not sure where this is needed!!!!
37948
37949     onResize : function(){
37950         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37951         if(!this.boxLabel){
37952             this.el.alignTo(this.wrap, 'c-c');
37953         }
37954     },
37955
37956     initEvents : function(){
37957         Roo.form.Checkbox.superclass.initEvents.call(this);
37958         this.el.on("click", this.onClick,  this);
37959         this.el.on("change", this.onClick,  this);
37960     },
37961
37962
37963     getResizeEl : function(){
37964         return this.wrap;
37965     },
37966
37967     getPositionEl : function(){
37968         return this.wrap;
37969     },
37970
37971     // private
37972     onRender : function(ct, position){
37973         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37974         /*
37975         if(this.inputValue !== undefined){
37976             this.el.dom.value = this.inputValue;
37977         }
37978         */
37979         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37980         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37981         var viewEl = this.wrap.createChild({ 
37982             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37983         this.viewEl = viewEl;   
37984         this.wrap.on('click', this.onClick,  this); 
37985         
37986         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37987         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37988         
37989         
37990         
37991         if(this.boxLabel){
37992             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37993         //    viewEl.on('click', this.onClick,  this); 
37994         }
37995         //if(this.checked){
37996             this.setChecked(this.checked);
37997         //}else{
37998             //this.checked = this.el.dom;
37999         //}
38000
38001     },
38002
38003     // private
38004     initValue : Roo.emptyFn,
38005
38006     /**
38007      * Returns the checked state of the checkbox.
38008      * @return {Boolean} True if checked, else false
38009      */
38010     getValue : function(){
38011         if(this.el){
38012             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38013         }
38014         return this.valueOff;
38015         
38016     },
38017
38018         // private
38019     onClick : function(){ 
38020         this.setChecked(!this.checked);
38021
38022         //if(this.el.dom.checked != this.checked){
38023         //    this.setValue(this.el.dom.checked);
38024        // }
38025     },
38026
38027     /**
38028      * Sets the checked state of the checkbox.
38029      * On is always based on a string comparison between inputValue and the param.
38030      * @param {Boolean/String} value - the value to set 
38031      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38032      */
38033     setValue : function(v,suppressEvent){
38034         
38035         
38036         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38037         //if(this.el && this.el.dom){
38038         //    this.el.dom.checked = this.checked;
38039         //    this.el.dom.defaultChecked = this.checked;
38040         //}
38041         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38042         //this.fireEvent("check", this, this.checked);
38043     },
38044     // private..
38045     setChecked : function(state,suppressEvent)
38046     {
38047         if (this.inSetChecked) {
38048             this.checked = state;
38049             return;
38050         }
38051         
38052     
38053         if(this.wrap){
38054             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38055         }
38056         this.checked = state;
38057         if(suppressEvent !== true){
38058             this.fireEvent('check', this, state);
38059         }
38060         this.inSetChecked = true;
38061         this.el.dom.value = state ? this.inputValue : this.valueOff;
38062         this.inSetChecked = false;
38063         
38064     },
38065     // handle setting of hidden value by some other method!!?!?
38066     setFromHidden: function()
38067     {
38068         if(!this.el){
38069             return;
38070         }
38071         //console.log("SET FROM HIDDEN");
38072         //alert('setFrom hidden');
38073         this.setValue(this.el.dom.value);
38074     },
38075     
38076     onDestroy : function()
38077     {
38078         if(this.viewEl){
38079             Roo.get(this.viewEl).remove();
38080         }
38081          
38082         Roo.form.Checkbox.superclass.onDestroy.call(this);
38083     }
38084
38085 });/*
38086  * Based on:
38087  * Ext JS Library 1.1.1
38088  * Copyright(c) 2006-2007, Ext JS, LLC.
38089  *
38090  * Originally Released Under LGPL - original licence link has changed is not relivant.
38091  *
38092  * Fork - LGPL
38093  * <script type="text/javascript">
38094  */
38095  
38096 /**
38097  * @class Roo.form.Radio
38098  * @extends Roo.form.Checkbox
38099  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38100  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38101  * @constructor
38102  * Creates a new Radio
38103  * @param {Object} config Configuration options
38104  */
38105 Roo.form.Radio = function(){
38106     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38107 };
38108 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38109     inputType: 'radio',
38110
38111     /**
38112      * If this radio is part of a group, it will return the selected value
38113      * @return {String}
38114      */
38115     getGroupValue : function(){
38116         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38117     }
38118 });//<script type="text/javascript">
38119
38120 /*
38121  * Ext JS Library 1.1.1
38122  * Copyright(c) 2006-2007, Ext JS, LLC.
38123  * licensing@extjs.com
38124  * 
38125  * http://www.extjs.com/license
38126  */
38127  
38128  /*
38129   * 
38130   * Known bugs:
38131   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38132   * - IE ? - no idea how much works there.
38133   * 
38134   * 
38135   * 
38136   */
38137  
38138
38139 /**
38140  * @class Ext.form.HtmlEditor
38141  * @extends Ext.form.Field
38142  * Provides a lightweight HTML Editor component.
38143  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38144  * 
38145  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38146  * supported by this editor.</b><br/><br/>
38147  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38148  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38149  */
38150 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38151       /**
38152      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38153      */
38154     toolbars : false,
38155     /**
38156      * @cfg {String} createLinkText The default text for the create link prompt
38157      */
38158     createLinkText : 'Please enter the URL for the link:',
38159     /**
38160      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38161      */
38162     defaultLinkValue : 'http:/'+'/',
38163    
38164     
38165     // id of frame..
38166     frameId: false,
38167     
38168     // private properties
38169     validationEvent : false,
38170     deferHeight: true,
38171     initialized : false,
38172     activated : false,
38173     sourceEditMode : false,
38174     onFocus : Roo.emptyFn,
38175     iframePad:3,
38176     hideMode:'offsets',
38177     defaultAutoCreate : {
38178         tag: "textarea",
38179         style:"width:500px;height:300px;",
38180         autocomplete: "off"
38181     },
38182
38183     // private
38184     initComponent : function(){
38185         this.addEvents({
38186             /**
38187              * @event initialize
38188              * Fires when the editor is fully initialized (including the iframe)
38189              * @param {HtmlEditor} this
38190              */
38191             initialize: true,
38192             /**
38193              * @event activate
38194              * Fires when the editor is first receives the focus. Any insertion must wait
38195              * until after this event.
38196              * @param {HtmlEditor} this
38197              */
38198             activate: true,
38199              /**
38200              * @event beforesync
38201              * Fires before the textarea is updated with content from the editor iframe. Return false
38202              * to cancel the sync.
38203              * @param {HtmlEditor} this
38204              * @param {String} html
38205              */
38206             beforesync: true,
38207              /**
38208              * @event beforepush
38209              * Fires before the iframe editor is updated with content from the textarea. Return false
38210              * to cancel the push.
38211              * @param {HtmlEditor} this
38212              * @param {String} html
38213              */
38214             beforepush: true,
38215              /**
38216              * @event sync
38217              * Fires when the textarea is updated with content from the editor iframe.
38218              * @param {HtmlEditor} this
38219              * @param {String} html
38220              */
38221             sync: true,
38222              /**
38223              * @event push
38224              * Fires when the iframe editor is updated with content from the textarea.
38225              * @param {HtmlEditor} this
38226              * @param {String} html
38227              */
38228             push: true,
38229              /**
38230              * @event editmodechange
38231              * Fires when the editor switches edit modes
38232              * @param {HtmlEditor} this
38233              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38234              */
38235             editmodechange: true,
38236             /**
38237              * @event editorevent
38238              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38239              * @param {HtmlEditor} this
38240              */
38241             editorevent: true
38242         })
38243     },
38244
38245     /**
38246      * Protected method that will not generally be called directly. It
38247      * is called when the editor creates its toolbar. Override this method if you need to
38248      * add custom toolbar buttons.
38249      * @param {HtmlEditor} editor
38250      */
38251     createToolbar : function(editor){
38252         if (!editor.toolbars || !editor.toolbars.length) {
38253             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38254         }
38255         
38256         for (var i =0 ; i < editor.toolbars.length;i++) {
38257             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38258             editor.toolbars[i].init(editor);
38259         }
38260          
38261         
38262     },
38263
38264     /**
38265      * Protected method that will not generally be called directly. It
38266      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38267      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38268      */
38269     getDocMarkup : function(){
38270         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38271     },
38272
38273     // private
38274     onRender : function(ct, position){
38275         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38276         this.el.dom.style.border = '0 none';
38277         this.el.dom.setAttribute('tabIndex', -1);
38278         this.el.addClass('x-hidden');
38279         if(Roo.isIE){ // fix IE 1px bogus margin
38280             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38281         }
38282         this.wrap = this.el.wrap({
38283             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38284         });
38285
38286         this.frameId = Roo.id();
38287         this.createToolbar(this);
38288         
38289         
38290         
38291         
38292       
38293         
38294         var iframe = this.wrap.createChild({
38295             tag: 'iframe',
38296             id: this.frameId,
38297             name: this.frameId,
38298             frameBorder : 'no',
38299             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38300         });
38301         
38302        // console.log(iframe);
38303         //this.wrap.dom.appendChild(iframe);
38304
38305         this.iframe = iframe.dom;
38306
38307          this.assignDocWin();
38308         
38309         this.doc.designMode = 'on';
38310        
38311         this.doc.open();
38312         this.doc.write(this.getDocMarkup());
38313         this.doc.close();
38314
38315         
38316         var task = { // must defer to wait for browser to be ready
38317             run : function(){
38318                 //console.log("run task?" + this.doc.readyState);
38319                 this.assignDocWin();
38320                 if(this.doc.body || this.doc.readyState == 'complete'){
38321                     try {
38322                         this.doc.designMode="on";
38323                     } catch (e) {
38324                         return;
38325                     }
38326                     Roo.TaskMgr.stop(task);
38327                     this.initEditor.defer(10, this);
38328                 }
38329             },
38330             interval : 10,
38331             duration:10000,
38332             scope: this
38333         };
38334         Roo.TaskMgr.start(task);
38335
38336         if(!this.width){
38337             this.setSize(this.el.getSize());
38338         }
38339     },
38340
38341     // private
38342     onResize : function(w, h){
38343         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38344         if(this.el && this.iframe){
38345             if(typeof w == 'number'){
38346                 var aw = w - this.wrap.getFrameWidth('lr');
38347                 this.el.setWidth(this.adjustWidth('textarea', aw));
38348                 this.iframe.style.width = aw + 'px';
38349             }
38350             if(typeof h == 'number'){
38351                 var tbh = 0;
38352                 for (var i =0; i < this.toolbars.length;i++) {
38353                     // fixme - ask toolbars for heights?
38354                     tbh += this.toolbars[i].tb.el.getHeight();
38355                 }
38356                 
38357                 
38358                 
38359                 
38360                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38361                 this.el.setHeight(this.adjustWidth('textarea', ah));
38362                 this.iframe.style.height = ah + 'px';
38363                 if(this.doc){
38364                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38365                 }
38366             }
38367         }
38368     },
38369
38370     /**
38371      * Toggles the editor between standard and source edit mode.
38372      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38373      */
38374     toggleSourceEdit : function(sourceEditMode){
38375         
38376         this.sourceEditMode = sourceEditMode === true;
38377         
38378         if(this.sourceEditMode){
38379           
38380             this.syncValue();
38381             this.iframe.className = 'x-hidden';
38382             this.el.removeClass('x-hidden');
38383             this.el.dom.removeAttribute('tabIndex');
38384             this.el.focus();
38385         }else{
38386              
38387             this.pushValue();
38388             this.iframe.className = '';
38389             this.el.addClass('x-hidden');
38390             this.el.dom.setAttribute('tabIndex', -1);
38391             this.deferFocus();
38392         }
38393         this.setSize(this.wrap.getSize());
38394         this.fireEvent('editmodechange', this, this.sourceEditMode);
38395     },
38396
38397     // private used internally
38398     createLink : function(){
38399         var url = prompt(this.createLinkText, this.defaultLinkValue);
38400         if(url && url != 'http:/'+'/'){
38401             this.relayCmd('createlink', url);
38402         }
38403     },
38404
38405     // private (for BoxComponent)
38406     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38407
38408     // private (for BoxComponent)
38409     getResizeEl : function(){
38410         return this.wrap;
38411     },
38412
38413     // private (for BoxComponent)
38414     getPositionEl : function(){
38415         return this.wrap;
38416     },
38417
38418     // private
38419     initEvents : function(){
38420         this.originalValue = this.getValue();
38421     },
38422
38423     /**
38424      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38425      * @method
38426      */
38427     markInvalid : Roo.emptyFn,
38428     /**
38429      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38430      * @method
38431      */
38432     clearInvalid : Roo.emptyFn,
38433
38434     setValue : function(v){
38435         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38436         this.pushValue();
38437     },
38438
38439     /**
38440      * Protected method that will not generally be called directly. If you need/want
38441      * custom HTML cleanup, this is the method you should override.
38442      * @param {String} html The HTML to be cleaned
38443      * return {String} The cleaned HTML
38444      */
38445     cleanHtml : function(html){
38446         html = String(html);
38447         if(html.length > 5){
38448             if(Roo.isSafari){ // strip safari nonsense
38449                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38450             }
38451         }
38452         if(html == '&nbsp;'){
38453             html = '';
38454         }
38455         return html;
38456     },
38457
38458     /**
38459      * Protected method that will not generally be called directly. Syncs the contents
38460      * of the editor iframe with the textarea.
38461      */
38462     syncValue : function(){
38463         if(this.initialized){
38464             var bd = (this.doc.body || this.doc.documentElement);
38465             var html = bd.innerHTML;
38466             if(Roo.isSafari){
38467                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38468                 var m = bs.match(/text-align:(.*?);/i);
38469                 if(m && m[1]){
38470                     html = '<div style="'+m[0]+'">' + html + '</div>';
38471                 }
38472             }
38473             html = this.cleanHtml(html);
38474             if(this.fireEvent('beforesync', this, html) !== false){
38475                 this.el.dom.value = html;
38476                 this.fireEvent('sync', this, html);
38477             }
38478         }
38479     },
38480
38481     /**
38482      * Protected method that will not generally be called directly. Pushes the value of the textarea
38483      * into the iframe editor.
38484      */
38485     pushValue : function(){
38486         if(this.initialized){
38487             var v = this.el.dom.value;
38488             if(v.length < 1){
38489                 v = '&#160;';
38490             }
38491             if(this.fireEvent('beforepush', this, v) !== false){
38492                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38493                 this.fireEvent('push', this, v);
38494             }
38495         }
38496     },
38497
38498     // private
38499     deferFocus : function(){
38500         this.focus.defer(10, this);
38501     },
38502
38503     // doc'ed in Field
38504     focus : function(){
38505         if(this.win && !this.sourceEditMode){
38506             this.win.focus();
38507         }else{
38508             this.el.focus();
38509         }
38510     },
38511     
38512     assignDocWin: function()
38513     {
38514         var iframe = this.iframe;
38515         
38516          if(Roo.isIE){
38517             this.doc = iframe.contentWindow.document;
38518             this.win = iframe.contentWindow;
38519         } else {
38520             if (!Roo.get(this.frameId)) {
38521                 return;
38522             }
38523             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38524             this.win = Roo.get(this.frameId).dom.contentWindow;
38525         }
38526     },
38527     
38528     // private
38529     initEditor : function(){
38530         //console.log("INIT EDITOR");
38531         this.assignDocWin();
38532         
38533         
38534         
38535         this.doc.designMode="on";
38536         this.doc.open();
38537         this.doc.write(this.getDocMarkup());
38538         this.doc.close();
38539         
38540         var dbody = (this.doc.body || this.doc.documentElement);
38541         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38542         // this copies styles from the containing element into thsi one..
38543         // not sure why we need all of this..
38544         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38545         ss['background-attachment'] = 'fixed'; // w3c
38546         dbody.bgProperties = 'fixed'; // ie
38547         Roo.DomHelper.applyStyles(dbody, ss);
38548         Roo.EventManager.on(this.doc, {
38549             'mousedown': this.onEditorEvent,
38550             'dblclick': this.onEditorEvent,
38551             'click': this.onEditorEvent,
38552             'keyup': this.onEditorEvent,
38553             buffer:100,
38554             scope: this
38555         });
38556         if(Roo.isGecko){
38557             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38558         }
38559         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38560             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38561         }
38562         this.initialized = true;
38563
38564         this.fireEvent('initialize', this);
38565         this.pushValue();
38566     },
38567
38568     // private
38569     onDestroy : function(){
38570         
38571         
38572         
38573         if(this.rendered){
38574             
38575             for (var i =0; i < this.toolbars.length;i++) {
38576                 // fixme - ask toolbars for heights?
38577                 this.toolbars[i].onDestroy();
38578             }
38579             
38580             this.wrap.dom.innerHTML = '';
38581             this.wrap.remove();
38582         }
38583     },
38584
38585     // private
38586     onFirstFocus : function(){
38587         
38588         this.assignDocWin();
38589         
38590         
38591         this.activated = true;
38592         for (var i =0; i < this.toolbars.length;i++) {
38593             this.toolbars[i].onFirstFocus();
38594         }
38595        
38596         if(Roo.isGecko){ // prevent silly gecko errors
38597             this.win.focus();
38598             var s = this.win.getSelection();
38599             if(!s.focusNode || s.focusNode.nodeType != 3){
38600                 var r = s.getRangeAt(0);
38601                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38602                 r.collapse(true);
38603                 this.deferFocus();
38604             }
38605             try{
38606                 this.execCmd('useCSS', true);
38607                 this.execCmd('styleWithCSS', false);
38608             }catch(e){}
38609         }
38610         this.fireEvent('activate', this);
38611     },
38612
38613     // private
38614     adjustFont: function(btn){
38615         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38616         //if(Roo.isSafari){ // safari
38617         //    adjust *= 2;
38618        // }
38619         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38620         if(Roo.isSafari){ // safari
38621             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38622             v =  (v < 10) ? 10 : v;
38623             v =  (v > 48) ? 48 : v;
38624             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38625             
38626         }
38627         
38628         
38629         v = Math.max(1, v+adjust);
38630         
38631         this.execCmd('FontSize', v  );
38632     },
38633
38634     onEditorEvent : function(e){
38635         this.fireEvent('editorevent', this, e);
38636       //  this.updateToolbar();
38637         this.syncValue();
38638     },
38639
38640     insertTag : function(tg)
38641     {
38642         // could be a bit smarter... -> wrap the current selected tRoo..
38643         
38644         this.execCmd("formatblock",   tg);
38645         
38646     },
38647     
38648     insertText : function(txt)
38649     {
38650         
38651         
38652         range = this.createRange();
38653         range.deleteContents();
38654                //alert(Sender.getAttribute('label'));
38655                
38656         range.insertNode(this.doc.createTextNode(txt));
38657     } ,
38658     
38659     // private
38660     relayBtnCmd : function(btn){
38661         this.relayCmd(btn.cmd);
38662     },
38663
38664     /**
38665      * Executes a Midas editor command on the editor document and performs necessary focus and
38666      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38667      * @param {String} cmd The Midas command
38668      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38669      */
38670     relayCmd : function(cmd, value){
38671         this.win.focus();
38672         this.execCmd(cmd, value);
38673         this.fireEvent('editorevent', this);
38674         //this.updateToolbar();
38675         this.deferFocus();
38676     },
38677
38678     /**
38679      * Executes a Midas editor command directly on the editor document.
38680      * For visual commands, you should use {@link #relayCmd} instead.
38681      * <b>This should only be called after the editor is initialized.</b>
38682      * @param {String} cmd The Midas command
38683      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38684      */
38685     execCmd : function(cmd, value){
38686         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38687         this.syncValue();
38688     },
38689
38690    
38691     /**
38692      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38693      * to insert tRoo.
38694      * @param {String} text
38695      */
38696     insertAtCursor : function(text){
38697         if(!this.activated){
38698             return;
38699         }
38700         if(Roo.isIE){
38701             this.win.focus();
38702             var r = this.doc.selection.createRange();
38703             if(r){
38704                 r.collapse(true);
38705                 r.pasteHTML(text);
38706                 this.syncValue();
38707                 this.deferFocus();
38708             }
38709         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38710             this.win.focus();
38711             this.execCmd('InsertHTML', text);
38712             this.deferFocus();
38713         }
38714     },
38715  // private
38716     mozKeyPress : function(e){
38717         if(e.ctrlKey){
38718             var c = e.getCharCode(), cmd;
38719           
38720             if(c > 0){
38721                 c = String.fromCharCode(c).toLowerCase();
38722                 switch(c){
38723                     case 'b':
38724                         cmd = 'bold';
38725                     break;
38726                     case 'i':
38727                         cmd = 'italic';
38728                     break;
38729                     case 'u':
38730                         cmd = 'underline';
38731                     case 'v':
38732                         this.cleanUpPaste.defer(100, this);
38733                         return;
38734                     break;
38735                 }
38736                 if(cmd){
38737                     this.win.focus();
38738                     this.execCmd(cmd);
38739                     this.deferFocus();
38740                     e.preventDefault();
38741                 }
38742                 
38743             }
38744         }
38745     },
38746
38747     // private
38748     fixKeys : function(){ // load time branching for fastest keydown performance
38749         if(Roo.isIE){
38750             return function(e){
38751                 var k = e.getKey(), r;
38752                 if(k == e.TAB){
38753                     e.stopEvent();
38754                     r = this.doc.selection.createRange();
38755                     if(r){
38756                         r.collapse(true);
38757                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38758                         this.deferFocus();
38759                     }
38760                     return;
38761                 }
38762                 
38763                 if(k == e.ENTER){
38764                     r = this.doc.selection.createRange();
38765                     if(r){
38766                         var target = r.parentElement();
38767                         if(!target || target.tagName.toLowerCase() != 'li'){
38768                             e.stopEvent();
38769                             r.pasteHTML('<br />');
38770                             r.collapse(false);
38771                             r.select();
38772                         }
38773                     }
38774                 }
38775                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38776                     this.cleanUpPaste.defer(100, this);
38777                     return;
38778                 }
38779                 
38780                 
38781             };
38782         }else if(Roo.isOpera){
38783             return function(e){
38784                 var k = e.getKey();
38785                 if(k == e.TAB){
38786                     e.stopEvent();
38787                     this.win.focus();
38788                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38789                     this.deferFocus();
38790                 }
38791                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38792                     this.cleanUpPaste.defer(100, this);
38793                     return;
38794                 }
38795                 
38796             };
38797         }else if(Roo.isSafari){
38798             return function(e){
38799                 var k = e.getKey();
38800                 
38801                 if(k == e.TAB){
38802                     e.stopEvent();
38803                     this.execCmd('InsertText','\t');
38804                     this.deferFocus();
38805                     return;
38806                 }
38807                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38808                     this.cleanUpPaste.defer(100, this);
38809                     return;
38810                 }
38811                 
38812              };
38813         }
38814     }(),
38815     
38816     getAllAncestors: function()
38817     {
38818         var p = this.getSelectedNode();
38819         var a = [];
38820         if (!p) {
38821             a.push(p); // push blank onto stack..
38822             p = this.getParentElement();
38823         }
38824         
38825         
38826         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38827             a.push(p);
38828             p = p.parentNode;
38829         }
38830         a.push(this.doc.body);
38831         return a;
38832     },
38833     lastSel : false,
38834     lastSelNode : false,
38835     
38836     
38837     getSelection : function() 
38838     {
38839         this.assignDocWin();
38840         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38841     },
38842     
38843     getSelectedNode: function() 
38844     {
38845         // this may only work on Gecko!!!
38846         
38847         // should we cache this!!!!
38848         
38849         
38850         
38851          
38852         var range = this.createRange(this.getSelection());
38853         
38854         if (Roo.isIE) {
38855             var parent = range.parentElement();
38856             while (true) {
38857                 var testRange = range.duplicate();
38858                 testRange.moveToElementText(parent);
38859                 if (testRange.inRange(range)) {
38860                     break;
38861                 }
38862                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38863                     break;
38864                 }
38865                 parent = parent.parentElement;
38866             }
38867             return parent;
38868         }
38869         
38870         
38871         var ar = range.endContainer.childNodes;
38872         if (!ar.length) {
38873             ar = range.commonAncestorContainer.childNodes;
38874             //alert(ar.length);
38875         }
38876         var nodes = [];
38877         var other_nodes = [];
38878         var has_other_nodes = false;
38879         for (var i=0;i<ar.length;i++) {
38880             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38881                 continue;
38882             }
38883             // fullly contained node.
38884             
38885             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38886                 nodes.push(ar[i]);
38887                 continue;
38888             }
38889             
38890             // probably selected..
38891             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38892                 other_nodes.push(ar[i]);
38893                 continue;
38894             }
38895             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38896                 continue;
38897             }
38898             
38899             
38900             has_other_nodes = true;
38901         }
38902         if (!nodes.length && other_nodes.length) {
38903             nodes= other_nodes;
38904         }
38905         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38906             return false;
38907         }
38908         
38909         return nodes[0];
38910     },
38911     createRange: function(sel)
38912     {
38913         // this has strange effects when using with 
38914         // top toolbar - not sure if it's a great idea.
38915         //this.editor.contentWindow.focus();
38916         if (typeof sel != "undefined") {
38917             try {
38918                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38919             } catch(e) {
38920                 return this.doc.createRange();
38921             }
38922         } else {
38923             return this.doc.createRange();
38924         }
38925     },
38926     getParentElement: function()
38927     {
38928         
38929         this.assignDocWin();
38930         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38931         
38932         var range = this.createRange(sel);
38933          
38934         try {
38935             var p = range.commonAncestorContainer;
38936             while (p.nodeType == 3) { // text node
38937                 p = p.parentNode;
38938             }
38939             return p;
38940         } catch (e) {
38941             return null;
38942         }
38943     
38944     },
38945     
38946     
38947     
38948     // BC Hacks - cause I cant work out what i was trying to do..
38949     rangeIntersectsNode : function(range, node)
38950     {
38951         var nodeRange = node.ownerDocument.createRange();
38952         try {
38953             nodeRange.selectNode(node);
38954         }
38955         catch (e) {
38956             nodeRange.selectNodeContents(node);
38957         }
38958
38959         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38960                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38961     },
38962     rangeCompareNode : function(range, node) {
38963         var nodeRange = node.ownerDocument.createRange();
38964         try {
38965             nodeRange.selectNode(node);
38966         } catch (e) {
38967             nodeRange.selectNodeContents(node);
38968         }
38969         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38970         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38971
38972         if (nodeIsBefore && !nodeIsAfter)
38973             return 0;
38974         if (!nodeIsBefore && nodeIsAfter)
38975             return 1;
38976         if (nodeIsBefore && nodeIsAfter)
38977             return 2;
38978
38979         return 3;
38980     },
38981
38982     // private? - in a new class?
38983     cleanUpPaste :  function()
38984     {
38985         // cleans up the whole document..
38986       //  console.log('cleanuppaste');
38987         this.cleanUpChildren(this.doc.body)
38988         
38989         
38990     },
38991     cleanUpChildren : function (n)
38992     {
38993         if (!n.childNodes.length) {
38994             return;
38995         }
38996         for (var i = n.childNodes.length-1; i > -1 ; i--) {
38997            this.cleanUpChild(n.childNodes[i]);
38998         }
38999     },
39000     
39001     
39002         
39003     
39004     cleanUpChild : function (node)
39005     {
39006         //console.log(node);
39007         if (node.nodeName == "#text") {
39008             // clean up silly Windows -- stuff?
39009             return; 
39010         }
39011         if (node.nodeName == "#comment") {
39012             node.parentNode.removeChild(node);
39013             // clean up silly Windows -- stuff?
39014             return; 
39015         }
39016         
39017         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39018             // remove node.
39019             node.parentNode.removeChild(node);
39020             return;
39021             
39022         }
39023         if (!node.attributes || !node.attributes.length) {
39024             this.cleanUpChildren(node);
39025             return;
39026         }
39027         
39028         function cleanAttr(n,v)
39029         {
39030             
39031             if (v.match(/^\./) || v.match(/^\//)) {
39032                 return;
39033             }
39034             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39035                 return;
39036             }
39037             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39038             node.removeAttribute(n);
39039             
39040         }
39041         
39042         function cleanStyle(n,v)
39043         {
39044             if (v.match(/expression/)) { //XSS?? should we even bother..
39045                 node.removeAttribute(n);
39046                 return;
39047             }
39048             
39049             
39050             var parts = v.split(/;/);
39051             Roo.each(parts, function(p) {
39052                 p = p.replace(/\s+/g,'');
39053                 if (!p.length) {
39054                     return;
39055                 }
39056                 var l = p.split(':').shift().replace(/\s+/g,'');
39057                 
39058                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39059                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39060                     node.removeAttribute(n);
39061                     return false;
39062                 }
39063             });
39064             
39065             
39066         }
39067         
39068         
39069         for (var i = node.attributes.length-1; i > -1 ; i--) {
39070             var a = node.attributes[i];
39071             //console.log(a);
39072             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39073                 node.removeAttribute(a.name);
39074                 return;
39075             }
39076             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39077                 cleanAttr(a.name,a.value); // fixme..
39078                 return;
39079             }
39080             if (a.name == 'style') {
39081                 cleanStyle(a.name,a.value);
39082             }
39083             /// clean up MS crap..
39084             if (a.name == 'class') {
39085                 if (a.value.match(/^Mso/)) {
39086                     node.className = '';
39087                 }
39088             }
39089             
39090             // style cleanup!?
39091             // class cleanup?
39092             
39093         }
39094         
39095         
39096         this.cleanUpChildren(node);
39097         
39098         
39099     }
39100     
39101     
39102     // hide stuff that is not compatible
39103     /**
39104      * @event blur
39105      * @hide
39106      */
39107     /**
39108      * @event change
39109      * @hide
39110      */
39111     /**
39112      * @event focus
39113      * @hide
39114      */
39115     /**
39116      * @event specialkey
39117      * @hide
39118      */
39119     /**
39120      * @cfg {String} fieldClass @hide
39121      */
39122     /**
39123      * @cfg {String} focusClass @hide
39124      */
39125     /**
39126      * @cfg {String} autoCreate @hide
39127      */
39128     /**
39129      * @cfg {String} inputType @hide
39130      */
39131     /**
39132      * @cfg {String} invalidClass @hide
39133      */
39134     /**
39135      * @cfg {String} invalidText @hide
39136      */
39137     /**
39138      * @cfg {String} msgFx @hide
39139      */
39140     /**
39141      * @cfg {String} validateOnBlur @hide
39142      */
39143 });
39144
39145 Roo.form.HtmlEditor.white = [
39146         'area', 'br', 'img', 'input', 'hr', 'wbr',
39147         
39148        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39149        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39150        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39151        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39152        'table',   'ul',         'xmp', 
39153        
39154        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39155       'thead',   'tr', 
39156      
39157       'dir', 'menu', 'ol', 'ul', 'dl',
39158        
39159       'embed',  'object'
39160 ];
39161
39162
39163 Roo.form.HtmlEditor.black = [
39164     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39165         'applet', // 
39166         'base',   'basefont', 'bgsound', 'blink',  'body', 
39167         'frame',  'frameset', 'head',    'html',   'ilayer', 
39168         'iframe', 'layer',  'link',     'meta',    'object',   
39169         'script', 'style' ,'title',  'xml' // clean later..
39170 ];
39171 Roo.form.HtmlEditor.clean = [
39172     'script', 'style', 'title', 'xml'
39173 ];
39174
39175 // attributes..
39176
39177 Roo.form.HtmlEditor.ablack = [
39178     'on'
39179 ];
39180     
39181 Roo.form.HtmlEditor.aclean = [ 
39182     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39183 ];
39184
39185 // protocols..
39186 Roo.form.HtmlEditor.pwhite= [
39187         'http',  'https',  'mailto'
39188 ];
39189
39190 Roo.form.HtmlEditor.cwhite= [
39191         'text-align',
39192         'font-size'
39193 ];
39194
39195 // <script type="text/javascript">
39196 /*
39197  * Based on
39198  * Ext JS Library 1.1.1
39199  * Copyright(c) 2006-2007, Ext JS, LLC.
39200  *  
39201  
39202  */
39203
39204 /**
39205  * @class Roo.form.HtmlEditorToolbar1
39206  * Basic Toolbar
39207  * 
39208  * Usage:
39209  *
39210  new Roo.form.HtmlEditor({
39211     ....
39212     toolbars : [
39213         new Roo.form.HtmlEditorToolbar1({
39214             disable : { fonts: 1 , format: 1, ..., ... , ...],
39215             btns : [ .... ]
39216         })
39217     }
39218      
39219  * 
39220  * @cfg {Object} disable List of elements to disable..
39221  * @cfg {Array} btns List of additional buttons.
39222  * 
39223  * 
39224  * NEEDS Extra CSS? 
39225  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39226  */
39227  
39228 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39229 {
39230     
39231     Roo.apply(this, config);
39232     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39233     // dont call parent... till later.
39234 }
39235
39236 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39237     
39238     tb: false,
39239     
39240     rendered: false,
39241     
39242     editor : false,
39243     /**
39244      * @cfg {Object} disable  List of toolbar elements to disable
39245          
39246      */
39247     disable : false,
39248       /**
39249      * @cfg {Array} fontFamilies An array of available font families
39250      */
39251     fontFamilies : [
39252         'Arial',
39253         'Courier New',
39254         'Tahoma',
39255         'Times New Roman',
39256         'Verdana'
39257     ],
39258     
39259     specialChars : [
39260            "&#169;",
39261           "&#174;",     
39262           "&#8482;",    
39263           "&#163;" ,    
39264          // "&#8212;",    
39265           "&#8230;",    
39266           "&#247;" ,    
39267         //  "&#225;" ,     ?? a acute?
39268            "&#8364;"    , //Euro
39269        //   "&#8220;"    ,
39270         //  "&#8221;"    ,
39271         //  "&#8226;"    ,
39272           "&#176;"  //   , // degrees
39273
39274          // "&#233;"     , // e ecute
39275          // "&#250;"     , // u ecute?
39276     ],
39277     inputElements : [ 
39278             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39279             "input:submit", "input:button", "select", "textarea", "label" ],
39280     formats : [
39281         ["p"] ,  
39282         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39283         ["pre"],[ "code"], 
39284         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39285     ],
39286      /**
39287      * @cfg {String} defaultFont default font to use.
39288      */
39289     defaultFont: 'tahoma',
39290    
39291     fontSelect : false,
39292     
39293     
39294     formatCombo : false,
39295     
39296     init : function(editor)
39297     {
39298         this.editor = editor;
39299         
39300         
39301         var fid = editor.frameId;
39302         var etb = this;
39303         function btn(id, toggle, handler){
39304             var xid = fid + '-'+ id ;
39305             return {
39306                 id : xid,
39307                 cmd : id,
39308                 cls : 'x-btn-icon x-edit-'+id,
39309                 enableToggle:toggle !== false,
39310                 scope: editor, // was editor...
39311                 handler:handler||editor.relayBtnCmd,
39312                 clickEvent:'mousedown',
39313                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39314                 tabIndex:-1
39315             };
39316         }
39317         
39318         
39319         
39320         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39321         this.tb = tb;
39322          // stop form submits
39323         tb.el.on('click', function(e){
39324             e.preventDefault(); // what does this do?
39325         });
39326
39327         if(!this.disable.font && !Roo.isSafari){
39328             /* why no safari for fonts
39329             editor.fontSelect = tb.el.createChild({
39330                 tag:'select',
39331                 tabIndex: -1,
39332                 cls:'x-font-select',
39333                 html: editor.createFontOptions()
39334             });
39335             editor.fontSelect.on('change', function(){
39336                 var font = editor.fontSelect.dom.value;
39337                 editor.relayCmd('fontname', font);
39338                 editor.deferFocus();
39339             }, editor);
39340             tb.add(
39341                 editor.fontSelect.dom,
39342                 '-'
39343             );
39344             */
39345         };
39346         if(!this.disable.formats){
39347             this.formatCombo = new Roo.form.ComboBox({
39348                 store: new Roo.data.SimpleStore({
39349                     id : 'tag',
39350                     fields: ['tag'],
39351                     data : this.formats // from states.js
39352                 }),
39353                 blockFocus : true,
39354                 //autoCreate : {tag: "div",  size: "20"},
39355                 displayField:'tag',
39356                 typeAhead: false,
39357                 mode: 'local',
39358                 editable : false,
39359                 triggerAction: 'all',
39360                 emptyText:'Add tag',
39361                 selectOnFocus:true,
39362                 width:135,
39363                 listeners : {
39364                     'select': function(c, r, i) {
39365                         editor.insertTag(r.get('tag'));
39366                         editor.focus();
39367                     }
39368                 }
39369
39370             });
39371             tb.addField(this.formatCombo);
39372             
39373         }
39374         
39375         if(!this.disable.format){
39376             tb.add(
39377                 btn('bold'),
39378                 btn('italic'),
39379                 btn('underline')
39380             );
39381         };
39382         if(!this.disable.fontSize){
39383             tb.add(
39384                 '-',
39385                 
39386                 
39387                 btn('increasefontsize', false, editor.adjustFont),
39388                 btn('decreasefontsize', false, editor.adjustFont)
39389             );
39390         };
39391         
39392         
39393         if(this.disable.colors){
39394             tb.add(
39395                 '-', {
39396                     id:editor.frameId +'-forecolor',
39397                     cls:'x-btn-icon x-edit-forecolor',
39398                     clickEvent:'mousedown',
39399                     tooltip: this.buttonTips['forecolor'] || undefined,
39400                     tabIndex:-1,
39401                     menu : new Roo.menu.ColorMenu({
39402                         allowReselect: true,
39403                         focus: Roo.emptyFn,
39404                         value:'000000',
39405                         plain:true,
39406                         selectHandler: function(cp, color){
39407                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39408                             editor.deferFocus();
39409                         },
39410                         scope: editor,
39411                         clickEvent:'mousedown'
39412                     })
39413                 }, {
39414                     id:editor.frameId +'backcolor',
39415                     cls:'x-btn-icon x-edit-backcolor',
39416                     clickEvent:'mousedown',
39417                     tooltip: this.buttonTips['backcolor'] || undefined,
39418                     tabIndex:-1,
39419                     menu : new Roo.menu.ColorMenu({
39420                         focus: Roo.emptyFn,
39421                         value:'FFFFFF',
39422                         plain:true,
39423                         allowReselect: true,
39424                         selectHandler: function(cp, color){
39425                             if(Roo.isGecko){
39426                                 editor.execCmd('useCSS', false);
39427                                 editor.execCmd('hilitecolor', color);
39428                                 editor.execCmd('useCSS', true);
39429                                 editor.deferFocus();
39430                             }else{
39431                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39432                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39433                                 editor.deferFocus();
39434                             }
39435                         },
39436                         scope:editor,
39437                         clickEvent:'mousedown'
39438                     })
39439                 }
39440             );
39441         };
39442         // now add all the items...
39443         
39444
39445         if(!this.disable.alignments){
39446             tb.add(
39447                 '-',
39448                 btn('justifyleft'),
39449                 btn('justifycenter'),
39450                 btn('justifyright')
39451             );
39452         };
39453
39454         //if(!Roo.isSafari){
39455             if(!this.disable.links){
39456                 tb.add(
39457                     '-',
39458                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39459                 );
39460             };
39461
39462             if(!this.disable.lists){
39463                 tb.add(
39464                     '-',
39465                     btn('insertorderedlist'),
39466                     btn('insertunorderedlist')
39467                 );
39468             }
39469             if(!this.disable.sourceEdit){
39470                 tb.add(
39471                     '-',
39472                     btn('sourceedit', true, function(btn){
39473                         this.toggleSourceEdit(btn.pressed);
39474                     })
39475                 );
39476             }
39477         //}
39478         
39479         var smenu = { };
39480         // special menu.. - needs to be tidied up..
39481         if (!this.disable.special) {
39482             smenu = {
39483                 text: "&#169;",
39484                 cls: 'x-edit-none',
39485                 menu : {
39486                     items : []
39487                    }
39488             };
39489             for (var i =0; i < this.specialChars.length; i++) {
39490                 smenu.menu.items.push({
39491                     
39492                     html: this.specialChars[i],
39493                     handler: function(a,b) {
39494                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39495                         
39496                     },
39497                     tabIndex:-1
39498                 });
39499             }
39500             
39501             
39502             tb.add(smenu);
39503             
39504             
39505         }
39506         if (this.btns) {
39507             for(var i =0; i< this.btns.length;i++) {
39508                 var b = this.btns[i];
39509                 b.cls =  'x-edit-none';
39510                 b.scope = editor;
39511                 tb.add(b);
39512             }
39513         
39514         }
39515         
39516         
39517         
39518         // disable everything...
39519         
39520         this.tb.items.each(function(item){
39521            if(item.id != editor.frameId+ '-sourceedit'){
39522                 item.disable();
39523             }
39524         });
39525         this.rendered = true;
39526         
39527         // the all the btns;
39528         editor.on('editorevent', this.updateToolbar, this);
39529         // other toolbars need to implement this..
39530         //editor.on('editmodechange', this.updateToolbar, this);
39531     },
39532     
39533     
39534     
39535     /**
39536      * Protected method that will not generally be called directly. It triggers
39537      * a toolbar update by reading the markup state of the current selection in the editor.
39538      */
39539     updateToolbar: function(){
39540
39541         if(!this.editor.activated){
39542             this.editor.onFirstFocus();
39543             return;
39544         }
39545
39546         var btns = this.tb.items.map, 
39547             doc = this.editor.doc,
39548             frameId = this.editor.frameId;
39549
39550         if(!this.disable.font && !Roo.isSafari){
39551             /*
39552             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39553             if(name != this.fontSelect.dom.value){
39554                 this.fontSelect.dom.value = name;
39555             }
39556             */
39557         }
39558         if(!this.disable.format){
39559             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39560             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39561             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39562         }
39563         if(!this.disable.alignments){
39564             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39565             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39566             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39567         }
39568         if(!Roo.isSafari && !this.disable.lists){
39569             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39570             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39571         }
39572         
39573         var ans = this.editor.getAllAncestors();
39574         if (this.formatCombo) {
39575             
39576             
39577             var store = this.formatCombo.store;
39578             this.formatCombo.setValue("");
39579             for (var i =0; i < ans.length;i++) {
39580                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39581                     // select it..
39582                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39583                     break;
39584                 }
39585             }
39586         }
39587         
39588         
39589         
39590         // hides menus... - so this cant be on a menu...
39591         Roo.menu.MenuMgr.hideAll();
39592
39593         //this.editorsyncValue();
39594     },
39595    
39596     
39597     createFontOptions : function(){
39598         var buf = [], fs = this.fontFamilies, ff, lc;
39599         for(var i = 0, len = fs.length; i< len; i++){
39600             ff = fs[i];
39601             lc = ff.toLowerCase();
39602             buf.push(
39603                 '<option value="',lc,'" style="font-family:',ff,';"',
39604                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39605                     ff,
39606                 '</option>'
39607             );
39608         }
39609         return buf.join('');
39610     },
39611     
39612     toggleSourceEdit : function(sourceEditMode){
39613         if(sourceEditMode === undefined){
39614             sourceEditMode = !this.sourceEditMode;
39615         }
39616         this.sourceEditMode = sourceEditMode === true;
39617         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39618         // just toggle the button?
39619         if(btn.pressed !== this.editor.sourceEditMode){
39620             btn.toggle(this.editor.sourceEditMode);
39621             return;
39622         }
39623         
39624         if(this.sourceEditMode){
39625             this.tb.items.each(function(item){
39626                 if(item.cmd != 'sourceedit'){
39627                     item.disable();
39628                 }
39629             });
39630           
39631         }else{
39632             if(this.initialized){
39633                 this.tb.items.each(function(item){
39634                     item.enable();
39635                 });
39636             }
39637             
39638         }
39639         // tell the editor that it's been pressed..
39640         this.editor.toggleSourceEdit(sourceEditMode);
39641        
39642     },
39643      /**
39644      * Object collection of toolbar tooltips for the buttons in the editor. The key
39645      * is the command id associated with that button and the value is a valid QuickTips object.
39646      * For example:
39647 <pre><code>
39648 {
39649     bold : {
39650         title: 'Bold (Ctrl+B)',
39651         text: 'Make the selected text bold.',
39652         cls: 'x-html-editor-tip'
39653     },
39654     italic : {
39655         title: 'Italic (Ctrl+I)',
39656         text: 'Make the selected text italic.',
39657         cls: 'x-html-editor-tip'
39658     },
39659     ...
39660 </code></pre>
39661     * @type Object
39662      */
39663     buttonTips : {
39664         bold : {
39665             title: 'Bold (Ctrl+B)',
39666             text: 'Make the selected text bold.',
39667             cls: 'x-html-editor-tip'
39668         },
39669         italic : {
39670             title: 'Italic (Ctrl+I)',
39671             text: 'Make the selected text italic.',
39672             cls: 'x-html-editor-tip'
39673         },
39674         underline : {
39675             title: 'Underline (Ctrl+U)',
39676             text: 'Underline the selected text.',
39677             cls: 'x-html-editor-tip'
39678         },
39679         increasefontsize : {
39680             title: 'Grow Text',
39681             text: 'Increase the font size.',
39682             cls: 'x-html-editor-tip'
39683         },
39684         decreasefontsize : {
39685             title: 'Shrink Text',
39686             text: 'Decrease the font size.',
39687             cls: 'x-html-editor-tip'
39688         },
39689         backcolor : {
39690             title: 'Text Highlight Color',
39691             text: 'Change the background color of the selected text.',
39692             cls: 'x-html-editor-tip'
39693         },
39694         forecolor : {
39695             title: 'Font Color',
39696             text: 'Change the color of the selected text.',
39697             cls: 'x-html-editor-tip'
39698         },
39699         justifyleft : {
39700             title: 'Align Text Left',
39701             text: 'Align text to the left.',
39702             cls: 'x-html-editor-tip'
39703         },
39704         justifycenter : {
39705             title: 'Center Text',
39706             text: 'Center text in the editor.',
39707             cls: 'x-html-editor-tip'
39708         },
39709         justifyright : {
39710             title: 'Align Text Right',
39711             text: 'Align text to the right.',
39712             cls: 'x-html-editor-tip'
39713         },
39714         insertunorderedlist : {
39715             title: 'Bullet List',
39716             text: 'Start a bulleted list.',
39717             cls: 'x-html-editor-tip'
39718         },
39719         insertorderedlist : {
39720             title: 'Numbered List',
39721             text: 'Start a numbered list.',
39722             cls: 'x-html-editor-tip'
39723         },
39724         createlink : {
39725             title: 'Hyperlink',
39726             text: 'Make the selected text a hyperlink.',
39727             cls: 'x-html-editor-tip'
39728         },
39729         sourceedit : {
39730             title: 'Source Edit',
39731             text: 'Switch to source editing mode.',
39732             cls: 'x-html-editor-tip'
39733         }
39734     },
39735     // private
39736     onDestroy : function(){
39737         if(this.rendered){
39738             
39739             this.tb.items.each(function(item){
39740                 if(item.menu){
39741                     item.menu.removeAll();
39742                     if(item.menu.el){
39743                         item.menu.el.destroy();
39744                     }
39745                 }
39746                 item.destroy();
39747             });
39748              
39749         }
39750     },
39751     onFirstFocus: function() {
39752         this.tb.items.each(function(item){
39753            item.enable();
39754         });
39755     }
39756 });
39757
39758
39759
39760
39761 // <script type="text/javascript">
39762 /*
39763  * Based on
39764  * Ext JS Library 1.1.1
39765  * Copyright(c) 2006-2007, Ext JS, LLC.
39766  *  
39767  
39768  */
39769
39770  
39771 /**
39772  * @class Roo.form.HtmlEditor.ToolbarContext
39773  * Context Toolbar
39774  * 
39775  * Usage:
39776  *
39777  new Roo.form.HtmlEditor({
39778     ....
39779     toolbars : [
39780         new Roo.form.HtmlEditor.ToolbarStandard(),
39781         new Roo.form.HtmlEditor.ToolbarContext()
39782         })
39783     }
39784      
39785  * 
39786  * @config : {Object} disable List of elements to disable.. (not done yet.)
39787  * 
39788  * 
39789  */
39790
39791 Roo.form.HtmlEditor.ToolbarContext = function(config)
39792 {
39793     
39794     Roo.apply(this, config);
39795     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39796     // dont call parent... till later.
39797 }
39798 Roo.form.HtmlEditor.ToolbarContext.types = {
39799     'IMG' : {
39800         width : {
39801             title: "Width",
39802             width: 40
39803         },
39804         height:  {
39805             title: "Height",
39806             width: 40
39807         },
39808         align: {
39809             title: "Align",
39810             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39811             width : 80
39812             
39813         },
39814         border: {
39815             title: "Border",
39816             width: 40
39817         },
39818         alt: {
39819             title: "Alt",
39820             width: 120
39821         },
39822         src : {
39823             title: "Src",
39824             width: 220
39825         }
39826         
39827     },
39828     'A' : {
39829         name : {
39830             title: "Name",
39831             width: 50
39832         },
39833         href:  {
39834             title: "Href",
39835             width: 220
39836         } // border?
39837         
39838     },
39839     'TABLE' : {
39840         rows : {
39841             title: "Rows",
39842             width: 20
39843         },
39844         cols : {
39845             title: "Cols",
39846             width: 20
39847         },
39848         width : {
39849             title: "Width",
39850             width: 40
39851         },
39852         height : {
39853             title: "Height",
39854             width: 40
39855         },
39856         border : {
39857             title: "Border",
39858             width: 20
39859         }
39860     },
39861     'TD' : {
39862         width : {
39863             title: "Width",
39864             width: 40
39865         },
39866         height : {
39867             title: "Height",
39868             width: 40
39869         },   
39870         align: {
39871             title: "Align",
39872             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39873             width: 40
39874         },
39875         valign: {
39876             title: "Valign",
39877             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39878             width: 40
39879         },
39880         colspan: {
39881             title: "Colspan",
39882             width: 20
39883             
39884         }
39885     },
39886     'INPUT' : {
39887         name : {
39888             title: "name",
39889             width: 120
39890         },
39891         value : {
39892             title: "Value",
39893             width: 120
39894         },
39895         width : {
39896             title: "Width",
39897             width: 40
39898         }
39899     },
39900     'LABEL' : {
39901         'for' : {
39902             title: "For",
39903             width: 120
39904         }
39905     },
39906     'TEXTAREA' : {
39907           name : {
39908             title: "name",
39909             width: 120
39910         },
39911         rows : {
39912             title: "Rows",
39913             width: 20
39914         },
39915         cols : {
39916             title: "Cols",
39917             width: 20
39918         }
39919     },
39920     'SELECT' : {
39921         name : {
39922             title: "name",
39923             width: 120
39924         },
39925         selectoptions : {
39926             title: "Options",
39927             width: 200
39928         }
39929     },
39930     'BODY' : {
39931         title : {
39932             title: "title",
39933             width: 120,
39934             disabled : true
39935         }
39936     }
39937 };
39938
39939
39940
39941 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39942     
39943     tb: false,
39944     
39945     rendered: false,
39946     
39947     editor : false,
39948     /**
39949      * @cfg {Object} disable  List of toolbar elements to disable
39950          
39951      */
39952     disable : false,
39953     
39954     
39955     
39956     toolbars : false,
39957     
39958     init : function(editor)
39959     {
39960         this.editor = editor;
39961         
39962         
39963         var fid = editor.frameId;
39964         var etb = this;
39965         function btn(id, toggle, handler){
39966             var xid = fid + '-'+ id ;
39967             return {
39968                 id : xid,
39969                 cmd : id,
39970                 cls : 'x-btn-icon x-edit-'+id,
39971                 enableToggle:toggle !== false,
39972                 scope: editor, // was editor...
39973                 handler:handler||editor.relayBtnCmd,
39974                 clickEvent:'mousedown',
39975                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39976                 tabIndex:-1
39977             };
39978         }
39979         // create a new element.
39980         var wdiv = editor.wrap.createChild({
39981                 tag: 'div'
39982             }, editor.wrap.dom.firstChild.nextSibling, true);
39983         
39984         // can we do this more than once??
39985         
39986          // stop form submits
39987       
39988  
39989         // disable everything...
39990         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39991         this.toolbars = {};
39992            
39993         for (var i in  ty) {
39994           
39995             this.toolbars[i] = this.buildToolbar(ty[i],i);
39996         }
39997         this.tb = this.toolbars.BODY;
39998         this.tb.el.show();
39999         
40000          
40001         this.rendered = true;
40002         
40003         // the all the btns;
40004         editor.on('editorevent', this.updateToolbar, this);
40005         // other toolbars need to implement this..
40006         //editor.on('editmodechange', this.updateToolbar, this);
40007     },
40008     
40009     
40010     
40011     /**
40012      * Protected method that will not generally be called directly. It triggers
40013      * a toolbar update by reading the markup state of the current selection in the editor.
40014      */
40015     updateToolbar: function(){
40016
40017         if(!this.editor.activated){
40018             this.editor.onFirstFocus();
40019             return;
40020         }
40021
40022         
40023         var ans = this.editor.getAllAncestors();
40024         
40025         // pick
40026         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40027         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40028         sel = sel ? sel : this.editor.doc.body;
40029         sel = sel.tagName.length ? sel : this.editor.doc.body;
40030         var tn = sel.tagName.toUpperCase();
40031         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40032         tn = sel.tagName.toUpperCase();
40033         if (this.tb.name  == tn) {
40034             return; // no change
40035         }
40036         this.tb.el.hide();
40037         ///console.log("show: " + tn);
40038         this.tb =  this.toolbars[tn];
40039         this.tb.el.show();
40040         this.tb.fields.each(function(e) {
40041             e.setValue(sel.getAttribute(e.name));
40042         });
40043         this.tb.selectedNode = sel;
40044         
40045         
40046         Roo.menu.MenuMgr.hideAll();
40047
40048         //this.editorsyncValue();
40049     },
40050    
40051        
40052     // private
40053     onDestroy : function(){
40054         if(this.rendered){
40055             
40056             this.tb.items.each(function(item){
40057                 if(item.menu){
40058                     item.menu.removeAll();
40059                     if(item.menu.el){
40060                         item.menu.el.destroy();
40061                     }
40062                 }
40063                 item.destroy();
40064             });
40065              
40066         }
40067     },
40068     onFirstFocus: function() {
40069         // need to do this for all the toolbars..
40070         this.tb.items.each(function(item){
40071            item.enable();
40072         });
40073     },
40074     buildToolbar: function(tlist, nm)
40075     {
40076         var editor = this.editor;
40077          // create a new element.
40078         var wdiv = editor.wrap.createChild({
40079                 tag: 'div'
40080             }, editor.wrap.dom.firstChild.nextSibling, true);
40081         
40082        
40083         var tb = new Roo.Toolbar(wdiv);
40084         tb.add(nm+ ":&nbsp;");
40085         for (var i in tlist) {
40086             var item = tlist[i];
40087             tb.add(item.title + ":&nbsp;");
40088             if (item.opts) {
40089                 // fixme
40090                 
40091               
40092                 tb.addField( new Roo.form.ComboBox({
40093                     store: new Roo.data.SimpleStore({
40094                         id : 'val',
40095                         fields: ['val'],
40096                         data : item.opts // from states.js
40097                     }),
40098                     name : i,
40099                     displayField:'val',
40100                     typeAhead: false,
40101                     mode: 'local',
40102                     editable : false,
40103                     triggerAction: 'all',
40104                     emptyText:'Select',
40105                     selectOnFocus:true,
40106                     width: item.width ? item.width  : 130,
40107                     listeners : {
40108                         'select': function(c, r, i) {
40109                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40110                         }
40111                     }
40112
40113                 }));
40114                 continue;
40115                     
40116                 
40117                 
40118                 
40119                 
40120                 tb.addField( new Roo.form.TextField({
40121                     name: i,
40122                     width: 100,
40123                     //allowBlank:false,
40124                     value: ''
40125                 }));
40126                 continue;
40127             }
40128             tb.addField( new Roo.form.TextField({
40129                 name: i,
40130                 width: item.width,
40131                 //allowBlank:true,
40132                 value: '',
40133                 listeners: {
40134                     'change' : function(f, nv, ov) {
40135                         tb.selectedNode.setAttribute(f.name, nv);
40136                     }
40137                 }
40138             }));
40139              
40140         }
40141         tb.el.on('click', function(e){
40142             e.preventDefault(); // what does this do?
40143         });
40144         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40145         tb.el.hide();
40146         tb.name = nm;
40147         // dont need to disable them... as they will get hidden
40148         return tb;
40149          
40150         
40151     }
40152     
40153     
40154     
40155     
40156 });
40157
40158
40159
40160
40161
40162 /*
40163  * Based on:
40164  * Ext JS Library 1.1.1
40165  * Copyright(c) 2006-2007, Ext JS, LLC.
40166  *
40167  * Originally Released Under LGPL - original licence link has changed is not relivant.
40168  *
40169  * Fork - LGPL
40170  * <script type="text/javascript">
40171  */
40172  
40173 /**
40174  * @class Roo.form.BasicForm
40175  * @extends Roo.util.Observable
40176  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40177  * @constructor
40178  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40179  * @param {Object} config Configuration options
40180  */
40181 Roo.form.BasicForm = function(el, config){
40182     this.allItems = [];
40183     this.childForms = [];
40184     Roo.apply(this, config);
40185     /*
40186      * The Roo.form.Field items in this form.
40187      * @type MixedCollection
40188      */
40189      
40190      
40191     this.items = new Roo.util.MixedCollection(false, function(o){
40192         return o.id || (o.id = Roo.id());
40193     });
40194     this.addEvents({
40195         /**
40196          * @event beforeaction
40197          * Fires before any action is performed. Return false to cancel the action.
40198          * @param {Form} this
40199          * @param {Action} action The action to be performed
40200          */
40201         beforeaction: true,
40202         /**
40203          * @event actionfailed
40204          * Fires when an action fails.
40205          * @param {Form} this
40206          * @param {Action} action The action that failed
40207          */
40208         actionfailed : true,
40209         /**
40210          * @event actioncomplete
40211          * Fires when an action is completed.
40212          * @param {Form} this
40213          * @param {Action} action The action that completed
40214          */
40215         actioncomplete : true
40216     });
40217     if(el){
40218         this.initEl(el);
40219     }
40220     Roo.form.BasicForm.superclass.constructor.call(this);
40221 };
40222
40223 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40224     /**
40225      * @cfg {String} method
40226      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40227      */
40228     /**
40229      * @cfg {DataReader} reader
40230      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40231      * This is optional as there is built-in support for processing JSON.
40232      */
40233     /**
40234      * @cfg {DataReader} errorReader
40235      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40236      * This is completely optional as there is built-in support for processing JSON.
40237      */
40238     /**
40239      * @cfg {String} url
40240      * The URL to use for form actions if one isn't supplied in the action options.
40241      */
40242     /**
40243      * @cfg {Boolean} fileUpload
40244      * Set to true if this form is a file upload.
40245      */
40246     /**
40247      * @cfg {Object} baseParams
40248      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40249      */
40250     /**
40251      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40252      */
40253     timeout: 30,
40254
40255     // private
40256     activeAction : null,
40257
40258     /**
40259      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40260      * or setValues() data instead of when the form was first created.
40261      */
40262     trackResetOnLoad : false,
40263     
40264     
40265     /**
40266      * childForms - used for multi-tab forms
40267      * @type {Array}
40268      */
40269     childForms : false,
40270     
40271     /**
40272      * allItems - full list of fields.
40273      * @type {Array}
40274      */
40275     allItems : false,
40276     
40277     /**
40278      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40279      * element by passing it or its id or mask the form itself by passing in true.
40280      * @type Mixed
40281      */
40282     waitMsgTarget : undefined,
40283
40284     // private
40285     initEl : function(el){
40286         this.el = Roo.get(el);
40287         this.id = this.el.id || Roo.id();
40288         this.el.on('submit', this.onSubmit, this);
40289         this.el.addClass('x-form');
40290     },
40291
40292     // private
40293     onSubmit : function(e){
40294         e.stopEvent();
40295     },
40296
40297     /**
40298      * Returns true if client-side validation on the form is successful.
40299      * @return Boolean
40300      */
40301     isValid : function(){
40302         var valid = true;
40303         this.items.each(function(f){
40304            if(!f.validate()){
40305                valid = false;
40306            }
40307         });
40308         return valid;
40309     },
40310
40311     /**
40312      * Returns true if any fields in this form have changed since their original load.
40313      * @return Boolean
40314      */
40315     isDirty : function(){
40316         var dirty = false;
40317         this.items.each(function(f){
40318            if(f.isDirty()){
40319                dirty = true;
40320                return false;
40321            }
40322         });
40323         return dirty;
40324     },
40325
40326     /**
40327      * Performs a predefined action (submit or load) or custom actions you define on this form.
40328      * @param {String} actionName The name of the action type
40329      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40330      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40331      * accept other config options):
40332      * <pre>
40333 Property          Type             Description
40334 ----------------  ---------------  ----------------------------------------------------------------------------------
40335 url               String           The url for the action (defaults to the form's url)
40336 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40337 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40338 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40339                                    validate the form on the client (defaults to false)
40340      * </pre>
40341      * @return {BasicForm} this
40342      */
40343     doAction : function(action, options){
40344         if(typeof action == 'string'){
40345             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40346         }
40347         if(this.fireEvent('beforeaction', this, action) !== false){
40348             this.beforeAction(action);
40349             action.run.defer(100, action);
40350         }
40351         return this;
40352     },
40353
40354     /**
40355      * Shortcut to do a submit action.
40356      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40357      * @return {BasicForm} this
40358      */
40359     submit : function(options){
40360         this.doAction('submit', options);
40361         return this;
40362     },
40363
40364     /**
40365      * Shortcut to do a load action.
40366      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40367      * @return {BasicForm} this
40368      */
40369     load : function(options){
40370         this.doAction('load', options);
40371         return this;
40372     },
40373
40374     /**
40375      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40376      * @param {Record} record The record to edit
40377      * @return {BasicForm} this
40378      */
40379     updateRecord : function(record){
40380         record.beginEdit();
40381         var fs = record.fields;
40382         fs.each(function(f){
40383             var field = this.findField(f.name);
40384             if(field){
40385                 record.set(f.name, field.getValue());
40386             }
40387         }, this);
40388         record.endEdit();
40389         return this;
40390     },
40391
40392     /**
40393      * Loads an Roo.data.Record into this form.
40394      * @param {Record} record The record to load
40395      * @return {BasicForm} this
40396      */
40397     loadRecord : function(record){
40398         this.setValues(record.data);
40399         return this;
40400     },
40401
40402     // private
40403     beforeAction : function(action){
40404         var o = action.options;
40405         if(o.waitMsg){
40406             if(this.waitMsgTarget === true){
40407                 this.el.mask(o.waitMsg, 'x-mask-loading');
40408             }else if(this.waitMsgTarget){
40409                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40410                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
40411             }else{
40412                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
40413             }
40414         }
40415     },
40416
40417     // private
40418     afterAction : function(action, success){
40419         this.activeAction = null;
40420         var o = action.options;
40421         if(o.waitMsg){
40422             if(this.waitMsgTarget === true){
40423                 this.el.unmask();
40424             }else if(this.waitMsgTarget){
40425                 this.waitMsgTarget.unmask();
40426             }else{
40427                 Roo.MessageBox.updateProgress(1);
40428                 Roo.MessageBox.hide();
40429             }
40430         }
40431         if(success){
40432             if(o.reset){
40433                 this.reset();
40434             }
40435             Roo.callback(o.success, o.scope, [this, action]);
40436             this.fireEvent('actioncomplete', this, action);
40437         }else{
40438             Roo.callback(o.failure, o.scope, [this, action]);
40439             this.fireEvent('actionfailed', this, action);
40440         }
40441     },
40442
40443     /**
40444      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40445      * @param {String} id The value to search for
40446      * @return Field
40447      */
40448     findField : function(id){
40449         var field = this.items.get(id);
40450         if(!field){
40451             this.items.each(function(f){
40452                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40453                     field = f;
40454                     return false;
40455                 }
40456             });
40457         }
40458         return field || null;
40459     },
40460
40461     /**
40462      * Add a secondary form to this one, 
40463      * Used to provide tabbed forms. One form is primary, with hidden values 
40464      * which mirror the elements from the other forms.
40465      * 
40466      * @param {Roo.form.Form} form to add.
40467      * 
40468      */
40469     addForm : function(form)
40470     {
40471        
40472         if (this.childForms.indexOf(form) > -1) {
40473             // already added..
40474             return;
40475         }
40476         this.childForms.push(form);
40477         var n = '';
40478         Roo.each(form.allItems, function (fe) {
40479             
40480             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40481             if (this.findField(n)) { // already added..
40482                 return;
40483             }
40484             var add = new Roo.form.Hidden({
40485                 name : n
40486             });
40487             add.render(this.el);
40488             
40489             this.add( add );
40490         }, this);
40491         
40492     },
40493     /**
40494      * Mark fields in this form invalid in bulk.
40495      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40496      * @return {BasicForm} this
40497      */
40498     markInvalid : function(errors){
40499         if(errors instanceof Array){
40500             for(var i = 0, len = errors.length; i < len; i++){
40501                 var fieldError = errors[i];
40502                 var f = this.findField(fieldError.id);
40503                 if(f){
40504                     f.markInvalid(fieldError.msg);
40505                 }
40506             }
40507         }else{
40508             var field, id;
40509             for(id in errors){
40510                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40511                     field.markInvalid(errors[id]);
40512                 }
40513             }
40514         }
40515         Roo.each(this.childForms || [], function (f) {
40516             f.markInvalid(errors);
40517         });
40518         
40519         return this;
40520     },
40521
40522     /**
40523      * Set values for fields in this form in bulk.
40524      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40525      * @return {BasicForm} this
40526      */
40527     setValues : function(values){
40528         if(values instanceof Array){ // array of objects
40529             for(var i = 0, len = values.length; i < len; i++){
40530                 var v = values[i];
40531                 var f = this.findField(v.id);
40532                 if(f){
40533                     f.setValue(v.value);
40534                     if(this.trackResetOnLoad){
40535                         f.originalValue = f.getValue();
40536                     }
40537                 }
40538             }
40539         }else{ // object hash
40540             var field, id;
40541             for(id in values){
40542                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40543                     
40544                     if (field.setFromData && 
40545                         field.valueField && 
40546                         field.displayField &&
40547                         // combos' with local stores can 
40548                         // be queried via setValue()
40549                         // to set their value..
40550                         (field.store && !field.store.isLocal)
40551                         ) {
40552                         // it's a combo
40553                         var sd = { };
40554                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40555                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40556                         field.setFromData(sd);
40557                         
40558                     } else {
40559                         field.setValue(values[id]);
40560                     }
40561                     
40562                     
40563                     if(this.trackResetOnLoad){
40564                         field.originalValue = field.getValue();
40565                     }
40566                 }
40567             }
40568         }
40569          
40570         Roo.each(this.childForms || [], function (f) {
40571             f.setValues(values);
40572         });
40573                 
40574         return this;
40575     },
40576
40577     /**
40578      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40579      * they are returned as an array.
40580      * @param {Boolean} asString
40581      * @return {Object}
40582      */
40583     getValues : function(asString){
40584         if (this.childForms) {
40585             // copy values from the child forms
40586             Roo.each(this.childForms, function (f) {
40587                 this.setValues(f.getValues());
40588             }, this);
40589         }
40590         
40591         
40592         
40593         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40594         if(asString === true){
40595             return fs;
40596         }
40597         return Roo.urlDecode(fs);
40598     },
40599     
40600     /**
40601      * Returns the fields in this form as an object with key/value pairs. 
40602      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40603      * @return {Object}
40604      */
40605     getFieldValues : function()
40606     {
40607         if (this.childForms) {
40608             // copy values from the child forms
40609             Roo.each(this.childForms, function (f) {
40610                 this.setValues(f.getValues());
40611             }, this);
40612         }
40613         
40614         var ret = {};
40615         this.items.each(function(f){
40616             if (!f.getName()) {
40617                 return;
40618             }
40619             var v = f.getValue();
40620             if ((typeof(v) == 'object') && f.getRawValue) {
40621                 v = f.getRawValue() ; // dates..
40622             }
40623             ret[f.getName()] = v;
40624         });
40625         
40626         return ret;
40627     },
40628
40629     /**
40630      * Clears all invalid messages in this form.
40631      * @return {BasicForm} this
40632      */
40633     clearInvalid : function(){
40634         this.items.each(function(f){
40635            f.clearInvalid();
40636         });
40637         
40638         Roo.each(this.childForms || [], function (f) {
40639             f.clearInvalid();
40640         });
40641         
40642         
40643         return this;
40644     },
40645
40646     /**
40647      * Resets this form.
40648      * @return {BasicForm} this
40649      */
40650     reset : function(){
40651         this.items.each(function(f){
40652             f.reset();
40653         });
40654         
40655         Roo.each(this.childForms || [], function (f) {
40656             f.reset();
40657         });
40658        
40659         
40660         return this;
40661     },
40662
40663     /**
40664      * Add Roo.form components to this form.
40665      * @param {Field} field1
40666      * @param {Field} field2 (optional)
40667      * @param {Field} etc (optional)
40668      * @return {BasicForm} this
40669      */
40670     add : function(){
40671         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40672         return this;
40673     },
40674
40675
40676     /**
40677      * Removes a field from the items collection (does NOT remove its markup).
40678      * @param {Field} field
40679      * @return {BasicForm} this
40680      */
40681     remove : function(field){
40682         this.items.remove(field);
40683         return this;
40684     },
40685
40686     /**
40687      * Looks at the fields in this form, checks them for an id attribute,
40688      * and calls applyTo on the existing dom element with that id.
40689      * @return {BasicForm} this
40690      */
40691     render : function(){
40692         this.items.each(function(f){
40693             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40694                 f.applyTo(f.id);
40695             }
40696         });
40697         return this;
40698     },
40699
40700     /**
40701      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40702      * @param {Object} values
40703      * @return {BasicForm} this
40704      */
40705     applyToFields : function(o){
40706         this.items.each(function(f){
40707            Roo.apply(f, o);
40708         });
40709         return this;
40710     },
40711
40712     /**
40713      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40714      * @param {Object} values
40715      * @return {BasicForm} this
40716      */
40717     applyIfToFields : function(o){
40718         this.items.each(function(f){
40719            Roo.applyIf(f, o);
40720         });
40721         return this;
40722     }
40723 });
40724
40725 // back compat
40726 Roo.BasicForm = Roo.form.BasicForm;/*
40727  * Based on:
40728  * Ext JS Library 1.1.1
40729  * Copyright(c) 2006-2007, Ext JS, LLC.
40730  *
40731  * Originally Released Under LGPL - original licence link has changed is not relivant.
40732  *
40733  * Fork - LGPL
40734  * <script type="text/javascript">
40735  */
40736
40737 /**
40738  * @class Roo.form.Form
40739  * @extends Roo.form.BasicForm
40740  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40741  * @constructor
40742  * @param {Object} config Configuration options
40743  */
40744 Roo.form.Form = function(config){
40745     var xitems =  [];
40746     if (config.items) {
40747         xitems = config.items;
40748         delete config.items;
40749     }
40750    
40751     
40752     Roo.form.Form.superclass.constructor.call(this, null, config);
40753     this.url = this.url || this.action;
40754     if(!this.root){
40755         this.root = new Roo.form.Layout(Roo.applyIf({
40756             id: Roo.id()
40757         }, config));
40758     }
40759     this.active = this.root;
40760     /**
40761      * Array of all the buttons that have been added to this form via {@link addButton}
40762      * @type Array
40763      */
40764     this.buttons = [];
40765     this.allItems = [];
40766     this.addEvents({
40767         /**
40768          * @event clientvalidation
40769          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40770          * @param {Form} this
40771          * @param {Boolean} valid true if the form has passed client-side validation
40772          */
40773         clientvalidation: true,
40774         /**
40775          * @event rendered
40776          * Fires when the form is rendered
40777          * @param {Roo.form.Form} form
40778          */
40779         rendered : true
40780     });
40781     
40782     Roo.each(xitems, this.addxtype, this);
40783     
40784     
40785     
40786 };
40787
40788 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40789     /**
40790      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40791      */
40792     /**
40793      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40794      */
40795     /**
40796      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40797      */
40798     buttonAlign:'center',
40799
40800     /**
40801      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40802      */
40803     minButtonWidth:75,
40804
40805     /**
40806      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40807      * This property cascades to child containers if not set.
40808      */
40809     labelAlign:'left',
40810
40811     /**
40812      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40813      * fires a looping event with that state. This is required to bind buttons to the valid
40814      * state using the config value formBind:true on the button.
40815      */
40816     monitorValid : false,
40817
40818     /**
40819      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40820      */
40821     monitorPoll : 200,
40822
40823   
40824     /**
40825      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40826      * fields are added and the column is closed. If no fields are passed the column remains open
40827      * until end() is called.
40828      * @param {Object} config The config to pass to the column
40829      * @param {Field} field1 (optional)
40830      * @param {Field} field2 (optional)
40831      * @param {Field} etc (optional)
40832      * @return Column The column container object
40833      */
40834     column : function(c){
40835         var col = new Roo.form.Column(c);
40836         this.start(col);
40837         if(arguments.length > 1){ // duplicate code required because of Opera
40838             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40839             this.end();
40840         }
40841         return col;
40842     },
40843
40844     /**
40845      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40846      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40847      * until end() is called.
40848      * @param {Object} config The config to pass to the fieldset
40849      * @param {Field} field1 (optional)
40850      * @param {Field} field2 (optional)
40851      * @param {Field} etc (optional)
40852      * @return FieldSet The fieldset container object
40853      */
40854     fieldset : function(c){
40855         var fs = new Roo.form.FieldSet(c);
40856         this.start(fs);
40857         if(arguments.length > 1){ // duplicate code required because of Opera
40858             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40859             this.end();
40860         }
40861         return fs;
40862     },
40863
40864     /**
40865      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40866      * fields are added and the container is closed. If no fields are passed the container remains open
40867      * until end() is called.
40868      * @param {Object} config The config to pass to the Layout
40869      * @param {Field} field1 (optional)
40870      * @param {Field} field2 (optional)
40871      * @param {Field} etc (optional)
40872      * @return Layout The container object
40873      */
40874     container : function(c){
40875         var l = new Roo.form.Layout(c);
40876         this.start(l);
40877         if(arguments.length > 1){ // duplicate code required because of Opera
40878             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40879             this.end();
40880         }
40881         return l;
40882     },
40883
40884     /**
40885      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40886      * @param {Object} container A Roo.form.Layout or subclass of Layout
40887      * @return {Form} this
40888      */
40889     start : function(c){
40890         // cascade label info
40891         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40892         this.active.stack.push(c);
40893         c.ownerCt = this.active;
40894         this.active = c;
40895         return this;
40896     },
40897
40898     /**
40899      * Closes the current open container
40900      * @return {Form} this
40901      */
40902     end : function(){
40903         if(this.active == this.root){
40904             return this;
40905         }
40906         this.active = this.active.ownerCt;
40907         return this;
40908     },
40909
40910     /**
40911      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40912      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40913      * as the label of the field.
40914      * @param {Field} field1
40915      * @param {Field} field2 (optional)
40916      * @param {Field} etc. (optional)
40917      * @return {Form} this
40918      */
40919     add : function(){
40920         this.active.stack.push.apply(this.active.stack, arguments);
40921         this.allItems.push.apply(this.allItems,arguments);
40922         var r = [];
40923         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40924             if(a[i].isFormField){
40925                 r.push(a[i]);
40926             }
40927         }
40928         if(r.length > 0){
40929             Roo.form.Form.superclass.add.apply(this, r);
40930         }
40931         return this;
40932     },
40933     
40934
40935     
40936     
40937     
40938      /**
40939      * Find any element that has been added to a form, using it's ID or name
40940      * This can include framesets, columns etc. along with regular fields..
40941      * @param {String} id - id or name to find.
40942      
40943      * @return {Element} e - or false if nothing found.
40944      */
40945     findbyId : function(id)
40946     {
40947         var ret = false;
40948         if (!id) {
40949             return ret;
40950         }
40951         Ext.each(this.allItems, function(f){
40952             if (f.id == id || f.name == id ){
40953                 ret = f;
40954                 return false;
40955             }
40956         });
40957         return ret;
40958     },
40959
40960     
40961     
40962     /**
40963      * Render this form into the passed container. This should only be called once!
40964      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40965      * @return {Form} this
40966      */
40967     render : function(ct){
40968         ct = Roo.get(ct);
40969         var o = this.autoCreate || {
40970             tag: 'form',
40971             method : this.method || 'POST',
40972             id : this.id || Roo.id()
40973         };
40974         this.initEl(ct.createChild(o));
40975
40976         this.root.render(this.el);
40977
40978         this.items.each(function(f){
40979             f.render('x-form-el-'+f.id);
40980         });
40981
40982         if(this.buttons.length > 0){
40983             // tables are required to maintain order and for correct IE layout
40984             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40985                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40986                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40987             }}, null, true);
40988             var tr = tb.getElementsByTagName('tr')[0];
40989             for(var i = 0, len = this.buttons.length; i < len; i++) {
40990                 var b = this.buttons[i];
40991                 var td = document.createElement('td');
40992                 td.className = 'x-form-btn-td';
40993                 b.render(tr.appendChild(td));
40994             }
40995         }
40996         if(this.monitorValid){ // initialize after render
40997             this.startMonitoring();
40998         }
40999         this.fireEvent('rendered', this);
41000         return this;
41001     },
41002
41003     /**
41004      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41005      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41006      * object or a valid Roo.DomHelper element config
41007      * @param {Function} handler The function called when the button is clicked
41008      * @param {Object} scope (optional) The scope of the handler function
41009      * @return {Roo.Button}
41010      */
41011     addButton : function(config, handler, scope){
41012         var bc = {
41013             handler: handler,
41014             scope: scope,
41015             minWidth: this.minButtonWidth,
41016             hideParent:true
41017         };
41018         if(typeof config == "string"){
41019             bc.text = config;
41020         }else{
41021             Roo.apply(bc, config);
41022         }
41023         var btn = new Roo.Button(null, bc);
41024         this.buttons.push(btn);
41025         return btn;
41026     },
41027
41028      /**
41029      * Adds a series of form elements (using the xtype property as the factory method.
41030      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41031      * @param {Object} config 
41032      */
41033     
41034     addxtype : function()
41035     {
41036         var ar = Array.prototype.slice.call(arguments, 0);
41037         var ret = false;
41038         for(var i = 0; i < ar.length; i++) {
41039             if (!ar[i]) {
41040                 continue; // skip -- if this happends something invalid got sent, we 
41041                 // should ignore it, as basically that interface element will not show up
41042                 // and that should be pretty obvious!!
41043             }
41044             
41045             if (Roo.form[ar[i].xtype]) {
41046                 ar[i].form = this;
41047                 var fe = Roo.factory(ar[i], Roo.form);
41048                 if (!ret) {
41049                     ret = fe;
41050                 }
41051                 fe.form = this;
41052                 if (fe.store) {
41053                     fe.store.form = this;
41054                 }
41055                 if (fe.isLayout) {  
41056                          
41057                     this.start(fe);
41058                     this.allItems.push(fe);
41059                     if (fe.items && fe.addxtype) {
41060                         fe.addxtype.apply(fe, fe.items);
41061                         delete fe.items;
41062                     }
41063                      this.end();
41064                     continue;
41065                 }
41066                 
41067                 
41068                  
41069                 this.add(fe);
41070               //  console.log('adding ' + ar[i].xtype);
41071             }
41072             if (ar[i].xtype == 'Button') {  
41073                 //console.log('adding button');
41074                 //console.log(ar[i]);
41075                 this.addButton(ar[i]);
41076                 this.allItems.push(fe);
41077                 continue;
41078             }
41079             
41080             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41081                 alert('end is not supported on xtype any more, use items');
41082             //    this.end();
41083             //    //console.log('adding end');
41084             }
41085             
41086         }
41087         return ret;
41088     },
41089     
41090     /**
41091      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41092      * option "monitorValid"
41093      */
41094     startMonitoring : function(){
41095         if(!this.bound){
41096             this.bound = true;
41097             Roo.TaskMgr.start({
41098                 run : this.bindHandler,
41099                 interval : this.monitorPoll || 200,
41100                 scope: this
41101             });
41102         }
41103     },
41104
41105     /**
41106      * Stops monitoring of the valid state of this form
41107      */
41108     stopMonitoring : function(){
41109         this.bound = false;
41110     },
41111
41112     // private
41113     bindHandler : function(){
41114         if(!this.bound){
41115             return false; // stops binding
41116         }
41117         var valid = true;
41118         this.items.each(function(f){
41119             if(!f.isValid(true)){
41120                 valid = false;
41121                 return false;
41122             }
41123         });
41124         for(var i = 0, len = this.buttons.length; i < len; i++){
41125             var btn = this.buttons[i];
41126             if(btn.formBind === true && btn.disabled === valid){
41127                 btn.setDisabled(!valid);
41128             }
41129         }
41130         this.fireEvent('clientvalidation', this, valid);
41131     }
41132     
41133     
41134     
41135     
41136     
41137     
41138     
41139     
41140 });
41141
41142
41143 // back compat
41144 Roo.Form = Roo.form.Form;
41145 /*
41146  * Based on:
41147  * Ext JS Library 1.1.1
41148  * Copyright(c) 2006-2007, Ext JS, LLC.
41149  *
41150  * Originally Released Under LGPL - original licence link has changed is not relivant.
41151  *
41152  * Fork - LGPL
41153  * <script type="text/javascript">
41154  */
41155  
41156  /**
41157  * @class Roo.form.Action
41158  * Internal Class used to handle form actions
41159  * @constructor
41160  * @param {Roo.form.BasicForm} el The form element or its id
41161  * @param {Object} config Configuration options
41162  */
41163  
41164  
41165 // define the action interface
41166 Roo.form.Action = function(form, options){
41167     this.form = form;
41168     this.options = options || {};
41169 };
41170 /**
41171  * Client Validation Failed
41172  * @const 
41173  */
41174 Roo.form.Action.CLIENT_INVALID = 'client';
41175 /**
41176  * Server Validation Failed
41177  * @const 
41178  */
41179  Roo.form.Action.SERVER_INVALID = 'server';
41180  /**
41181  * Connect to Server Failed
41182  * @const 
41183  */
41184 Roo.form.Action.CONNECT_FAILURE = 'connect';
41185 /**
41186  * Reading Data from Server Failed
41187  * @const 
41188  */
41189 Roo.form.Action.LOAD_FAILURE = 'load';
41190
41191 Roo.form.Action.prototype = {
41192     type : 'default',
41193     failureType : undefined,
41194     response : undefined,
41195     result : undefined,
41196
41197     // interface method
41198     run : function(options){
41199
41200     },
41201
41202     // interface method
41203     success : function(response){
41204
41205     },
41206
41207     // interface method
41208     handleResponse : function(response){
41209
41210     },
41211
41212     // default connection failure
41213     failure : function(response){
41214         this.response = response;
41215         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41216         this.form.afterAction(this, false);
41217     },
41218
41219     processResponse : function(response){
41220         this.response = response;
41221         if(!response.responseText){
41222             return true;
41223         }
41224         this.result = this.handleResponse(response);
41225         return this.result;
41226     },
41227
41228     // utility functions used internally
41229     getUrl : function(appendParams){
41230         var url = this.options.url || this.form.url || this.form.el.dom.action;
41231         if(appendParams){
41232             var p = this.getParams();
41233             if(p){
41234                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41235             }
41236         }
41237         return url;
41238     },
41239
41240     getMethod : function(){
41241         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41242     },
41243
41244     getParams : function(){
41245         var bp = this.form.baseParams;
41246         var p = this.options.params;
41247         if(p){
41248             if(typeof p == "object"){
41249                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41250             }else if(typeof p == 'string' && bp){
41251                 p += '&' + Roo.urlEncode(bp);
41252             }
41253         }else if(bp){
41254             p = Roo.urlEncode(bp);
41255         }
41256         return p;
41257     },
41258
41259     createCallback : function(){
41260         return {
41261             success: this.success,
41262             failure: this.failure,
41263             scope: this,
41264             timeout: (this.form.timeout*1000),
41265             upload: this.form.fileUpload ? this.success : undefined
41266         };
41267     }
41268 };
41269
41270 Roo.form.Action.Submit = function(form, options){
41271     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41272 };
41273
41274 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41275     type : 'submit',
41276
41277     run : function()
41278     {
41279         // run get Values on the form, so it syncs any secondary forms.
41280         this.form.getValues();
41281         
41282         var o = this.options;
41283         var method = this.getMethod();
41284         var isPost = method == 'POST';
41285         if(o.clientValidation === false || this.form.isValid()){
41286             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41287                 form:this.form.el.dom,
41288                 url:this.getUrl(!isPost),
41289                 method: method,
41290                 params:isPost ? this.getParams() : null,
41291                 isUpload: this.form.fileUpload
41292             }));
41293
41294         }else if (o.clientValidation !== false){ // client validation failed
41295             this.failureType = Roo.form.Action.CLIENT_INVALID;
41296             this.form.afterAction(this, false);
41297         }
41298     },
41299
41300     success : function(response){
41301         var result = this.processResponse(response);
41302         if(result === true || result.success){
41303             this.form.afterAction(this, true);
41304             return;
41305         }
41306         if(result.errors){
41307             this.form.markInvalid(result.errors);
41308             this.failureType = Roo.form.Action.SERVER_INVALID;
41309         }
41310         this.form.afterAction(this, false);
41311     },
41312
41313     handleResponse : function(response){
41314         if(this.form.errorReader){
41315             var rs = this.form.errorReader.read(response);
41316             var errors = [];
41317             if(rs.records){
41318                 for(var i = 0, len = rs.records.length; i < len; i++) {
41319                     var r = rs.records[i];
41320                     errors[i] = r.data;
41321                 }
41322             }
41323             if(errors.length < 1){
41324                 errors = null;
41325             }
41326             return {
41327                 success : rs.success,
41328                 errors : errors
41329             };
41330         }
41331         var ret = false;
41332         try {
41333             ret = Roo.decode(response.responseText);
41334         } catch (e) {
41335             ret = {
41336                 success: false,
41337                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41338                 errors : []
41339             };
41340         }
41341         return ret;
41342         
41343     }
41344 });
41345
41346
41347 Roo.form.Action.Load = function(form, options){
41348     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41349     this.reader = this.form.reader;
41350 };
41351
41352 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41353     type : 'load',
41354
41355     run : function(){
41356         Roo.Ajax.request(Roo.apply(
41357                 this.createCallback(), {
41358                     method:this.getMethod(),
41359                     url:this.getUrl(false),
41360                     params:this.getParams()
41361         }));
41362     },
41363
41364     success : function(response){
41365         var result = this.processResponse(response);
41366         if(result === true || !result.success || !result.data){
41367             this.failureType = Roo.form.Action.LOAD_FAILURE;
41368             this.form.afterAction(this, false);
41369             return;
41370         }
41371         this.form.clearInvalid();
41372         this.form.setValues(result.data);
41373         this.form.afterAction(this, true);
41374     },
41375
41376     handleResponse : function(response){
41377         if(this.form.reader){
41378             var rs = this.form.reader.read(response);
41379             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41380             return {
41381                 success : rs.success,
41382                 data : data
41383             };
41384         }
41385         return Roo.decode(response.responseText);
41386     }
41387 });
41388
41389 Roo.form.Action.ACTION_TYPES = {
41390     'load' : Roo.form.Action.Load,
41391     'submit' : Roo.form.Action.Submit
41392 };/*
41393  * Based on:
41394  * Ext JS Library 1.1.1
41395  * Copyright(c) 2006-2007, Ext JS, LLC.
41396  *
41397  * Originally Released Under LGPL - original licence link has changed is not relivant.
41398  *
41399  * Fork - LGPL
41400  * <script type="text/javascript">
41401  */
41402  
41403 /**
41404  * @class Roo.form.Layout
41405  * @extends Roo.Component
41406  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41407  * @constructor
41408  * @param {Object} config Configuration options
41409  */
41410 Roo.form.Layout = function(config){
41411     var xitems = [];
41412     if (config.items) {
41413         xitems = config.items;
41414         delete config.items;
41415     }
41416     Roo.form.Layout.superclass.constructor.call(this, config);
41417     this.stack = [];
41418     Roo.each(xitems, this.addxtype, this);
41419      
41420 };
41421
41422 Roo.extend(Roo.form.Layout, Roo.Component, {
41423     /**
41424      * @cfg {String/Object} autoCreate
41425      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41426      */
41427     /**
41428      * @cfg {String/Object/Function} style
41429      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41430      * a function which returns such a specification.
41431      */
41432     /**
41433      * @cfg {String} labelAlign
41434      * Valid values are "left," "top" and "right" (defaults to "left")
41435      */
41436     /**
41437      * @cfg {Number} labelWidth
41438      * Fixed width in pixels of all field labels (defaults to undefined)
41439      */
41440     /**
41441      * @cfg {Boolean} clear
41442      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41443      */
41444     clear : true,
41445     /**
41446      * @cfg {String} labelSeparator
41447      * The separator to use after field labels (defaults to ':')
41448      */
41449     labelSeparator : ':',
41450     /**
41451      * @cfg {Boolean} hideLabels
41452      * True to suppress the display of field labels in this layout (defaults to false)
41453      */
41454     hideLabels : false,
41455
41456     // private
41457     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41458     
41459     isLayout : true,
41460     
41461     // private
41462     onRender : function(ct, position){
41463         if(this.el){ // from markup
41464             this.el = Roo.get(this.el);
41465         }else {  // generate
41466             var cfg = this.getAutoCreate();
41467             this.el = ct.createChild(cfg, position);
41468         }
41469         if(this.style){
41470             this.el.applyStyles(this.style);
41471         }
41472         if(this.labelAlign){
41473             this.el.addClass('x-form-label-'+this.labelAlign);
41474         }
41475         if(this.hideLabels){
41476             this.labelStyle = "display:none";
41477             this.elementStyle = "padding-left:0;";
41478         }else{
41479             if(typeof this.labelWidth == 'number'){
41480                 this.labelStyle = "width:"+this.labelWidth+"px;";
41481                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41482             }
41483             if(this.labelAlign == 'top'){
41484                 this.labelStyle = "width:auto;";
41485                 this.elementStyle = "padding-left:0;";
41486             }
41487         }
41488         var stack = this.stack;
41489         var slen = stack.length;
41490         if(slen > 0){
41491             if(!this.fieldTpl){
41492                 var t = new Roo.Template(
41493                     '<div class="x-form-item {5}">',
41494                         '<label for="{0}" style="{2}">{1}{4}</label>',
41495                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41496                         '</div>',
41497                     '</div><div class="x-form-clear-left"></div>'
41498                 );
41499                 t.disableFormats = true;
41500                 t.compile();
41501                 Roo.form.Layout.prototype.fieldTpl = t;
41502             }
41503             for(var i = 0; i < slen; i++) {
41504                 if(stack[i].isFormField){
41505                     this.renderField(stack[i]);
41506                 }else{
41507                     this.renderComponent(stack[i]);
41508                 }
41509             }
41510         }
41511         if(this.clear){
41512             this.el.createChild({cls:'x-form-clear'});
41513         }
41514     },
41515
41516     // private
41517     renderField : function(f){
41518         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41519                f.id, //0
41520                f.fieldLabel, //1
41521                f.labelStyle||this.labelStyle||'', //2
41522                this.elementStyle||'', //3
41523                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41524                f.itemCls||this.itemCls||''  //5
41525        ], true).getPrevSibling());
41526     },
41527
41528     // private
41529     renderComponent : function(c){
41530         c.render(c.isLayout ? this.el : this.el.createChild());    
41531     },
41532     /**
41533      * Adds a object form elements (using the xtype property as the factory method.)
41534      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41535      * @param {Object} config 
41536      */
41537     addxtype : function(o)
41538     {
41539         // create the lement.
41540         o.form = this.form;
41541         var fe = Roo.factory(o, Roo.form);
41542         this.form.allItems.push(fe);
41543         this.stack.push(fe);
41544         
41545         if (fe.isFormField) {
41546             this.form.items.add(fe);
41547         }
41548          
41549         return fe;
41550     }
41551 });
41552
41553 /**
41554  * @class Roo.form.Column
41555  * @extends Roo.form.Layout
41556  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41557  * @constructor
41558  * @param {Object} config Configuration options
41559  */
41560 Roo.form.Column = function(config){
41561     Roo.form.Column.superclass.constructor.call(this, config);
41562 };
41563
41564 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41565     /**
41566      * @cfg {Number/String} width
41567      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41568      */
41569     /**
41570      * @cfg {String/Object} autoCreate
41571      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41572      */
41573
41574     // private
41575     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41576
41577     // private
41578     onRender : function(ct, position){
41579         Roo.form.Column.superclass.onRender.call(this, ct, position);
41580         if(this.width){
41581             this.el.setWidth(this.width);
41582         }
41583     }
41584 });
41585
41586
41587 /**
41588  * @class Roo.form.Row
41589  * @extends Roo.form.Layout
41590  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41591  * @constructor
41592  * @param {Object} config Configuration options
41593  */
41594
41595  
41596 Roo.form.Row = function(config){
41597     Roo.form.Row.superclass.constructor.call(this, config);
41598 };
41599  
41600 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41601       /**
41602      * @cfg {Number/String} width
41603      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41604      */
41605     /**
41606      * @cfg {Number/String} height
41607      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41608      */
41609     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41610     
41611     padWidth : 20,
41612     // private
41613     onRender : function(ct, position){
41614         //console.log('row render');
41615         if(!this.rowTpl){
41616             var t = new Roo.Template(
41617                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41618                     '<label for="{0}" style="{2}">{1}{4}</label>',
41619                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41620                     '</div>',
41621                 '</div>'
41622             );
41623             t.disableFormats = true;
41624             t.compile();
41625             Roo.form.Layout.prototype.rowTpl = t;
41626         }
41627         this.fieldTpl = this.rowTpl;
41628         
41629         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41630         var labelWidth = 100;
41631         
41632         if ((this.labelAlign != 'top')) {
41633             if (typeof this.labelWidth == 'number') {
41634                 labelWidth = this.labelWidth
41635             }
41636             this.padWidth =  20 + labelWidth;
41637             
41638         }
41639         
41640         Roo.form.Column.superclass.onRender.call(this, ct, position);
41641         if(this.width){
41642             this.el.setWidth(this.width);
41643         }
41644         if(this.height){
41645             this.el.setHeight(this.height);
41646         }
41647     },
41648     
41649     // private
41650     renderField : function(f){
41651         f.fieldEl = this.fieldTpl.append(this.el, [
41652                f.id, f.fieldLabel,
41653                f.labelStyle||this.labelStyle||'',
41654                this.elementStyle||'',
41655                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41656                f.itemCls||this.itemCls||'',
41657                f.width ? f.width + this.padWidth : 160 + this.padWidth
41658        ],true);
41659     }
41660 });
41661  
41662
41663 /**
41664  * @class Roo.form.FieldSet
41665  * @extends Roo.form.Layout
41666  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41667  * @constructor
41668  * @param {Object} config Configuration options
41669  */
41670 Roo.form.FieldSet = function(config){
41671     Roo.form.FieldSet.superclass.constructor.call(this, config);
41672 };
41673
41674 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41675     /**
41676      * @cfg {String} legend
41677      * The text to display as the legend for the FieldSet (defaults to '')
41678      */
41679     /**
41680      * @cfg {String/Object} autoCreate
41681      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41682      */
41683
41684     // private
41685     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41686
41687     // private
41688     onRender : function(ct, position){
41689         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41690         if(this.legend){
41691             this.setLegend(this.legend);
41692         }
41693     },
41694
41695     // private
41696     setLegend : function(text){
41697         if(this.rendered){
41698             this.el.child('legend').update(text);
41699         }
41700     }
41701 });/*
41702  * Based on:
41703  * Ext JS Library 1.1.1
41704  * Copyright(c) 2006-2007, Ext JS, LLC.
41705  *
41706  * Originally Released Under LGPL - original licence link has changed is not relivant.
41707  *
41708  * Fork - LGPL
41709  * <script type="text/javascript">
41710  */
41711 /**
41712  * @class Roo.form.VTypes
41713  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41714  * @singleton
41715  */
41716 Roo.form.VTypes = function(){
41717     // closure these in so they are only created once.
41718     var alpha = /^[a-zA-Z_]+$/;
41719     var alphanum = /^[a-zA-Z0-9_]+$/;
41720     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41721     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41722
41723     // All these messages and functions are configurable
41724     return {
41725         /**
41726          * The function used to validate email addresses
41727          * @param {String} value The email address
41728          */
41729         'email' : function(v){
41730             return email.test(v);
41731         },
41732         /**
41733          * The error text to display when the email validation function returns false
41734          * @type String
41735          */
41736         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41737         /**
41738          * The keystroke filter mask to be applied on email input
41739          * @type RegExp
41740          */
41741         'emailMask' : /[a-z0-9_\.\-@]/i,
41742
41743         /**
41744          * The function used to validate URLs
41745          * @param {String} value The URL
41746          */
41747         'url' : function(v){
41748             return url.test(v);
41749         },
41750         /**
41751          * The error text to display when the url validation function returns false
41752          * @type String
41753          */
41754         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41755         
41756         /**
41757          * The function used to validate alpha values
41758          * @param {String} value The value
41759          */
41760         'alpha' : function(v){
41761             return alpha.test(v);
41762         },
41763         /**
41764          * The error text to display when the alpha validation function returns false
41765          * @type String
41766          */
41767         'alphaText' : 'This field should only contain letters and _',
41768         /**
41769          * The keystroke filter mask to be applied on alpha input
41770          * @type RegExp
41771          */
41772         'alphaMask' : /[a-z_]/i,
41773
41774         /**
41775          * The function used to validate alphanumeric values
41776          * @param {String} value The value
41777          */
41778         'alphanum' : function(v){
41779             return alphanum.test(v);
41780         },
41781         /**
41782          * The error text to display when the alphanumeric validation function returns false
41783          * @type String
41784          */
41785         'alphanumText' : 'This field should only contain letters, numbers and _',
41786         /**
41787          * The keystroke filter mask to be applied on alphanumeric input
41788          * @type RegExp
41789          */
41790         'alphanumMask' : /[a-z0-9_]/i
41791     };
41792 }();//<script type="text/javascript">
41793
41794 /**
41795  * @class Roo.form.FCKeditor
41796  * @extends Roo.form.TextArea
41797  * Wrapper around the FCKEditor http://www.fckeditor.net
41798  * @constructor
41799  * Creates a new FCKeditor
41800  * @param {Object} config Configuration options
41801  */
41802 Roo.form.FCKeditor = function(config){
41803     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41804     this.addEvents({
41805          /**
41806          * @event editorinit
41807          * Fired when the editor is initialized - you can add extra handlers here..
41808          * @param {FCKeditor} this
41809          * @param {Object} the FCK object.
41810          */
41811         editorinit : true
41812     });
41813     
41814     
41815 };
41816 Roo.form.FCKeditor.editors = { };
41817 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41818 {
41819     //defaultAutoCreate : {
41820     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41821     //},
41822     // private
41823     /**
41824      * @cfg {Object} fck options - see fck manual for details.
41825      */
41826     fckconfig : false,
41827     
41828     /**
41829      * @cfg {Object} fck toolbar set (Basic or Default)
41830      */
41831     toolbarSet : 'Basic',
41832     /**
41833      * @cfg {Object} fck BasePath
41834      */ 
41835     basePath : '/fckeditor/',
41836     
41837     
41838     frame : false,
41839     
41840     value : '',
41841     
41842    
41843     onRender : function(ct, position)
41844     {
41845         if(!this.el){
41846             this.defaultAutoCreate = {
41847                 tag: "textarea",
41848                 style:"width:300px;height:60px;",
41849                 autocomplete: "off"
41850             };
41851         }
41852         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41853         /*
41854         if(this.grow){
41855             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41856             if(this.preventScrollbars){
41857                 this.el.setStyle("overflow", "hidden");
41858             }
41859             this.el.setHeight(this.growMin);
41860         }
41861         */
41862         //console.log('onrender' + this.getId() );
41863         Roo.form.FCKeditor.editors[this.getId()] = this;
41864          
41865
41866         this.replaceTextarea() ;
41867         
41868     },
41869     
41870     getEditor : function() {
41871         return this.fckEditor;
41872     },
41873     /**
41874      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41875      * @param {Mixed} value The value to set
41876      */
41877     
41878     
41879     setValue : function(value)
41880     {
41881         //console.log('setValue: ' + value);
41882         
41883         if(typeof(value) == 'undefined') { // not sure why this is happending...
41884             return;
41885         }
41886         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41887         
41888         //if(!this.el || !this.getEditor()) {
41889         //    this.value = value;
41890             //this.setValue.defer(100,this,[value]);    
41891         //    return;
41892         //} 
41893         
41894         if(!this.getEditor()) {
41895             return;
41896         }
41897         
41898         this.getEditor().SetData(value);
41899         
41900         //
41901
41902     },
41903
41904     /**
41905      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41906      * @return {Mixed} value The field value
41907      */
41908     getValue : function()
41909     {
41910         
41911         if (this.frame && this.frame.dom.style.display == 'none') {
41912             return Roo.form.FCKeditor.superclass.getValue.call(this);
41913         }
41914         
41915         if(!this.el || !this.getEditor()) {
41916            
41917            // this.getValue.defer(100,this); 
41918             return this.value;
41919         }
41920        
41921         
41922         var value=this.getEditor().GetData();
41923         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41924         return Roo.form.FCKeditor.superclass.getValue.call(this);
41925         
41926
41927     },
41928
41929     /**
41930      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41931      * @return {Mixed} value The field value
41932      */
41933     getRawValue : function()
41934     {
41935         if (this.frame && this.frame.dom.style.display == 'none') {
41936             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41937         }
41938         
41939         if(!this.el || !this.getEditor()) {
41940             //this.getRawValue.defer(100,this); 
41941             return this.value;
41942             return;
41943         }
41944         
41945         
41946         
41947         var value=this.getEditor().GetData();
41948         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41949         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41950          
41951     },
41952     
41953     setSize : function(w,h) {
41954         
41955         
41956         
41957         //if (this.frame && this.frame.dom.style.display == 'none') {
41958         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41959         //    return;
41960         //}
41961         //if(!this.el || !this.getEditor()) {
41962         //    this.setSize.defer(100,this, [w,h]); 
41963         //    return;
41964         //}
41965         
41966         
41967         
41968         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41969         
41970         this.frame.dom.setAttribute('width', w);
41971         this.frame.dom.setAttribute('height', h);
41972         this.frame.setSize(w,h);
41973         
41974     },
41975     
41976     toggleSourceEdit : function(value) {
41977         
41978       
41979          
41980         this.el.dom.style.display = value ? '' : 'none';
41981         this.frame.dom.style.display = value ?  'none' : '';
41982         
41983     },
41984     
41985     
41986     focus: function(tag)
41987     {
41988         if (this.frame.dom.style.display == 'none') {
41989             return Roo.form.FCKeditor.superclass.focus.call(this);
41990         }
41991         if(!this.el || !this.getEditor()) {
41992             this.focus.defer(100,this, [tag]); 
41993             return;
41994         }
41995         
41996         
41997         
41998         
41999         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42000         this.getEditor().Focus();
42001         if (tgs.length) {
42002             if (!this.getEditor().Selection.GetSelection()) {
42003                 this.focus.defer(100,this, [tag]); 
42004                 return;
42005             }
42006             
42007             
42008             var r = this.getEditor().EditorDocument.createRange();
42009             r.setStart(tgs[0],0);
42010             r.setEnd(tgs[0],0);
42011             this.getEditor().Selection.GetSelection().removeAllRanges();
42012             this.getEditor().Selection.GetSelection().addRange(r);
42013             this.getEditor().Focus();
42014         }
42015         
42016     },
42017     
42018     
42019     
42020     replaceTextarea : function()
42021     {
42022         if ( document.getElementById( this.getId() + '___Frame' ) )
42023             return ;
42024         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42025         //{
42026             // We must check the elements firstly using the Id and then the name.
42027         var oTextarea = document.getElementById( this.getId() );
42028         
42029         var colElementsByName = document.getElementsByName( this.getId() ) ;
42030          
42031         oTextarea.style.display = 'none' ;
42032
42033         if ( oTextarea.tabIndex ) {            
42034             this.TabIndex = oTextarea.tabIndex ;
42035         }
42036         
42037         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42038         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42039         this.frame = Roo.get(this.getId() + '___Frame')
42040     },
42041     
42042     _getConfigHtml : function()
42043     {
42044         var sConfig = '' ;
42045
42046         for ( var o in this.fckconfig ) {
42047             sConfig += sConfig.length > 0  ? '&amp;' : '';
42048             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42049         }
42050
42051         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42052     },
42053     
42054     
42055     _getIFrameHtml : function()
42056     {
42057         var sFile = 'fckeditor.html' ;
42058         /* no idea what this is about..
42059         try
42060         {
42061             if ( (/fcksource=true/i).test( window.top.location.search ) )
42062                 sFile = 'fckeditor.original.html' ;
42063         }
42064         catch (e) { 
42065         */
42066
42067         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42068         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42069         
42070         
42071         var html = '<iframe id="' + this.getId() +
42072             '___Frame" src="' + sLink +
42073             '" width="' + this.width +
42074             '" height="' + this.height + '"' +
42075             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42076             ' frameborder="0" scrolling="no"></iframe>' ;
42077
42078         return html ;
42079     },
42080     
42081     _insertHtmlBefore : function( html, element )
42082     {
42083         if ( element.insertAdjacentHTML )       {
42084             // IE
42085             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42086         } else { // Gecko
42087             var oRange = document.createRange() ;
42088             oRange.setStartBefore( element ) ;
42089             var oFragment = oRange.createContextualFragment( html );
42090             element.parentNode.insertBefore( oFragment, element ) ;
42091         }
42092     }
42093     
42094     
42095   
42096     
42097     
42098     
42099     
42100
42101 });
42102
42103 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42104
42105 function FCKeditor_OnComplete(editorInstance){
42106     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42107     f.fckEditor = editorInstance;
42108     //console.log("loaded");
42109     f.fireEvent('editorinit', f, editorInstance);
42110
42111   
42112
42113  
42114
42115
42116
42117
42118
42119
42120
42121
42122
42123
42124
42125
42126
42127
42128
42129 //<script type="text/javascript">
42130 /**
42131  * @class Roo.form.GridField
42132  * @extends Roo.form.Field
42133  * Embed a grid (or editable grid into a form)
42134  * STATUS ALPHA
42135  * 
42136  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42137  * it needs 
42138  * xgrid.store = Roo.data.Store
42139  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42140  * xgrid.store.reader = Roo.data.JsonReader 
42141  * 
42142  * 
42143  * @constructor
42144  * Creates a new GridField
42145  * @param {Object} config Configuration options
42146  */
42147 Roo.form.GridField = function(config){
42148     Roo.form.GridField.superclass.constructor.call(this, config);
42149      
42150 };
42151
42152 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42153     /**
42154      * @cfg {Number} width  - used to restrict width of grid..
42155      */
42156     width : 100,
42157     /**
42158      * @cfg {Number} height - used to restrict height of grid..
42159      */
42160     height : 50,
42161      /**
42162      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42163          * 
42164          *}
42165      */
42166     xgrid : false, 
42167     /**
42168      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42169      * {tag: "input", type: "checkbox", autocomplete: "off"})
42170      */
42171    // defaultAutoCreate : { tag: 'div' },
42172     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42173     /**
42174      * @cfg {String} addTitle Text to include for adding a title.
42175      */
42176     addTitle : false,
42177     //
42178     onResize : function(){
42179         Roo.form.Field.superclass.onResize.apply(this, arguments);
42180     },
42181
42182     initEvents : function(){
42183         // Roo.form.Checkbox.superclass.initEvents.call(this);
42184         // has no events...
42185        
42186     },
42187
42188
42189     getResizeEl : function(){
42190         return this.wrap;
42191     },
42192
42193     getPositionEl : function(){
42194         return this.wrap;
42195     },
42196
42197     // private
42198     onRender : function(ct, position){
42199         
42200         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42201         var style = this.style;
42202         delete this.style;
42203         
42204         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42205         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42206         this.viewEl = this.wrap.createChild({ tag: 'div' });
42207         if (style) {
42208             this.viewEl.applyStyles(style);
42209         }
42210         if (this.width) {
42211             this.viewEl.setWidth(this.width);
42212         }
42213         if (this.height) {
42214             this.viewEl.setHeight(this.height);
42215         }
42216         //if(this.inputValue !== undefined){
42217         //this.setValue(this.value);
42218         
42219         
42220         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42221         
42222         
42223         this.grid.render();
42224         this.grid.getDataSource().on('remove', this.refreshValue, this);
42225         this.grid.getDataSource().on('update', this.refreshValue, this);
42226         this.grid.on('afteredit', this.refreshValue, this);
42227  
42228     },
42229      
42230     
42231     /**
42232      * Sets the value of the item. 
42233      * @param {String} either an object  or a string..
42234      */
42235     setValue : function(v){
42236         //this.value = v;
42237         v = v || []; // empty set..
42238         // this does not seem smart - it really only affects memoryproxy grids..
42239         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42240             var ds = this.grid.getDataSource();
42241             // assumes a json reader..
42242             var data = {}
42243             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42244             ds.loadData( data);
42245         }
42246         Roo.form.GridField.superclass.setValue.call(this, v);
42247         this.refreshValue();
42248         // should load data in the grid really....
42249     },
42250     
42251     // private
42252     refreshValue: function() {
42253          var val = [];
42254         this.grid.getDataSource().each(function(r) {
42255             val.push(r.data);
42256         });
42257         this.el.dom.value = Roo.encode(val);
42258     }
42259     
42260      
42261     
42262     
42263 });/*
42264  * Based on:
42265  * Ext JS Library 1.1.1
42266  * Copyright(c) 2006-2007, Ext JS, LLC.
42267  *
42268  * Originally Released Under LGPL - original licence link has changed is not relivant.
42269  *
42270  * Fork - LGPL
42271  * <script type="text/javascript">
42272  */
42273 /**
42274  * @class Roo.form.DisplayField
42275  * @extends Roo.form.Field
42276  * A generic Field to display non-editable data.
42277  * @constructor
42278  * Creates a new Display Field item.
42279  * @param {Object} config Configuration options
42280  */
42281 Roo.form.DisplayField = function(config){
42282     Roo.form.DisplayField.superclass.constructor.call(this, config);
42283     
42284 };
42285
42286 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42287     inputType:      'hidden',
42288     allowBlank:     true,
42289     readOnly:         true,
42290     
42291  
42292     /**
42293      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42294      */
42295     focusClass : undefined,
42296     /**
42297      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42298      */
42299     fieldClass: 'x-form-field',
42300     
42301      /**
42302      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42303      */
42304     valueRenderer: undefined,
42305     
42306     width: 100,
42307     /**
42308      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42309      * {tag: "input", type: "checkbox", autocomplete: "off"})
42310      */
42311      
42312  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42313
42314     onResize : function(){
42315         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42316         
42317     },
42318
42319     initEvents : function(){
42320         // Roo.form.Checkbox.superclass.initEvents.call(this);
42321         // has no events...
42322        
42323     },
42324
42325
42326     getResizeEl : function(){
42327         return this.wrap;
42328     },
42329
42330     getPositionEl : function(){
42331         return this.wrap;
42332     },
42333
42334     // private
42335     onRender : function(ct, position){
42336         
42337         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42338         //if(this.inputValue !== undefined){
42339         this.wrap = this.el.wrap();
42340         
42341         this.viewEl = this.wrap.createChild({ tag: 'div'});
42342         
42343         if (this.bodyStyle) {
42344             this.viewEl.applyStyles(this.bodyStyle);
42345         }
42346         //this.viewEl.setStyle('padding', '2px');
42347         
42348         this.setValue(this.value);
42349         
42350     },
42351 /*
42352     // private
42353     initValue : Roo.emptyFn,
42354
42355   */
42356
42357         // private
42358     onClick : function(){
42359         
42360     },
42361
42362     /**
42363      * Sets the checked state of the checkbox.
42364      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42365      */
42366     setValue : function(v){
42367         this.value = v;
42368         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42369         // this might be called before we have a dom element..
42370         if (!this.viewEl) {
42371             return;
42372         }
42373         this.viewEl.dom.innerHTML = html;
42374         Roo.form.DisplayField.superclass.setValue.call(this, v);
42375
42376     }
42377 });//<script type="text/javasscript">
42378  
42379
42380 /**
42381  * @class Roo.DDView
42382  * A DnD enabled version of Roo.View.
42383  * @param {Element/String} container The Element in which to create the View.
42384  * @param {String} tpl The template string used to create the markup for each element of the View
42385  * @param {Object} config The configuration properties. These include all the config options of
42386  * {@link Roo.View} plus some specific to this class.<br>
42387  * <p>
42388  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42389  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42390  * <p>
42391  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42392 .x-view-drag-insert-above {
42393         border-top:1px dotted #3366cc;
42394 }
42395 .x-view-drag-insert-below {
42396         border-bottom:1px dotted #3366cc;
42397 }
42398 </code></pre>
42399  * 
42400  */
42401  
42402 Roo.DDView = function(container, tpl, config) {
42403     Roo.DDView.superclass.constructor.apply(this, arguments);
42404     this.getEl().setStyle("outline", "0px none");
42405     this.getEl().unselectable();
42406     if (this.dragGroup) {
42407                 this.setDraggable(this.dragGroup.split(","));
42408     }
42409     if (this.dropGroup) {
42410                 this.setDroppable(this.dropGroup.split(","));
42411     }
42412     if (this.deletable) {
42413         this.setDeletable();
42414     }
42415     this.isDirtyFlag = false;
42416         this.addEvents({
42417                 "drop" : true
42418         });
42419 };
42420
42421 Roo.extend(Roo.DDView, Roo.View, {
42422 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42423 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42424 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42425 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42426
42427         isFormField: true,
42428
42429         reset: Roo.emptyFn,
42430         
42431         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42432
42433         validate: function() {
42434                 return true;
42435         },
42436         
42437         destroy: function() {
42438                 this.purgeListeners();
42439                 this.getEl.removeAllListeners();
42440                 this.getEl().remove();
42441                 if (this.dragZone) {
42442                         if (this.dragZone.destroy) {
42443                                 this.dragZone.destroy();
42444                         }
42445                 }
42446                 if (this.dropZone) {
42447                         if (this.dropZone.destroy) {
42448                                 this.dropZone.destroy();
42449                         }
42450                 }
42451         },
42452
42453 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42454         getName: function() {
42455                 return this.name;
42456         },
42457
42458 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42459         setValue: function(v) {
42460                 if (!this.store) {
42461                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42462                 }
42463                 var data = {};
42464                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42465                 this.store.proxy = new Roo.data.MemoryProxy(data);
42466                 this.store.load();
42467         },
42468
42469 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42470         getValue: function() {
42471                 var result = '(';
42472                 this.store.each(function(rec) {
42473                         result += rec.id + ',';
42474                 });
42475                 return result.substr(0, result.length - 1) + ')';
42476         },
42477         
42478         getIds: function() {
42479                 var i = 0, result = new Array(this.store.getCount());
42480                 this.store.each(function(rec) {
42481                         result[i++] = rec.id;
42482                 });
42483                 return result;
42484         },
42485         
42486         isDirty: function() {
42487                 return this.isDirtyFlag;
42488         },
42489
42490 /**
42491  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42492  *      whole Element becomes the target, and this causes the drop gesture to append.
42493  */
42494     getTargetFromEvent : function(e) {
42495                 var target = e.getTarget();
42496                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42497                 target = target.parentNode;
42498                 }
42499                 if (!target) {
42500                         target = this.el.dom.lastChild || this.el.dom;
42501                 }
42502                 return target;
42503     },
42504
42505 /**
42506  *      Create the drag data which consists of an object which has the property "ddel" as
42507  *      the drag proxy element. 
42508  */
42509     getDragData : function(e) {
42510         var target = this.findItemFromChild(e.getTarget());
42511                 if(target) {
42512                         this.handleSelection(e);
42513                         var selNodes = this.getSelectedNodes();
42514             var dragData = {
42515                 source: this,
42516                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42517                 nodes: selNodes,
42518                 records: []
42519                         };
42520                         var selectedIndices = this.getSelectedIndexes();
42521                         for (var i = 0; i < selectedIndices.length; i++) {
42522                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42523                         }
42524                         if (selNodes.length == 1) {
42525                                 dragData.ddel = target.cloneNode(true); // the div element
42526                         } else {
42527                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42528                                 div.className = 'multi-proxy';
42529                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42530                                         div.appendChild(selNodes[i].cloneNode(true));
42531                                 }
42532                                 dragData.ddel = div;
42533                         }
42534             //console.log(dragData)
42535             //console.log(dragData.ddel.innerHTML)
42536                         return dragData;
42537                 }
42538         //console.log('nodragData')
42539                 return false;
42540     },
42541     
42542 /**     Specify to which ddGroup items in this DDView may be dragged. */
42543     setDraggable: function(ddGroup) {
42544         if (ddGroup instanceof Array) {
42545                 Roo.each(ddGroup, this.setDraggable, this);
42546                 return;
42547         }
42548         if (this.dragZone) {
42549                 this.dragZone.addToGroup(ddGroup);
42550         } else {
42551                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42552                                 containerScroll: true,
42553                                 ddGroup: ddGroup 
42554
42555                         });
42556 //                      Draggability implies selection. DragZone's mousedown selects the element.
42557                         if (!this.multiSelect) { this.singleSelect = true; }
42558
42559 //                      Wire the DragZone's handlers up to methods in *this*
42560                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42561                 }
42562     },
42563
42564 /**     Specify from which ddGroup this DDView accepts drops. */
42565     setDroppable: function(ddGroup) {
42566         if (ddGroup instanceof Array) {
42567                 Roo.each(ddGroup, this.setDroppable, this);
42568                 return;
42569         }
42570         if (this.dropZone) {
42571                 this.dropZone.addToGroup(ddGroup);
42572         } else {
42573                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42574                                 containerScroll: true,
42575                                 ddGroup: ddGroup
42576                         });
42577
42578 //                      Wire the DropZone's handlers up to methods in *this*
42579                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42580                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42581                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42582                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42583                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42584                 }
42585     },
42586
42587 /**     Decide whether to drop above or below a View node. */
42588     getDropPoint : function(e, n, dd){
42589         if (n == this.el.dom) { return "above"; }
42590                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42591                 var c = t + (b - t) / 2;
42592                 var y = Roo.lib.Event.getPageY(e);
42593                 if(y <= c) {
42594                         return "above";
42595                 }else{
42596                         return "below";
42597                 }
42598     },
42599
42600     onNodeEnter : function(n, dd, e, data){
42601                 return false;
42602     },
42603     
42604     onNodeOver : function(n, dd, e, data){
42605                 var pt = this.getDropPoint(e, n, dd);
42606                 // set the insert point style on the target node
42607                 var dragElClass = this.dropNotAllowed;
42608                 if (pt) {
42609                         var targetElClass;
42610                         if (pt == "above"){
42611                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42612                                 targetElClass = "x-view-drag-insert-above";
42613                         } else {
42614                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42615                                 targetElClass = "x-view-drag-insert-below";
42616                         }
42617                         if (this.lastInsertClass != targetElClass){
42618                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42619                                 this.lastInsertClass = targetElClass;
42620                         }
42621                 }
42622                 return dragElClass;
42623         },
42624
42625     onNodeOut : function(n, dd, e, data){
42626                 this.removeDropIndicators(n);
42627     },
42628
42629     onNodeDrop : function(n, dd, e, data){
42630         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42631                 return false;
42632         }
42633         var pt = this.getDropPoint(e, n, dd);
42634                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42635                 if (pt == "below") { insertAt++; }
42636                 for (var i = 0; i < data.records.length; i++) {
42637                         var r = data.records[i];
42638                         var dup = this.store.getById(r.id);
42639                         if (dup && (dd != this.dragZone)) {
42640                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42641                         } else {
42642                                 if (data.copy) {
42643                                         this.store.insert(insertAt++, r.copy());
42644                                 } else {
42645                                         data.source.isDirtyFlag = true;
42646                                         r.store.remove(r);
42647                                         this.store.insert(insertAt++, r);
42648                                 }
42649                                 this.isDirtyFlag = true;
42650                         }
42651                 }
42652                 this.dragZone.cachedTarget = null;
42653                 return true;
42654     },
42655
42656     removeDropIndicators : function(n){
42657                 if(n){
42658                         Roo.fly(n).removeClass([
42659                                 "x-view-drag-insert-above",
42660                                 "x-view-drag-insert-below"]);
42661                         this.lastInsertClass = "_noclass";
42662                 }
42663     },
42664
42665 /**
42666  *      Utility method. Add a delete option to the DDView's context menu.
42667  *      @param {String} imageUrl The URL of the "delete" icon image.
42668  */
42669         setDeletable: function(imageUrl) {
42670                 if (!this.singleSelect && !this.multiSelect) {
42671                         this.singleSelect = true;
42672                 }
42673                 var c = this.getContextMenu();
42674                 this.contextMenu.on("itemclick", function(item) {
42675                         switch (item.id) {
42676                                 case "delete":
42677                                         this.remove(this.getSelectedIndexes());
42678                                         break;
42679                         }
42680                 }, this);
42681                 this.contextMenu.add({
42682                         icon: imageUrl,
42683                         id: "delete",
42684                         text: 'Delete'
42685                 });
42686         },
42687         
42688 /**     Return the context menu for this DDView. */
42689         getContextMenu: function() {
42690                 if (!this.contextMenu) {
42691 //                      Create the View's context menu
42692                         this.contextMenu = new Roo.menu.Menu({
42693                                 id: this.id + "-contextmenu"
42694                         });
42695                         this.el.on("contextmenu", this.showContextMenu, this);
42696                 }
42697                 return this.contextMenu;
42698         },
42699         
42700         disableContextMenu: function() {
42701                 if (this.contextMenu) {
42702                         this.el.un("contextmenu", this.showContextMenu, this);
42703                 }
42704         },
42705
42706         showContextMenu: function(e, item) {
42707         item = this.findItemFromChild(e.getTarget());
42708                 if (item) {
42709                         e.stopEvent();
42710                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42711                         this.contextMenu.showAt(e.getXY());
42712             }
42713     },
42714
42715 /**
42716  *      Remove {@link Roo.data.Record}s at the specified indices.
42717  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42718  */
42719     remove: function(selectedIndices) {
42720                 selectedIndices = [].concat(selectedIndices);
42721                 for (var i = 0; i < selectedIndices.length; i++) {
42722                         var rec = this.store.getAt(selectedIndices[i]);
42723                         this.store.remove(rec);
42724                 }
42725     },
42726
42727 /**
42728  *      Double click fires the event, but also, if this is draggable, and there is only one other
42729  *      related DropZone, it transfers the selected node.
42730  */
42731     onDblClick : function(e){
42732         var item = this.findItemFromChild(e.getTarget());
42733         if(item){
42734             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42735                 return false;
42736             }
42737             if (this.dragGroup) {
42738                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42739                     while (targets.indexOf(this.dropZone) > -1) {
42740                             targets.remove(this.dropZone);
42741                                 }
42742                     if (targets.length == 1) {
42743                                         this.dragZone.cachedTarget = null;
42744                         var el = Roo.get(targets[0].getEl());
42745                         var box = el.getBox(true);
42746                         targets[0].onNodeDrop(el.dom, {
42747                                 target: el.dom,
42748                                 xy: [box.x, box.y + box.height - 1]
42749                         }, null, this.getDragData(e));
42750                     }
42751                 }
42752         }
42753     },
42754     
42755     handleSelection: function(e) {
42756                 this.dragZone.cachedTarget = null;
42757         var item = this.findItemFromChild(e.getTarget());
42758         if (!item) {
42759                 this.clearSelections(true);
42760                 return;
42761         }
42762                 if (item && (this.multiSelect || this.singleSelect)){
42763                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42764                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42765                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42766                                 this.unselect(item);
42767                         } else {
42768                                 this.select(item, this.multiSelect && e.ctrlKey);
42769                                 this.lastSelection = item;
42770                         }
42771                 }
42772     },
42773
42774     onItemClick : function(item, index, e){
42775                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42776                         return false;
42777                 }
42778                 return true;
42779     },
42780
42781     unselect : function(nodeInfo, suppressEvent){
42782                 var node = this.getNode(nodeInfo);
42783                 if(node && this.isSelected(node)){
42784                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42785                                 Roo.fly(node).removeClass(this.selectedClass);
42786                                 this.selections.remove(node);
42787                                 if(!suppressEvent){
42788                                         this.fireEvent("selectionchange", this, this.selections);
42789                                 }
42790                         }
42791                 }
42792     }
42793 });
42794 /*
42795  * Based on:
42796  * Ext JS Library 1.1.1
42797  * Copyright(c) 2006-2007, Ext JS, LLC.
42798  *
42799  * Originally Released Under LGPL - original licence link has changed is not relivant.
42800  *
42801  * Fork - LGPL
42802  * <script type="text/javascript">
42803  */
42804  
42805 /**
42806  * @class Roo.LayoutManager
42807  * @extends Roo.util.Observable
42808  * Base class for layout managers.
42809  */
42810 Roo.LayoutManager = function(container, config){
42811     Roo.LayoutManager.superclass.constructor.call(this);
42812     this.el = Roo.get(container);
42813     // ie scrollbar fix
42814     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42815         document.body.scroll = "no";
42816     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42817         this.el.position('relative');
42818     }
42819     this.id = this.el.id;
42820     this.el.addClass("x-layout-container");
42821     /** false to disable window resize monitoring @type Boolean */
42822     this.monitorWindowResize = true;
42823     this.regions = {};
42824     this.addEvents({
42825         /**
42826          * @event layout
42827          * Fires when a layout is performed. 
42828          * @param {Roo.LayoutManager} this
42829          */
42830         "layout" : true,
42831         /**
42832          * @event regionresized
42833          * Fires when the user resizes a region. 
42834          * @param {Roo.LayoutRegion} region The resized region
42835          * @param {Number} newSize The new size (width for east/west, height for north/south)
42836          */
42837         "regionresized" : true,
42838         /**
42839          * @event regioncollapsed
42840          * Fires when a region is collapsed. 
42841          * @param {Roo.LayoutRegion} region The collapsed region
42842          */
42843         "regioncollapsed" : true,
42844         /**
42845          * @event regionexpanded
42846          * Fires when a region is expanded.  
42847          * @param {Roo.LayoutRegion} region The expanded region
42848          */
42849         "regionexpanded" : true
42850     });
42851     this.updating = false;
42852     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42853 };
42854
42855 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42856     /**
42857      * Returns true if this layout is currently being updated
42858      * @return {Boolean}
42859      */
42860     isUpdating : function(){
42861         return this.updating; 
42862     },
42863     
42864     /**
42865      * Suspend the LayoutManager from doing auto-layouts while
42866      * making multiple add or remove calls
42867      */
42868     beginUpdate : function(){
42869         this.updating = true;    
42870     },
42871     
42872     /**
42873      * Restore auto-layouts and optionally disable the manager from performing a layout
42874      * @param {Boolean} noLayout true to disable a layout update 
42875      */
42876     endUpdate : function(noLayout){
42877         this.updating = false;
42878         if(!noLayout){
42879             this.layout();
42880         }    
42881     },
42882     
42883     layout: function(){
42884         
42885     },
42886     
42887     onRegionResized : function(region, newSize){
42888         this.fireEvent("regionresized", region, newSize);
42889         this.layout();
42890     },
42891     
42892     onRegionCollapsed : function(region){
42893         this.fireEvent("regioncollapsed", region);
42894     },
42895     
42896     onRegionExpanded : function(region){
42897         this.fireEvent("regionexpanded", region);
42898     },
42899         
42900     /**
42901      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42902      * performs box-model adjustments.
42903      * @return {Object} The size as an object {width: (the width), height: (the height)}
42904      */
42905     getViewSize : function(){
42906         var size;
42907         if(this.el.dom != document.body){
42908             size = this.el.getSize();
42909         }else{
42910             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42911         }
42912         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42913         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42914         return size;
42915     },
42916     
42917     /**
42918      * Returns the Element this layout is bound to.
42919      * @return {Roo.Element}
42920      */
42921     getEl : function(){
42922         return this.el;
42923     },
42924     
42925     /**
42926      * Returns the specified region.
42927      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42928      * @return {Roo.LayoutRegion}
42929      */
42930     getRegion : function(target){
42931         return this.regions[target.toLowerCase()];
42932     },
42933     
42934     onWindowResize : function(){
42935         if(this.monitorWindowResize){
42936             this.layout();
42937         }
42938     }
42939 });/*
42940  * Based on:
42941  * Ext JS Library 1.1.1
42942  * Copyright(c) 2006-2007, Ext JS, LLC.
42943  *
42944  * Originally Released Under LGPL - original licence link has changed is not relivant.
42945  *
42946  * Fork - LGPL
42947  * <script type="text/javascript">
42948  */
42949 /**
42950  * @class Roo.BorderLayout
42951  * @extends Roo.LayoutManager
42952  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42953  * please see: <br><br>
42954  * <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>
42955  * <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>
42956  * Example:
42957  <pre><code>
42958  var layout = new Roo.BorderLayout(document.body, {
42959     north: {
42960         initialSize: 25,
42961         titlebar: false
42962     },
42963     west: {
42964         split:true,
42965         initialSize: 200,
42966         minSize: 175,
42967         maxSize: 400,
42968         titlebar: true,
42969         collapsible: true
42970     },
42971     east: {
42972         split:true,
42973         initialSize: 202,
42974         minSize: 175,
42975         maxSize: 400,
42976         titlebar: true,
42977         collapsible: true
42978     },
42979     south: {
42980         split:true,
42981         initialSize: 100,
42982         minSize: 100,
42983         maxSize: 200,
42984         titlebar: true,
42985         collapsible: true
42986     },
42987     center: {
42988         titlebar: true,
42989         autoScroll:true,
42990         resizeTabs: true,
42991         minTabWidth: 50,
42992         preferredTabWidth: 150
42993     }
42994 });
42995
42996 // shorthand
42997 var CP = Roo.ContentPanel;
42998
42999 layout.beginUpdate();
43000 layout.add("north", new CP("north", "North"));
43001 layout.add("south", new CP("south", {title: "South", closable: true}));
43002 layout.add("west", new CP("west", {title: "West"}));
43003 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43004 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43005 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43006 layout.getRegion("center").showPanel("center1");
43007 layout.endUpdate();
43008 </code></pre>
43009
43010 <b>The container the layout is rendered into can be either the body element or any other element.
43011 If it is not the body element, the container needs to either be an absolute positioned element,
43012 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43013 the container size if it is not the body element.</b>
43014
43015 * @constructor
43016 * Create a new BorderLayout
43017 * @param {String/HTMLElement/Element} container The container this layout is bound to
43018 * @param {Object} config Configuration options
43019  */
43020 Roo.BorderLayout = function(container, config){
43021     config = config || {};
43022     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43023     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43024     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43025         var target = this.factory.validRegions[i];
43026         if(config[target]){
43027             this.addRegion(target, config[target]);
43028         }
43029     }
43030 };
43031
43032 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43033     /**
43034      * Creates and adds a new region if it doesn't already exist.
43035      * @param {String} target The target region key (north, south, east, west or center).
43036      * @param {Object} config The regions config object
43037      * @return {BorderLayoutRegion} The new region
43038      */
43039     addRegion : function(target, config){
43040         if(!this.regions[target]){
43041             var r = this.factory.create(target, this, config);
43042             this.bindRegion(target, r);
43043         }
43044         return this.regions[target];
43045     },
43046
43047     // private (kinda)
43048     bindRegion : function(name, r){
43049         this.regions[name] = r;
43050         r.on("visibilitychange", this.layout, this);
43051         r.on("paneladded", this.layout, this);
43052         r.on("panelremoved", this.layout, this);
43053         r.on("invalidated", this.layout, this);
43054         r.on("resized", this.onRegionResized, this);
43055         r.on("collapsed", this.onRegionCollapsed, this);
43056         r.on("expanded", this.onRegionExpanded, this);
43057     },
43058
43059     /**
43060      * Performs a layout update.
43061      */
43062     layout : function(){
43063         if(this.updating) return;
43064         var size = this.getViewSize();
43065         var w = size.width;
43066         var h = size.height;
43067         var centerW = w;
43068         var centerH = h;
43069         var centerY = 0;
43070         var centerX = 0;
43071         //var x = 0, y = 0;
43072
43073         var rs = this.regions;
43074         var north = rs["north"];
43075         var south = rs["south"]; 
43076         var west = rs["west"];
43077         var east = rs["east"];
43078         var center = rs["center"];
43079         //if(this.hideOnLayout){ // not supported anymore
43080             //c.el.setStyle("display", "none");
43081         //}
43082         if(north && north.isVisible()){
43083             var b = north.getBox();
43084             var m = north.getMargins();
43085             b.width = w - (m.left+m.right);
43086             b.x = m.left;
43087             b.y = m.top;
43088             centerY = b.height + b.y + m.bottom;
43089             centerH -= centerY;
43090             north.updateBox(this.safeBox(b));
43091         }
43092         if(south && south.isVisible()){
43093             var b = south.getBox();
43094             var m = south.getMargins();
43095             b.width = w - (m.left+m.right);
43096             b.x = m.left;
43097             var totalHeight = (b.height + m.top + m.bottom);
43098             b.y = h - totalHeight + m.top;
43099             centerH -= totalHeight;
43100             south.updateBox(this.safeBox(b));
43101         }
43102         if(west && west.isVisible()){
43103             var b = west.getBox();
43104             var m = west.getMargins();
43105             b.height = centerH - (m.top+m.bottom);
43106             b.x = m.left;
43107             b.y = centerY + m.top;
43108             var totalWidth = (b.width + m.left + m.right);
43109             centerX += totalWidth;
43110             centerW -= totalWidth;
43111             west.updateBox(this.safeBox(b));
43112         }
43113         if(east && east.isVisible()){
43114             var b = east.getBox();
43115             var m = east.getMargins();
43116             b.height = centerH - (m.top+m.bottom);
43117             var totalWidth = (b.width + m.left + m.right);
43118             b.x = w - totalWidth + m.left;
43119             b.y = centerY + m.top;
43120             centerW -= totalWidth;
43121             east.updateBox(this.safeBox(b));
43122         }
43123         if(center){
43124             var m = center.getMargins();
43125             var centerBox = {
43126                 x: centerX + m.left,
43127                 y: centerY + m.top,
43128                 width: centerW - (m.left+m.right),
43129                 height: centerH - (m.top+m.bottom)
43130             };
43131             //if(this.hideOnLayout){
43132                 //center.el.setStyle("display", "block");
43133             //}
43134             center.updateBox(this.safeBox(centerBox));
43135         }
43136         this.el.repaint();
43137         this.fireEvent("layout", this);
43138     },
43139
43140     // private
43141     safeBox : function(box){
43142         box.width = Math.max(0, box.width);
43143         box.height = Math.max(0, box.height);
43144         return box;
43145     },
43146
43147     /**
43148      * Adds a ContentPanel (or subclass) to this layout.
43149      * @param {String} target The target region key (north, south, east, west or center).
43150      * @param {Roo.ContentPanel} panel The panel to add
43151      * @return {Roo.ContentPanel} The added panel
43152      */
43153     add : function(target, panel){
43154          
43155         target = target.toLowerCase();
43156         return this.regions[target].add(panel);
43157     },
43158
43159     /**
43160      * Remove a ContentPanel (or subclass) to this layout.
43161      * @param {String} target The target region key (north, south, east, west or center).
43162      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43163      * @return {Roo.ContentPanel} The removed panel
43164      */
43165     remove : function(target, panel){
43166         target = target.toLowerCase();
43167         return this.regions[target].remove(panel);
43168     },
43169
43170     /**
43171      * Searches all regions for a panel with the specified id
43172      * @param {String} panelId
43173      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43174      */
43175     findPanel : function(panelId){
43176         var rs = this.regions;
43177         for(var target in rs){
43178             if(typeof rs[target] != "function"){
43179                 var p = rs[target].getPanel(panelId);
43180                 if(p){
43181                     return p;
43182                 }
43183             }
43184         }
43185         return null;
43186     },
43187
43188     /**
43189      * Searches all regions for a panel with the specified id and activates (shows) it.
43190      * @param {String/ContentPanel} panelId The panels id or the panel itself
43191      * @return {Roo.ContentPanel} The shown panel or null
43192      */
43193     showPanel : function(panelId) {
43194       var rs = this.regions;
43195       for(var target in rs){
43196          var r = rs[target];
43197          if(typeof r != "function"){
43198             if(r.hasPanel(panelId)){
43199                return r.showPanel(panelId);
43200             }
43201          }
43202       }
43203       return null;
43204    },
43205
43206    /**
43207      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43208      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43209      */
43210     restoreState : function(provider){
43211         if(!provider){
43212             provider = Roo.state.Manager;
43213         }
43214         var sm = new Roo.LayoutStateManager();
43215         sm.init(this, provider);
43216     },
43217
43218     /**
43219      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43220      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43221      * a valid ContentPanel config object.  Example:
43222      * <pre><code>
43223 // Create the main layout
43224 var layout = new Roo.BorderLayout('main-ct', {
43225     west: {
43226         split:true,
43227         minSize: 175,
43228         titlebar: true
43229     },
43230     center: {
43231         title:'Components'
43232     }
43233 }, 'main-ct');
43234
43235 // Create and add multiple ContentPanels at once via configs
43236 layout.batchAdd({
43237    west: {
43238        id: 'source-files',
43239        autoCreate:true,
43240        title:'Ext Source Files',
43241        autoScroll:true,
43242        fitToFrame:true
43243    },
43244    center : {
43245        el: cview,
43246        autoScroll:true,
43247        fitToFrame:true,
43248        toolbar: tb,
43249        resizeEl:'cbody'
43250    }
43251 });
43252 </code></pre>
43253      * @param {Object} regions An object containing ContentPanel configs by region name
43254      */
43255     batchAdd : function(regions){
43256         this.beginUpdate();
43257         for(var rname in regions){
43258             var lr = this.regions[rname];
43259             if(lr){
43260                 this.addTypedPanels(lr, regions[rname]);
43261             }
43262         }
43263         this.endUpdate();
43264     },
43265
43266     // private
43267     addTypedPanels : function(lr, ps){
43268         if(typeof ps == 'string'){
43269             lr.add(new Roo.ContentPanel(ps));
43270         }
43271         else if(ps instanceof Array){
43272             for(var i =0, len = ps.length; i < len; i++){
43273                 this.addTypedPanels(lr, ps[i]);
43274             }
43275         }
43276         else if(!ps.events){ // raw config?
43277             var el = ps.el;
43278             delete ps.el; // prevent conflict
43279             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43280         }
43281         else {  // panel object assumed!
43282             lr.add(ps);
43283         }
43284     },
43285     /**
43286      * Adds a xtype elements to the layout.
43287      * <pre><code>
43288
43289 layout.addxtype({
43290        xtype : 'ContentPanel',
43291        region: 'west',
43292        items: [ .... ]
43293    }
43294 );
43295
43296 layout.addxtype({
43297         xtype : 'NestedLayoutPanel',
43298         region: 'west',
43299         layout: {
43300            center: { },
43301            west: { }   
43302         },
43303         items : [ ... list of content panels or nested layout panels.. ]
43304    }
43305 );
43306 </code></pre>
43307      * @param {Object} cfg Xtype definition of item to add.
43308      */
43309     addxtype : function(cfg)
43310     {
43311         // basically accepts a pannel...
43312         // can accept a layout region..!?!?
43313        // console.log('BorderLayout add ' + cfg.xtype)
43314         
43315         if (!cfg.xtype.match(/Panel$/)) {
43316             return false;
43317         }
43318         var ret = false;
43319         var region = cfg.region;
43320         delete cfg.region;
43321         
43322           
43323         var xitems = [];
43324         if (cfg.items) {
43325             xitems = cfg.items;
43326             delete cfg.items;
43327         }
43328         
43329         
43330         switch(cfg.xtype) 
43331         {
43332             case 'ContentPanel':  // ContentPanel (el, cfg)
43333             case 'ScrollPanel':  // ContentPanel (el, cfg)
43334                 if(cfg.autoCreate) {
43335                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43336                 } else {
43337                     var el = this.el.createChild();
43338                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43339                 }
43340                 
43341                 this.add(region, ret);
43342                 break;
43343             
43344             
43345             case 'TreePanel': // our new panel!
43346                 cfg.el = this.el.createChild();
43347                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43348                 this.add(region, ret);
43349                 break;
43350             
43351             case 'NestedLayoutPanel': 
43352                 // create a new Layout (which is  a Border Layout...
43353                 var el = this.el.createChild();
43354                 var clayout = cfg.layout;
43355                 delete cfg.layout;
43356                 clayout.items   = clayout.items  || [];
43357                 // replace this exitems with the clayout ones..
43358                 xitems = clayout.items;
43359                  
43360                 
43361                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43362                     cfg.background = false;
43363                 }
43364                 var layout = new Roo.BorderLayout(el, clayout);
43365                 
43366                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43367                 //console.log('adding nested layout panel '  + cfg.toSource());
43368                 this.add(region, ret);
43369                 
43370                 break;
43371                 
43372             case 'GridPanel': 
43373             
43374                 // needs grid and region
43375                 
43376                 //var el = this.getRegion(region).el.createChild();
43377                 var el = this.el.createChild();
43378                 // create the grid first...
43379                 
43380                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43381                 delete cfg.grid;
43382                 if (region == 'center' && this.active ) {
43383                     cfg.background = false;
43384                 }
43385                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43386                 
43387                 this.add(region, ret);
43388                 if (cfg.background) {
43389                     ret.on('activate', function(gp) {
43390                         if (!gp.grid.rendered) {
43391                             gp.grid.render();
43392                         }
43393                     });
43394                 } else {
43395                     grid.render();
43396                 }
43397                 break;
43398            
43399                
43400                 
43401                 
43402             default: 
43403                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43404                 return;
43405              // GridPanel (grid, cfg)
43406             
43407         }
43408         this.beginUpdate();
43409         // add children..
43410         Roo.each(xitems, function(i)  {
43411             ret.addxtype(i);
43412         });
43413         this.endUpdate();
43414         return ret;
43415         
43416     }
43417 });
43418
43419 /**
43420  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43421  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43422  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43423  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43424  * <pre><code>
43425 // shorthand
43426 var CP = Roo.ContentPanel;
43427
43428 var layout = Roo.BorderLayout.create({
43429     north: {
43430         initialSize: 25,
43431         titlebar: false,
43432         panels: [new CP("north", "North")]
43433     },
43434     west: {
43435         split:true,
43436         initialSize: 200,
43437         minSize: 175,
43438         maxSize: 400,
43439         titlebar: true,
43440         collapsible: true,
43441         panels: [new CP("west", {title: "West"})]
43442     },
43443     east: {
43444         split:true,
43445         initialSize: 202,
43446         minSize: 175,
43447         maxSize: 400,
43448         titlebar: true,
43449         collapsible: true,
43450         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43451     },
43452     south: {
43453         split:true,
43454         initialSize: 100,
43455         minSize: 100,
43456         maxSize: 200,
43457         titlebar: true,
43458         collapsible: true,
43459         panels: [new CP("south", {title: "South", closable: true})]
43460     },
43461     center: {
43462         titlebar: true,
43463         autoScroll:true,
43464         resizeTabs: true,
43465         minTabWidth: 50,
43466         preferredTabWidth: 150,
43467         panels: [
43468             new CP("center1", {title: "Close Me", closable: true}),
43469             new CP("center2", {title: "Center Panel", closable: false})
43470         ]
43471     }
43472 }, document.body);
43473
43474 layout.getRegion("center").showPanel("center1");
43475 </code></pre>
43476  * @param config
43477  * @param targetEl
43478  */
43479 Roo.BorderLayout.create = function(config, targetEl){
43480     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43481     layout.beginUpdate();
43482     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43483     for(var j = 0, jlen = regions.length; j < jlen; j++){
43484         var lr = regions[j];
43485         if(layout.regions[lr] && config[lr].panels){
43486             var r = layout.regions[lr];
43487             var ps = config[lr].panels;
43488             layout.addTypedPanels(r, ps);
43489         }
43490     }
43491     layout.endUpdate();
43492     return layout;
43493 };
43494
43495 // private
43496 Roo.BorderLayout.RegionFactory = {
43497     // private
43498     validRegions : ["north","south","east","west","center"],
43499
43500     // private
43501     create : function(target, mgr, config){
43502         target = target.toLowerCase();
43503         if(config.lightweight || config.basic){
43504             return new Roo.BasicLayoutRegion(mgr, config, target);
43505         }
43506         switch(target){
43507             case "north":
43508                 return new Roo.NorthLayoutRegion(mgr, config);
43509             case "south":
43510                 return new Roo.SouthLayoutRegion(mgr, config);
43511             case "east":
43512                 return new Roo.EastLayoutRegion(mgr, config);
43513             case "west":
43514                 return new Roo.WestLayoutRegion(mgr, config);
43515             case "center":
43516                 return new Roo.CenterLayoutRegion(mgr, config);
43517         }
43518         throw 'Layout region "'+target+'" not supported.';
43519     }
43520 };/*
43521  * Based on:
43522  * Ext JS Library 1.1.1
43523  * Copyright(c) 2006-2007, Ext JS, LLC.
43524  *
43525  * Originally Released Under LGPL - original licence link has changed is not relivant.
43526  *
43527  * Fork - LGPL
43528  * <script type="text/javascript">
43529  */
43530  
43531 /**
43532  * @class Roo.BasicLayoutRegion
43533  * @extends Roo.util.Observable
43534  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43535  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43536  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43537  */
43538 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43539     this.mgr = mgr;
43540     this.position  = pos;
43541     this.events = {
43542         /**
43543          * @scope Roo.BasicLayoutRegion
43544          */
43545         
43546         /**
43547          * @event beforeremove
43548          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43549          * @param {Roo.LayoutRegion} this
43550          * @param {Roo.ContentPanel} panel The panel
43551          * @param {Object} e The cancel event object
43552          */
43553         "beforeremove" : true,
43554         /**
43555          * @event invalidated
43556          * Fires when the layout for this region is changed.
43557          * @param {Roo.LayoutRegion} this
43558          */
43559         "invalidated" : true,
43560         /**
43561          * @event visibilitychange
43562          * Fires when this region is shown or hidden 
43563          * @param {Roo.LayoutRegion} this
43564          * @param {Boolean} visibility true or false
43565          */
43566         "visibilitychange" : true,
43567         /**
43568          * @event paneladded
43569          * Fires when a panel is added. 
43570          * @param {Roo.LayoutRegion} this
43571          * @param {Roo.ContentPanel} panel The panel
43572          */
43573         "paneladded" : true,
43574         /**
43575          * @event panelremoved
43576          * Fires when a panel is removed. 
43577          * @param {Roo.LayoutRegion} this
43578          * @param {Roo.ContentPanel} panel The panel
43579          */
43580         "panelremoved" : true,
43581         /**
43582          * @event collapsed
43583          * Fires when this region is collapsed.
43584          * @param {Roo.LayoutRegion} this
43585          */
43586         "collapsed" : true,
43587         /**
43588          * @event expanded
43589          * Fires when this region is expanded.
43590          * @param {Roo.LayoutRegion} this
43591          */
43592         "expanded" : true,
43593         /**
43594          * @event slideshow
43595          * Fires when this region is slid into view.
43596          * @param {Roo.LayoutRegion} this
43597          */
43598         "slideshow" : true,
43599         /**
43600          * @event slidehide
43601          * Fires when this region slides out of view. 
43602          * @param {Roo.LayoutRegion} this
43603          */
43604         "slidehide" : true,
43605         /**
43606          * @event panelactivated
43607          * Fires when a panel is activated. 
43608          * @param {Roo.LayoutRegion} this
43609          * @param {Roo.ContentPanel} panel The activated panel
43610          */
43611         "panelactivated" : true,
43612         /**
43613          * @event resized
43614          * Fires when the user resizes this region. 
43615          * @param {Roo.LayoutRegion} this
43616          * @param {Number} newSize The new size (width for east/west, height for north/south)
43617          */
43618         "resized" : true
43619     };
43620     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43621     this.panels = new Roo.util.MixedCollection();
43622     this.panels.getKey = this.getPanelId.createDelegate(this);
43623     this.box = null;
43624     this.activePanel = null;
43625     // ensure listeners are added...
43626     
43627     if (config.listeners || config.events) {
43628         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43629             listeners : config.listeners || {},
43630             events : config.events || {}
43631         });
43632     }
43633     
43634     if(skipConfig !== true){
43635         this.applyConfig(config);
43636     }
43637 };
43638
43639 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43640     getPanelId : function(p){
43641         return p.getId();
43642     },
43643     
43644     applyConfig : function(config){
43645         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43646         this.config = config;
43647         
43648     },
43649     
43650     /**
43651      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43652      * the width, for horizontal (north, south) the height.
43653      * @param {Number} newSize The new width or height
43654      */
43655     resizeTo : function(newSize){
43656         var el = this.el ? this.el :
43657                  (this.activePanel ? this.activePanel.getEl() : null);
43658         if(el){
43659             switch(this.position){
43660                 case "east":
43661                 case "west":
43662                     el.setWidth(newSize);
43663                     this.fireEvent("resized", this, newSize);
43664                 break;
43665                 case "north":
43666                 case "south":
43667                     el.setHeight(newSize);
43668                     this.fireEvent("resized", this, newSize);
43669                 break;                
43670             }
43671         }
43672     },
43673     
43674     getBox : function(){
43675         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43676     },
43677     
43678     getMargins : function(){
43679         return this.margins;
43680     },
43681     
43682     updateBox : function(box){
43683         this.box = box;
43684         var el = this.activePanel.getEl();
43685         el.dom.style.left = box.x + "px";
43686         el.dom.style.top = box.y + "px";
43687         this.activePanel.setSize(box.width, box.height);
43688     },
43689     
43690     /**
43691      * Returns the container element for this region.
43692      * @return {Roo.Element}
43693      */
43694     getEl : function(){
43695         return this.activePanel;
43696     },
43697     
43698     /**
43699      * Returns true if this region is currently visible.
43700      * @return {Boolean}
43701      */
43702     isVisible : function(){
43703         return this.activePanel ? true : false;
43704     },
43705     
43706     setActivePanel : function(panel){
43707         panel = this.getPanel(panel);
43708         if(this.activePanel && this.activePanel != panel){
43709             this.activePanel.setActiveState(false);
43710             this.activePanel.getEl().setLeftTop(-10000,-10000);
43711         }
43712         this.activePanel = panel;
43713         panel.setActiveState(true);
43714         if(this.box){
43715             panel.setSize(this.box.width, this.box.height);
43716         }
43717         this.fireEvent("panelactivated", this, panel);
43718         this.fireEvent("invalidated");
43719     },
43720     
43721     /**
43722      * Show the specified panel.
43723      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43724      * @return {Roo.ContentPanel} The shown panel or null
43725      */
43726     showPanel : function(panel){
43727         if(panel = this.getPanel(panel)){
43728             this.setActivePanel(panel);
43729         }
43730         return panel;
43731     },
43732     
43733     /**
43734      * Get the active panel for this region.
43735      * @return {Roo.ContentPanel} The active panel or null
43736      */
43737     getActivePanel : function(){
43738         return this.activePanel;
43739     },
43740     
43741     /**
43742      * Add the passed ContentPanel(s)
43743      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43744      * @return {Roo.ContentPanel} The panel added (if only one was added)
43745      */
43746     add : function(panel){
43747         if(arguments.length > 1){
43748             for(var i = 0, len = arguments.length; i < len; i++) {
43749                 this.add(arguments[i]);
43750             }
43751             return null;
43752         }
43753         if(this.hasPanel(panel)){
43754             this.showPanel(panel);
43755             return panel;
43756         }
43757         var el = panel.getEl();
43758         if(el.dom.parentNode != this.mgr.el.dom){
43759             this.mgr.el.dom.appendChild(el.dom);
43760         }
43761         if(panel.setRegion){
43762             panel.setRegion(this);
43763         }
43764         this.panels.add(panel);
43765         el.setStyle("position", "absolute");
43766         if(!panel.background){
43767             this.setActivePanel(panel);
43768             if(this.config.initialSize && this.panels.getCount()==1){
43769                 this.resizeTo(this.config.initialSize);
43770             }
43771         }
43772         this.fireEvent("paneladded", this, panel);
43773         return panel;
43774     },
43775     
43776     /**
43777      * Returns true if the panel is in this region.
43778      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43779      * @return {Boolean}
43780      */
43781     hasPanel : function(panel){
43782         if(typeof panel == "object"){ // must be panel obj
43783             panel = panel.getId();
43784         }
43785         return this.getPanel(panel) ? true : false;
43786     },
43787     
43788     /**
43789      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43790      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43791      * @param {Boolean} preservePanel Overrides the config preservePanel option
43792      * @return {Roo.ContentPanel} The panel that was removed
43793      */
43794     remove : function(panel, preservePanel){
43795         panel = this.getPanel(panel);
43796         if(!panel){
43797             return null;
43798         }
43799         var e = {};
43800         this.fireEvent("beforeremove", this, panel, e);
43801         if(e.cancel === true){
43802             return null;
43803         }
43804         var panelId = panel.getId();
43805         this.panels.removeKey(panelId);
43806         return panel;
43807     },
43808     
43809     /**
43810      * Returns the panel specified or null if it's not in this region.
43811      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43812      * @return {Roo.ContentPanel}
43813      */
43814     getPanel : function(id){
43815         if(typeof id == "object"){ // must be panel obj
43816             return id;
43817         }
43818         return this.panels.get(id);
43819     },
43820     
43821     /**
43822      * Returns this regions position (north/south/east/west/center).
43823      * @return {String} 
43824      */
43825     getPosition: function(){
43826         return this.position;    
43827     }
43828 });/*
43829  * Based on:
43830  * Ext JS Library 1.1.1
43831  * Copyright(c) 2006-2007, Ext JS, LLC.
43832  *
43833  * Originally Released Under LGPL - original licence link has changed is not relivant.
43834  *
43835  * Fork - LGPL
43836  * <script type="text/javascript">
43837  */
43838  
43839 /**
43840  * @class Roo.LayoutRegion
43841  * @extends Roo.BasicLayoutRegion
43842  * This class represents a region in a layout manager.
43843  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43844  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43845  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43846  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43847  * @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})
43848  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43849  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43850  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43851  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43852  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43853  * @cfg {String} title The title for the region (overrides panel titles)
43854  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43855  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43856  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43857  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43858  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43859  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43860  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43861  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43862  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43863  * @cfg {Boolean} showPin True to show a pin button
43864 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43865 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43866 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43867 * @cfg {Number} width  For East/West panels
43868 * @cfg {Number} height For North/South panels
43869 * @cfg {Boolean} split To show the splitter
43870  */
43871 Roo.LayoutRegion = function(mgr, config, pos){
43872     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43873     var dh = Roo.DomHelper;
43874     /** This region's container element 
43875     * @type Roo.Element */
43876     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43877     /** This region's title element 
43878     * @type Roo.Element */
43879
43880     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43881         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43882         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43883     ]}, true);
43884     this.titleEl.enableDisplayMode();
43885     /** This region's title text element 
43886     * @type HTMLElement */
43887     this.titleTextEl = this.titleEl.dom.firstChild;
43888     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43889     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43890     this.closeBtn.enableDisplayMode();
43891     this.closeBtn.on("click", this.closeClicked, this);
43892     this.closeBtn.hide();
43893
43894     this.createBody(config);
43895     this.visible = true;
43896     this.collapsed = false;
43897
43898     if(config.hideWhenEmpty){
43899         this.hide();
43900         this.on("paneladded", this.validateVisibility, this);
43901         this.on("panelremoved", this.validateVisibility, this);
43902     }
43903     this.applyConfig(config);
43904 };
43905
43906 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43907
43908     createBody : function(){
43909         /** This region's body element 
43910         * @type Roo.Element */
43911         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43912     },
43913
43914     applyConfig : function(c){
43915         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43916             var dh = Roo.DomHelper;
43917             if(c.titlebar !== false){
43918                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43919                 this.collapseBtn.on("click", this.collapse, this);
43920                 this.collapseBtn.enableDisplayMode();
43921
43922                 if(c.showPin === true || this.showPin){
43923                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43924                     this.stickBtn.enableDisplayMode();
43925                     this.stickBtn.on("click", this.expand, this);
43926                     this.stickBtn.hide();
43927                 }
43928             }
43929             /** This region's collapsed element
43930             * @type Roo.Element */
43931             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43932                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43933             ]}, true);
43934             if(c.floatable !== false){
43935                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43936                this.collapsedEl.on("click", this.collapseClick, this);
43937             }
43938
43939             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43940                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43941                    id: "message", unselectable: "on", style:{"float":"left"}});
43942                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43943              }
43944             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43945             this.expandBtn.on("click", this.expand, this);
43946         }
43947         if(this.collapseBtn){
43948             this.collapseBtn.setVisible(c.collapsible == true);
43949         }
43950         this.cmargins = c.cmargins || this.cmargins ||
43951                          (this.position == "west" || this.position == "east" ?
43952                              {top: 0, left: 2, right:2, bottom: 0} :
43953                              {top: 2, left: 0, right:0, bottom: 2});
43954         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43955         this.bottomTabs = c.tabPosition != "top";
43956         this.autoScroll = c.autoScroll || false;
43957         if(this.autoScroll){
43958             this.bodyEl.setStyle("overflow", "auto");
43959         }else{
43960             this.bodyEl.setStyle("overflow", "hidden");
43961         }
43962         //if(c.titlebar !== false){
43963             if((!c.titlebar && !c.title) || c.titlebar === false){
43964                 this.titleEl.hide();
43965             }else{
43966                 this.titleEl.show();
43967                 if(c.title){
43968                     this.titleTextEl.innerHTML = c.title;
43969                 }
43970             }
43971         //}
43972         this.duration = c.duration || .30;
43973         this.slideDuration = c.slideDuration || .45;
43974         this.config = c;
43975         if(c.collapsed){
43976             this.collapse(true);
43977         }
43978         if(c.hidden){
43979             this.hide();
43980         }
43981     },
43982     /**
43983      * Returns true if this region is currently visible.
43984      * @return {Boolean}
43985      */
43986     isVisible : function(){
43987         return this.visible;
43988     },
43989
43990     /**
43991      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43992      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43993      */
43994     setCollapsedTitle : function(title){
43995         title = title || "&#160;";
43996         if(this.collapsedTitleTextEl){
43997             this.collapsedTitleTextEl.innerHTML = title;
43998         }
43999     },
44000
44001     getBox : function(){
44002         var b;
44003         if(!this.collapsed){
44004             b = this.el.getBox(false, true);
44005         }else{
44006             b = this.collapsedEl.getBox(false, true);
44007         }
44008         return b;
44009     },
44010
44011     getMargins : function(){
44012         return this.collapsed ? this.cmargins : this.margins;
44013     },
44014
44015     highlight : function(){
44016         this.el.addClass("x-layout-panel-dragover");
44017     },
44018
44019     unhighlight : function(){
44020         this.el.removeClass("x-layout-panel-dragover");
44021     },
44022
44023     updateBox : function(box){
44024         this.box = box;
44025         if(!this.collapsed){
44026             this.el.dom.style.left = box.x + "px";
44027             this.el.dom.style.top = box.y + "px";
44028             this.updateBody(box.width, box.height);
44029         }else{
44030             this.collapsedEl.dom.style.left = box.x + "px";
44031             this.collapsedEl.dom.style.top = box.y + "px";
44032             this.collapsedEl.setSize(box.width, box.height);
44033         }
44034         if(this.tabs){
44035             this.tabs.autoSizeTabs();
44036         }
44037     },
44038
44039     updateBody : function(w, h){
44040         if(w !== null){
44041             this.el.setWidth(w);
44042             w -= this.el.getBorderWidth("rl");
44043             if(this.config.adjustments){
44044                 w += this.config.adjustments[0];
44045             }
44046         }
44047         if(h !== null){
44048             this.el.setHeight(h);
44049             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44050             h -= this.el.getBorderWidth("tb");
44051             if(this.config.adjustments){
44052                 h += this.config.adjustments[1];
44053             }
44054             this.bodyEl.setHeight(h);
44055             if(this.tabs){
44056                 h = this.tabs.syncHeight(h);
44057             }
44058         }
44059         if(this.panelSize){
44060             w = w !== null ? w : this.panelSize.width;
44061             h = h !== null ? h : this.panelSize.height;
44062         }
44063         if(this.activePanel){
44064             var el = this.activePanel.getEl();
44065             w = w !== null ? w : el.getWidth();
44066             h = h !== null ? h : el.getHeight();
44067             this.panelSize = {width: w, height: h};
44068             this.activePanel.setSize(w, h);
44069         }
44070         if(Roo.isIE && this.tabs){
44071             this.tabs.el.repaint();
44072         }
44073     },
44074
44075     /**
44076      * Returns the container element for this region.
44077      * @return {Roo.Element}
44078      */
44079     getEl : function(){
44080         return this.el;
44081     },
44082
44083     /**
44084      * Hides this region.
44085      */
44086     hide : function(){
44087         if(!this.collapsed){
44088             this.el.dom.style.left = "-2000px";
44089             this.el.hide();
44090         }else{
44091             this.collapsedEl.dom.style.left = "-2000px";
44092             this.collapsedEl.hide();
44093         }
44094         this.visible = false;
44095         this.fireEvent("visibilitychange", this, false);
44096     },
44097
44098     /**
44099      * Shows this region if it was previously hidden.
44100      */
44101     show : function(){
44102         if(!this.collapsed){
44103             this.el.show();
44104         }else{
44105             this.collapsedEl.show();
44106         }
44107         this.visible = true;
44108         this.fireEvent("visibilitychange", this, true);
44109     },
44110
44111     closeClicked : function(){
44112         if(this.activePanel){
44113             this.remove(this.activePanel);
44114         }
44115     },
44116
44117     collapseClick : function(e){
44118         if(this.isSlid){
44119            e.stopPropagation();
44120            this.slideIn();
44121         }else{
44122            e.stopPropagation();
44123            this.slideOut();
44124         }
44125     },
44126
44127     /**
44128      * Collapses this region.
44129      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44130      */
44131     collapse : function(skipAnim){
44132         if(this.collapsed) return;
44133         this.collapsed = true;
44134         if(this.split){
44135             this.split.el.hide();
44136         }
44137         if(this.config.animate && skipAnim !== true){
44138             this.fireEvent("invalidated", this);
44139             this.animateCollapse();
44140         }else{
44141             this.el.setLocation(-20000,-20000);
44142             this.el.hide();
44143             this.collapsedEl.show();
44144             this.fireEvent("collapsed", this);
44145             this.fireEvent("invalidated", this);
44146         }
44147     },
44148
44149     animateCollapse : function(){
44150         // overridden
44151     },
44152
44153     /**
44154      * Expands this region if it was previously collapsed.
44155      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44156      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44157      */
44158     expand : function(e, skipAnim){
44159         if(e) e.stopPropagation();
44160         if(!this.collapsed || this.el.hasActiveFx()) return;
44161         if(this.isSlid){
44162             this.afterSlideIn();
44163             skipAnim = true;
44164         }
44165         this.collapsed = false;
44166         if(this.config.animate && skipAnim !== true){
44167             this.animateExpand();
44168         }else{
44169             this.el.show();
44170             if(this.split){
44171                 this.split.el.show();
44172             }
44173             this.collapsedEl.setLocation(-2000,-2000);
44174             this.collapsedEl.hide();
44175             this.fireEvent("invalidated", this);
44176             this.fireEvent("expanded", this);
44177         }
44178     },
44179
44180     animateExpand : function(){
44181         // overridden
44182     },
44183
44184     initTabs : function(){
44185         this.bodyEl.setStyle("overflow", "hidden");
44186         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44187             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44188             disableTooltips: this.config.disableTabTips
44189         });
44190         if(this.config.hideTabs){
44191             ts.stripWrap.setDisplayed(false);
44192         }
44193         this.tabs = ts;
44194         ts.resizeTabs = this.config.resizeTabs === true;
44195         ts.minTabWidth = this.config.minTabWidth || 40;
44196         ts.maxTabWidth = this.config.maxTabWidth || 250;
44197         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44198         ts.monitorResize = false;
44199         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44200         ts.bodyEl.addClass('x-layout-tabs-body');
44201         this.panels.each(this.initPanelAsTab, this);
44202     },
44203
44204     initPanelAsTab : function(panel){
44205         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44206                     this.config.closeOnTab && panel.isClosable());
44207         if(panel.tabTip !== undefined){
44208             ti.setTooltip(panel.tabTip);
44209         }
44210         ti.on("activate", function(){
44211               this.setActivePanel(panel);
44212         }, this);
44213         if(this.config.closeOnTab){
44214             ti.on("beforeclose", function(t, e){
44215                 e.cancel = true;
44216                 this.remove(panel);
44217             }, this);
44218         }
44219         return ti;
44220     },
44221
44222     updatePanelTitle : function(panel, title){
44223         if(this.activePanel == panel){
44224             this.updateTitle(title);
44225         }
44226         if(this.tabs){
44227             var ti = this.tabs.getTab(panel.getEl().id);
44228             ti.setText(title);
44229             if(panel.tabTip !== undefined){
44230                 ti.setTooltip(panel.tabTip);
44231             }
44232         }
44233     },
44234
44235     updateTitle : function(title){
44236         if(this.titleTextEl && !this.config.title){
44237             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44238         }
44239     },
44240
44241     setActivePanel : function(panel){
44242         panel = this.getPanel(panel);
44243         if(this.activePanel && this.activePanel != panel){
44244             this.activePanel.setActiveState(false);
44245         }
44246         this.activePanel = panel;
44247         panel.setActiveState(true);
44248         if(this.panelSize){
44249             panel.setSize(this.panelSize.width, this.panelSize.height);
44250         }
44251         if(this.closeBtn){
44252             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44253         }
44254         this.updateTitle(panel.getTitle());
44255         if(this.tabs){
44256             this.fireEvent("invalidated", this);
44257         }
44258         this.fireEvent("panelactivated", this, panel);
44259     },
44260
44261     /**
44262      * Shows the specified panel.
44263      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44264      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44265      */
44266     showPanel : function(panel){
44267         if(panel = this.getPanel(panel)){
44268             if(this.tabs){
44269                 var tab = this.tabs.getTab(panel.getEl().id);
44270                 if(tab.isHidden()){
44271                     this.tabs.unhideTab(tab.id);
44272                 }
44273                 tab.activate();
44274             }else{
44275                 this.setActivePanel(panel);
44276             }
44277         }
44278         return panel;
44279     },
44280
44281     /**
44282      * Get the active panel for this region.
44283      * @return {Roo.ContentPanel} The active panel or null
44284      */
44285     getActivePanel : function(){
44286         return this.activePanel;
44287     },
44288
44289     validateVisibility : function(){
44290         if(this.panels.getCount() < 1){
44291             this.updateTitle("&#160;");
44292             this.closeBtn.hide();
44293             this.hide();
44294         }else{
44295             if(!this.isVisible()){
44296                 this.show();
44297             }
44298         }
44299     },
44300
44301     /**
44302      * Adds the passed ContentPanel(s) to this region.
44303      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44304      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44305      */
44306     add : function(panel){
44307         if(arguments.length > 1){
44308             for(var i = 0, len = arguments.length; i < len; i++) {
44309                 this.add(arguments[i]);
44310             }
44311             return null;
44312         }
44313         if(this.hasPanel(panel)){
44314             this.showPanel(panel);
44315             return panel;
44316         }
44317         panel.setRegion(this);
44318         this.panels.add(panel);
44319         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44320             this.bodyEl.dom.appendChild(panel.getEl().dom);
44321             if(panel.background !== true){
44322                 this.setActivePanel(panel);
44323             }
44324             this.fireEvent("paneladded", this, panel);
44325             return panel;
44326         }
44327         if(!this.tabs){
44328             this.initTabs();
44329         }else{
44330             this.initPanelAsTab(panel);
44331         }
44332         if(panel.background !== true){
44333             this.tabs.activate(panel.getEl().id);
44334         }
44335         this.fireEvent("paneladded", this, panel);
44336         return panel;
44337     },
44338
44339     /**
44340      * Hides the tab for the specified panel.
44341      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44342      */
44343     hidePanel : function(panel){
44344         if(this.tabs && (panel = this.getPanel(panel))){
44345             this.tabs.hideTab(panel.getEl().id);
44346         }
44347     },
44348
44349     /**
44350      * Unhides the tab for a previously hidden panel.
44351      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44352      */
44353     unhidePanel : function(panel){
44354         if(this.tabs && (panel = this.getPanel(panel))){
44355             this.tabs.unhideTab(panel.getEl().id);
44356         }
44357     },
44358
44359     clearPanels : function(){
44360         while(this.panels.getCount() > 0){
44361              this.remove(this.panels.first());
44362         }
44363     },
44364
44365     /**
44366      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44367      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44368      * @param {Boolean} preservePanel Overrides the config preservePanel option
44369      * @return {Roo.ContentPanel} The panel that was removed
44370      */
44371     remove : function(panel, preservePanel){
44372         panel = this.getPanel(panel);
44373         if(!panel){
44374             return null;
44375         }
44376         var e = {};
44377         this.fireEvent("beforeremove", this, panel, e);
44378         if(e.cancel === true){
44379             return null;
44380         }
44381         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44382         var panelId = panel.getId();
44383         this.panels.removeKey(panelId);
44384         if(preservePanel){
44385             document.body.appendChild(panel.getEl().dom);
44386         }
44387         if(this.tabs){
44388             this.tabs.removeTab(panel.getEl().id);
44389         }else if (!preservePanel){
44390             this.bodyEl.dom.removeChild(panel.getEl().dom);
44391         }
44392         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44393             var p = this.panels.first();
44394             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44395             tempEl.appendChild(p.getEl().dom);
44396             this.bodyEl.update("");
44397             this.bodyEl.dom.appendChild(p.getEl().dom);
44398             tempEl = null;
44399             this.updateTitle(p.getTitle());
44400             this.tabs = null;
44401             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44402             this.setActivePanel(p);
44403         }
44404         panel.setRegion(null);
44405         if(this.activePanel == panel){
44406             this.activePanel = null;
44407         }
44408         if(this.config.autoDestroy !== false && preservePanel !== true){
44409             try{panel.destroy();}catch(e){}
44410         }
44411         this.fireEvent("panelremoved", this, panel);
44412         return panel;
44413     },
44414
44415     /**
44416      * Returns the TabPanel component used by this region
44417      * @return {Roo.TabPanel}
44418      */
44419     getTabs : function(){
44420         return this.tabs;
44421     },
44422
44423     createTool : function(parentEl, className){
44424         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44425             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44426         btn.addClassOnOver("x-layout-tools-button-over");
44427         return btn;
44428     }
44429 });/*
44430  * Based on:
44431  * Ext JS Library 1.1.1
44432  * Copyright(c) 2006-2007, Ext JS, LLC.
44433  *
44434  * Originally Released Under LGPL - original licence link has changed is not relivant.
44435  *
44436  * Fork - LGPL
44437  * <script type="text/javascript">
44438  */
44439  
44440
44441
44442 /**
44443  * @class Roo.SplitLayoutRegion
44444  * @extends Roo.LayoutRegion
44445  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44446  */
44447 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44448     this.cursor = cursor;
44449     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44450 };
44451
44452 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44453     splitTip : "Drag to resize.",
44454     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44455     useSplitTips : false,
44456
44457     applyConfig : function(config){
44458         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44459         if(config.split){
44460             if(!this.split){
44461                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44462                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44463                 /** The SplitBar for this region 
44464                 * @type Roo.SplitBar */
44465                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44466                 this.split.on("moved", this.onSplitMove, this);
44467                 this.split.useShim = config.useShim === true;
44468                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44469                 if(this.useSplitTips){
44470                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44471                 }
44472                 if(config.collapsible){
44473                     this.split.el.on("dblclick", this.collapse,  this);
44474                 }
44475             }
44476             if(typeof config.minSize != "undefined"){
44477                 this.split.minSize = config.minSize;
44478             }
44479             if(typeof config.maxSize != "undefined"){
44480                 this.split.maxSize = config.maxSize;
44481             }
44482             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44483                 this.hideSplitter();
44484             }
44485         }
44486     },
44487
44488     getHMaxSize : function(){
44489          var cmax = this.config.maxSize || 10000;
44490          var center = this.mgr.getRegion("center");
44491          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44492     },
44493
44494     getVMaxSize : function(){
44495          var cmax = this.config.maxSize || 10000;
44496          var center = this.mgr.getRegion("center");
44497          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44498     },
44499
44500     onSplitMove : function(split, newSize){
44501         this.fireEvent("resized", this, newSize);
44502     },
44503     
44504     /** 
44505      * Returns the {@link Roo.SplitBar} for this region.
44506      * @return {Roo.SplitBar}
44507      */
44508     getSplitBar : function(){
44509         return this.split;
44510     },
44511     
44512     hide : function(){
44513         this.hideSplitter();
44514         Roo.SplitLayoutRegion.superclass.hide.call(this);
44515     },
44516
44517     hideSplitter : function(){
44518         if(this.split){
44519             this.split.el.setLocation(-2000,-2000);
44520             this.split.el.hide();
44521         }
44522     },
44523
44524     show : function(){
44525         if(this.split){
44526             this.split.el.show();
44527         }
44528         Roo.SplitLayoutRegion.superclass.show.call(this);
44529     },
44530     
44531     beforeSlide: function(){
44532         if(Roo.isGecko){// firefox overflow auto bug workaround
44533             this.bodyEl.clip();
44534             if(this.tabs) this.tabs.bodyEl.clip();
44535             if(this.activePanel){
44536                 this.activePanel.getEl().clip();
44537                 
44538                 if(this.activePanel.beforeSlide){
44539                     this.activePanel.beforeSlide();
44540                 }
44541             }
44542         }
44543     },
44544     
44545     afterSlide : function(){
44546         if(Roo.isGecko){// firefox overflow auto bug workaround
44547             this.bodyEl.unclip();
44548             if(this.tabs) this.tabs.bodyEl.unclip();
44549             if(this.activePanel){
44550                 this.activePanel.getEl().unclip();
44551                 if(this.activePanel.afterSlide){
44552                     this.activePanel.afterSlide();
44553                 }
44554             }
44555         }
44556     },
44557
44558     initAutoHide : function(){
44559         if(this.autoHide !== false){
44560             if(!this.autoHideHd){
44561                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44562                 this.autoHideHd = {
44563                     "mouseout": function(e){
44564                         if(!e.within(this.el, true)){
44565                             st.delay(500);
44566                         }
44567                     },
44568                     "mouseover" : function(e){
44569                         st.cancel();
44570                     },
44571                     scope : this
44572                 };
44573             }
44574             this.el.on(this.autoHideHd);
44575         }
44576     },
44577
44578     clearAutoHide : function(){
44579         if(this.autoHide !== false){
44580             this.el.un("mouseout", this.autoHideHd.mouseout);
44581             this.el.un("mouseover", this.autoHideHd.mouseover);
44582         }
44583     },
44584
44585     clearMonitor : function(){
44586         Roo.get(document).un("click", this.slideInIf, this);
44587     },
44588
44589     // these names are backwards but not changed for compat
44590     slideOut : function(){
44591         if(this.isSlid || this.el.hasActiveFx()){
44592             return;
44593         }
44594         this.isSlid = true;
44595         if(this.collapseBtn){
44596             this.collapseBtn.hide();
44597         }
44598         this.closeBtnState = this.closeBtn.getStyle('display');
44599         this.closeBtn.hide();
44600         if(this.stickBtn){
44601             this.stickBtn.show();
44602         }
44603         this.el.show();
44604         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44605         this.beforeSlide();
44606         this.el.setStyle("z-index", 10001);
44607         this.el.slideIn(this.getSlideAnchor(), {
44608             callback: function(){
44609                 this.afterSlide();
44610                 this.initAutoHide();
44611                 Roo.get(document).on("click", this.slideInIf, this);
44612                 this.fireEvent("slideshow", this);
44613             },
44614             scope: this,
44615             block: true
44616         });
44617     },
44618
44619     afterSlideIn : function(){
44620         this.clearAutoHide();
44621         this.isSlid = false;
44622         this.clearMonitor();
44623         this.el.setStyle("z-index", "");
44624         if(this.collapseBtn){
44625             this.collapseBtn.show();
44626         }
44627         this.closeBtn.setStyle('display', this.closeBtnState);
44628         if(this.stickBtn){
44629             this.stickBtn.hide();
44630         }
44631         this.fireEvent("slidehide", this);
44632     },
44633
44634     slideIn : function(cb){
44635         if(!this.isSlid || this.el.hasActiveFx()){
44636             Roo.callback(cb);
44637             return;
44638         }
44639         this.isSlid = false;
44640         this.beforeSlide();
44641         this.el.slideOut(this.getSlideAnchor(), {
44642             callback: function(){
44643                 this.el.setLeftTop(-10000, -10000);
44644                 this.afterSlide();
44645                 this.afterSlideIn();
44646                 Roo.callback(cb);
44647             },
44648             scope: this,
44649             block: true
44650         });
44651     },
44652     
44653     slideInIf : function(e){
44654         if(!e.within(this.el)){
44655             this.slideIn();
44656         }
44657     },
44658
44659     animateCollapse : function(){
44660         this.beforeSlide();
44661         this.el.setStyle("z-index", 20000);
44662         var anchor = this.getSlideAnchor();
44663         this.el.slideOut(anchor, {
44664             callback : function(){
44665                 this.el.setStyle("z-index", "");
44666                 this.collapsedEl.slideIn(anchor, {duration:.3});
44667                 this.afterSlide();
44668                 this.el.setLocation(-10000,-10000);
44669                 this.el.hide();
44670                 this.fireEvent("collapsed", this);
44671             },
44672             scope: this,
44673             block: true
44674         });
44675     },
44676
44677     animateExpand : function(){
44678         this.beforeSlide();
44679         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44680         this.el.setStyle("z-index", 20000);
44681         this.collapsedEl.hide({
44682             duration:.1
44683         });
44684         this.el.slideIn(this.getSlideAnchor(), {
44685             callback : function(){
44686                 this.el.setStyle("z-index", "");
44687                 this.afterSlide();
44688                 if(this.split){
44689                     this.split.el.show();
44690                 }
44691                 this.fireEvent("invalidated", this);
44692                 this.fireEvent("expanded", this);
44693             },
44694             scope: this,
44695             block: true
44696         });
44697     },
44698
44699     anchors : {
44700         "west" : "left",
44701         "east" : "right",
44702         "north" : "top",
44703         "south" : "bottom"
44704     },
44705
44706     sanchors : {
44707         "west" : "l",
44708         "east" : "r",
44709         "north" : "t",
44710         "south" : "b"
44711     },
44712
44713     canchors : {
44714         "west" : "tl-tr",
44715         "east" : "tr-tl",
44716         "north" : "tl-bl",
44717         "south" : "bl-tl"
44718     },
44719
44720     getAnchor : function(){
44721         return this.anchors[this.position];
44722     },
44723
44724     getCollapseAnchor : function(){
44725         return this.canchors[this.position];
44726     },
44727
44728     getSlideAnchor : function(){
44729         return this.sanchors[this.position];
44730     },
44731
44732     getAlignAdj : function(){
44733         var cm = this.cmargins;
44734         switch(this.position){
44735             case "west":
44736                 return [0, 0];
44737             break;
44738             case "east":
44739                 return [0, 0];
44740             break;
44741             case "north":
44742                 return [0, 0];
44743             break;
44744             case "south":
44745                 return [0, 0];
44746             break;
44747         }
44748     },
44749
44750     getExpandAdj : function(){
44751         var c = this.collapsedEl, cm = this.cmargins;
44752         switch(this.position){
44753             case "west":
44754                 return [-(cm.right+c.getWidth()+cm.left), 0];
44755             break;
44756             case "east":
44757                 return [cm.right+c.getWidth()+cm.left, 0];
44758             break;
44759             case "north":
44760                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44761             break;
44762             case "south":
44763                 return [0, cm.top+cm.bottom+c.getHeight()];
44764             break;
44765         }
44766     }
44767 });/*
44768  * Based on:
44769  * Ext JS Library 1.1.1
44770  * Copyright(c) 2006-2007, Ext JS, LLC.
44771  *
44772  * Originally Released Under LGPL - original licence link has changed is not relivant.
44773  *
44774  * Fork - LGPL
44775  * <script type="text/javascript">
44776  */
44777 /*
44778  * These classes are private internal classes
44779  */
44780 Roo.CenterLayoutRegion = function(mgr, config){
44781     Roo.LayoutRegion.call(this, mgr, config, "center");
44782     this.visible = true;
44783     this.minWidth = config.minWidth || 20;
44784     this.minHeight = config.minHeight || 20;
44785 };
44786
44787 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44788     hide : function(){
44789         // center panel can't be hidden
44790     },
44791     
44792     show : function(){
44793         // center panel can't be hidden
44794     },
44795     
44796     getMinWidth: function(){
44797         return this.minWidth;
44798     },
44799     
44800     getMinHeight: function(){
44801         return this.minHeight;
44802     }
44803 });
44804
44805
44806 Roo.NorthLayoutRegion = function(mgr, config){
44807     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44808     if(this.split){
44809         this.split.placement = Roo.SplitBar.TOP;
44810         this.split.orientation = Roo.SplitBar.VERTICAL;
44811         this.split.el.addClass("x-layout-split-v");
44812     }
44813     var size = config.initialSize || config.height;
44814     if(typeof size != "undefined"){
44815         this.el.setHeight(size);
44816     }
44817 };
44818 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44819     orientation: Roo.SplitBar.VERTICAL,
44820     getBox : function(){
44821         if(this.collapsed){
44822             return this.collapsedEl.getBox();
44823         }
44824         var box = this.el.getBox();
44825         if(this.split){
44826             box.height += this.split.el.getHeight();
44827         }
44828         return box;
44829     },
44830     
44831     updateBox : function(box){
44832         if(this.split && !this.collapsed){
44833             box.height -= this.split.el.getHeight();
44834             this.split.el.setLeft(box.x);
44835             this.split.el.setTop(box.y+box.height);
44836             this.split.el.setWidth(box.width);
44837         }
44838         if(this.collapsed){
44839             this.updateBody(box.width, null);
44840         }
44841         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44842     }
44843 });
44844
44845 Roo.SouthLayoutRegion = function(mgr, config){
44846     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44847     if(this.split){
44848         this.split.placement = Roo.SplitBar.BOTTOM;
44849         this.split.orientation = Roo.SplitBar.VERTICAL;
44850         this.split.el.addClass("x-layout-split-v");
44851     }
44852     var size = config.initialSize || config.height;
44853     if(typeof size != "undefined"){
44854         this.el.setHeight(size);
44855     }
44856 };
44857 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44858     orientation: Roo.SplitBar.VERTICAL,
44859     getBox : function(){
44860         if(this.collapsed){
44861             return this.collapsedEl.getBox();
44862         }
44863         var box = this.el.getBox();
44864         if(this.split){
44865             var sh = this.split.el.getHeight();
44866             box.height += sh;
44867             box.y -= sh;
44868         }
44869         return box;
44870     },
44871     
44872     updateBox : function(box){
44873         if(this.split && !this.collapsed){
44874             var sh = this.split.el.getHeight();
44875             box.height -= sh;
44876             box.y += sh;
44877             this.split.el.setLeft(box.x);
44878             this.split.el.setTop(box.y-sh);
44879             this.split.el.setWidth(box.width);
44880         }
44881         if(this.collapsed){
44882             this.updateBody(box.width, null);
44883         }
44884         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44885     }
44886 });
44887
44888 Roo.EastLayoutRegion = function(mgr, config){
44889     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44890     if(this.split){
44891         this.split.placement = Roo.SplitBar.RIGHT;
44892         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44893         this.split.el.addClass("x-layout-split-h");
44894     }
44895     var size = config.initialSize || config.width;
44896     if(typeof size != "undefined"){
44897         this.el.setWidth(size);
44898     }
44899 };
44900 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44901     orientation: Roo.SplitBar.HORIZONTAL,
44902     getBox : function(){
44903         if(this.collapsed){
44904             return this.collapsedEl.getBox();
44905         }
44906         var box = this.el.getBox();
44907         if(this.split){
44908             var sw = this.split.el.getWidth();
44909             box.width += sw;
44910             box.x -= sw;
44911         }
44912         return box;
44913     },
44914
44915     updateBox : function(box){
44916         if(this.split && !this.collapsed){
44917             var sw = this.split.el.getWidth();
44918             box.width -= sw;
44919             this.split.el.setLeft(box.x);
44920             this.split.el.setTop(box.y);
44921             this.split.el.setHeight(box.height);
44922             box.x += sw;
44923         }
44924         if(this.collapsed){
44925             this.updateBody(null, box.height);
44926         }
44927         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44928     }
44929 });
44930
44931 Roo.WestLayoutRegion = function(mgr, config){
44932     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44933     if(this.split){
44934         this.split.placement = Roo.SplitBar.LEFT;
44935         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44936         this.split.el.addClass("x-layout-split-h");
44937     }
44938     var size = config.initialSize || config.width;
44939     if(typeof size != "undefined"){
44940         this.el.setWidth(size);
44941     }
44942 };
44943 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44944     orientation: Roo.SplitBar.HORIZONTAL,
44945     getBox : function(){
44946         if(this.collapsed){
44947             return this.collapsedEl.getBox();
44948         }
44949         var box = this.el.getBox();
44950         if(this.split){
44951             box.width += this.split.el.getWidth();
44952         }
44953         return box;
44954     },
44955     
44956     updateBox : function(box){
44957         if(this.split && !this.collapsed){
44958             var sw = this.split.el.getWidth();
44959             box.width -= sw;
44960             this.split.el.setLeft(box.x+box.width);
44961             this.split.el.setTop(box.y);
44962             this.split.el.setHeight(box.height);
44963         }
44964         if(this.collapsed){
44965             this.updateBody(null, box.height);
44966         }
44967         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44968     }
44969 });
44970 /*
44971  * Based on:
44972  * Ext JS Library 1.1.1
44973  * Copyright(c) 2006-2007, Ext JS, LLC.
44974  *
44975  * Originally Released Under LGPL - original licence link has changed is not relivant.
44976  *
44977  * Fork - LGPL
44978  * <script type="text/javascript">
44979  */
44980  
44981  
44982 /*
44983  * Private internal class for reading and applying state
44984  */
44985 Roo.LayoutStateManager = function(layout){
44986      // default empty state
44987      this.state = {
44988         north: {},
44989         south: {},
44990         east: {},
44991         west: {}       
44992     };
44993 };
44994
44995 Roo.LayoutStateManager.prototype = {
44996     init : function(layout, provider){
44997         this.provider = provider;
44998         var state = provider.get(layout.id+"-layout-state");
44999         if(state){
45000             var wasUpdating = layout.isUpdating();
45001             if(!wasUpdating){
45002                 layout.beginUpdate();
45003             }
45004             for(var key in state){
45005                 if(typeof state[key] != "function"){
45006                     var rstate = state[key];
45007                     var r = layout.getRegion(key);
45008                     if(r && rstate){
45009                         if(rstate.size){
45010                             r.resizeTo(rstate.size);
45011                         }
45012                         if(rstate.collapsed == true){
45013                             r.collapse(true);
45014                         }else{
45015                             r.expand(null, true);
45016                         }
45017                     }
45018                 }
45019             }
45020             if(!wasUpdating){
45021                 layout.endUpdate();
45022             }
45023             this.state = state; 
45024         }
45025         this.layout = layout;
45026         layout.on("regionresized", this.onRegionResized, this);
45027         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45028         layout.on("regionexpanded", this.onRegionExpanded, this);
45029     },
45030     
45031     storeState : function(){
45032         this.provider.set(this.layout.id+"-layout-state", this.state);
45033     },
45034     
45035     onRegionResized : function(region, newSize){
45036         this.state[region.getPosition()].size = newSize;
45037         this.storeState();
45038     },
45039     
45040     onRegionCollapsed : function(region){
45041         this.state[region.getPosition()].collapsed = true;
45042         this.storeState();
45043     },
45044     
45045     onRegionExpanded : function(region){
45046         this.state[region.getPosition()].collapsed = false;
45047         this.storeState();
45048     }
45049 };/*
45050  * Based on:
45051  * Ext JS Library 1.1.1
45052  * Copyright(c) 2006-2007, Ext JS, LLC.
45053  *
45054  * Originally Released Under LGPL - original licence link has changed is not relivant.
45055  *
45056  * Fork - LGPL
45057  * <script type="text/javascript">
45058  */
45059 /**
45060  * @class Roo.ContentPanel
45061  * @extends Roo.util.Observable
45062  * A basic ContentPanel element.
45063  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45064  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45065  * @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
45066  * @cfg {Boolean} closable True if the panel can be closed/removed
45067  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45068  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45069  * @cfg {Toolbar} toolbar A toolbar for this panel
45070  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45071  * @cfg {String} title The title for this panel
45072  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45073  * @cfg {String} url Calls {@link #setUrl} with this value
45074  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45075  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45076  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45077  * @constructor
45078  * Create a new ContentPanel.
45079  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45080  * @param {String/Object} config A string to set only the title or a config object
45081  * @param {String} content (optional) Set the HTML content for this panel
45082  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45083  */
45084 Roo.ContentPanel = function(el, config, content){
45085     
45086      
45087     /*
45088     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45089         config = el;
45090         el = Roo.id();
45091     }
45092     if (config && config.parentLayout) { 
45093         el = config.parentLayout.el.createChild(); 
45094     }
45095     */
45096     if(el.autoCreate){ // xtype is available if this is called from factory
45097         config = el;
45098         el = Roo.id();
45099     }
45100     this.el = Roo.get(el);
45101     if(!this.el && config && config.autoCreate){
45102         if(typeof config.autoCreate == "object"){
45103             if(!config.autoCreate.id){
45104                 config.autoCreate.id = config.id||el;
45105             }
45106             this.el = Roo.DomHelper.append(document.body,
45107                         config.autoCreate, true);
45108         }else{
45109             this.el = Roo.DomHelper.append(document.body,
45110                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45111         }
45112     }
45113     this.closable = false;
45114     this.loaded = false;
45115     this.active = false;
45116     if(typeof config == "string"){
45117         this.title = config;
45118     }else{
45119         Roo.apply(this, config);
45120     }
45121     
45122     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45123         this.wrapEl = this.el.wrap();    
45124         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45125         
45126     }
45127     
45128     
45129     
45130     if(this.resizeEl){
45131         this.resizeEl = Roo.get(this.resizeEl, true);
45132     }else{
45133         this.resizeEl = this.el;
45134     }
45135     this.addEvents({
45136         /**
45137          * @event activate
45138          * Fires when this panel is activated. 
45139          * @param {Roo.ContentPanel} this
45140          */
45141         "activate" : true,
45142         /**
45143          * @event deactivate
45144          * Fires when this panel is activated. 
45145          * @param {Roo.ContentPanel} this
45146          */
45147         "deactivate" : true,
45148
45149         /**
45150          * @event resize
45151          * Fires when this panel is resized if fitToFrame is true.
45152          * @param {Roo.ContentPanel} this
45153          * @param {Number} width The width after any component adjustments
45154          * @param {Number} height The height after any component adjustments
45155          */
45156         "resize" : true
45157     });
45158     if(this.autoScroll){
45159         this.resizeEl.setStyle("overflow", "auto");
45160     }
45161     content = content || this.content;
45162     if(content){
45163         this.setContent(content);
45164     }
45165     if(config && config.url){
45166         this.setUrl(this.url, this.params, this.loadOnce);
45167     }
45168     
45169     
45170     
45171     Roo.ContentPanel.superclass.constructor.call(this);
45172 };
45173
45174 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45175     tabTip:'',
45176     setRegion : function(region){
45177         this.region = region;
45178         if(region){
45179            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45180         }else{
45181            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45182         } 
45183     },
45184     
45185     /**
45186      * Returns the toolbar for this Panel if one was configured. 
45187      * @return {Roo.Toolbar} 
45188      */
45189     getToolbar : function(){
45190         return this.toolbar;
45191     },
45192     
45193     setActiveState : function(active){
45194         this.active = active;
45195         if(!active){
45196             this.fireEvent("deactivate", this);
45197         }else{
45198             this.fireEvent("activate", this);
45199         }
45200     },
45201     /**
45202      * Updates this panel's element
45203      * @param {String} content The new content
45204      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45205     */
45206     setContent : function(content, loadScripts){
45207         this.el.update(content, loadScripts);
45208     },
45209
45210     ignoreResize : function(w, h){
45211         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45212             return true;
45213         }else{
45214             this.lastSize = {width: w, height: h};
45215             return false;
45216         }
45217     },
45218     /**
45219      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45220      * @return {Roo.UpdateManager} The UpdateManager
45221      */
45222     getUpdateManager : function(){
45223         return this.el.getUpdateManager();
45224     },
45225      /**
45226      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45227      * @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:
45228 <pre><code>
45229 panel.load({
45230     url: "your-url.php",
45231     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45232     callback: yourFunction,
45233     scope: yourObject, //(optional scope)
45234     discardUrl: false,
45235     nocache: false,
45236     text: "Loading...",
45237     timeout: 30,
45238     scripts: false
45239 });
45240 </code></pre>
45241      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45242      * 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.
45243      * @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}
45244      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45245      * @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.
45246      * @return {Roo.ContentPanel} this
45247      */
45248     load : function(){
45249         var um = this.el.getUpdateManager();
45250         um.update.apply(um, arguments);
45251         return this;
45252     },
45253
45254
45255     /**
45256      * 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.
45257      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45258      * @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)
45259      * @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)
45260      * @return {Roo.UpdateManager} The UpdateManager
45261      */
45262     setUrl : function(url, params, loadOnce){
45263         if(this.refreshDelegate){
45264             this.removeListener("activate", this.refreshDelegate);
45265         }
45266         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45267         this.on("activate", this.refreshDelegate);
45268         return this.el.getUpdateManager();
45269     },
45270     
45271     _handleRefresh : function(url, params, loadOnce){
45272         if(!loadOnce || !this.loaded){
45273             var updater = this.el.getUpdateManager();
45274             updater.update(url, params, this._setLoaded.createDelegate(this));
45275         }
45276     },
45277     
45278     _setLoaded : function(){
45279         this.loaded = true;
45280     }, 
45281     
45282     /**
45283      * Returns this panel's id
45284      * @return {String} 
45285      */
45286     getId : function(){
45287         return this.el.id;
45288     },
45289     
45290     /** 
45291      * Returns this panel's element - used by regiosn to add.
45292      * @return {Roo.Element} 
45293      */
45294     getEl : function(){
45295         return this.wrapEl || this.el;
45296     },
45297     
45298     adjustForComponents : function(width, height){
45299         if(this.resizeEl != this.el){
45300             width -= this.el.getFrameWidth('lr');
45301             height -= this.el.getFrameWidth('tb');
45302         }
45303         if(this.toolbar){
45304             var te = this.toolbar.getEl();
45305             height -= te.getHeight();
45306             te.setWidth(width);
45307         }
45308         if(this.adjustments){
45309             width += this.adjustments[0];
45310             height += this.adjustments[1];
45311         }
45312         return {"width": width, "height": height};
45313     },
45314     
45315     setSize : function(width, height){
45316         if(this.fitToFrame && !this.ignoreResize(width, height)){
45317             if(this.fitContainer && this.resizeEl != this.el){
45318                 this.el.setSize(width, height);
45319             }
45320             var size = this.adjustForComponents(width, height);
45321             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45322             this.fireEvent('resize', this, size.width, size.height);
45323         }
45324     },
45325     
45326     /**
45327      * Returns this panel's title
45328      * @return {String} 
45329      */
45330     getTitle : function(){
45331         return this.title;
45332     },
45333     
45334     /**
45335      * Set this panel's title
45336      * @param {String} title
45337      */
45338     setTitle : function(title){
45339         this.title = title;
45340         if(this.region){
45341             this.region.updatePanelTitle(this, title);
45342         }
45343     },
45344     
45345     /**
45346      * Returns true is this panel was configured to be closable
45347      * @return {Boolean} 
45348      */
45349     isClosable : function(){
45350         return this.closable;
45351     },
45352     
45353     beforeSlide : function(){
45354         this.el.clip();
45355         this.resizeEl.clip();
45356     },
45357     
45358     afterSlide : function(){
45359         this.el.unclip();
45360         this.resizeEl.unclip();
45361     },
45362     
45363     /**
45364      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45365      *   Will fail silently if the {@link #setUrl} method has not been called.
45366      *   This does not activate the panel, just updates its content.
45367      */
45368     refresh : function(){
45369         if(this.refreshDelegate){
45370            this.loaded = false;
45371            this.refreshDelegate();
45372         }
45373     },
45374     
45375     /**
45376      * Destroys this panel
45377      */
45378     destroy : function(){
45379         this.el.removeAllListeners();
45380         var tempEl = document.createElement("span");
45381         tempEl.appendChild(this.el.dom);
45382         tempEl.innerHTML = "";
45383         this.el.remove();
45384         this.el = null;
45385     },
45386     
45387       /**
45388      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45389      * <pre><code>
45390
45391 layout.addxtype({
45392        xtype : 'Form',
45393        items: [ .... ]
45394    }
45395 );
45396
45397 </code></pre>
45398      * @param {Object} cfg Xtype definition of item to add.
45399      */
45400     
45401     addxtype : function(cfg) {
45402         // add form..
45403         if (cfg.xtype.match(/^Form$/)) {
45404             var el = this.el.createChild();
45405
45406             this.form = new  Roo.form.Form(cfg);
45407             
45408             
45409             if ( this.form.allItems.length) this.form.render(el.dom);
45410             return this.form;
45411         }
45412         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45413             // views..
45414             cfg.el = this.el.appendChild(document.createElement("div"));
45415             // factory?
45416             var ret = new Roo[cfg.xtype](cfg);
45417             ret.render(false, ''); // render blank..
45418             return ret;
45419             
45420         }
45421         return false;
45422         
45423     }
45424 });
45425
45426 /**
45427  * @class Roo.GridPanel
45428  * @extends Roo.ContentPanel
45429  * @constructor
45430  * Create a new GridPanel.
45431  * @param {Roo.grid.Grid} grid The grid for this panel
45432  * @param {String/Object} config A string to set only the panel's title, or a config object
45433  */
45434 Roo.GridPanel = function(grid, config){
45435     
45436   
45437     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45438         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45439         
45440     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45441     
45442     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45443     
45444     if(this.toolbar){
45445         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45446     }
45447     // xtype created footer. - not sure if will work as we normally have to render first..
45448     if (this.footer && !this.footer.el && this.footer.xtype) {
45449         
45450         this.footer.container = this.grid.getView().getFooterPanel(true);
45451         this.footer.dataSource = this.grid.dataSource;
45452         this.footer = Roo.factory(this.footer, Roo);
45453         
45454     }
45455     
45456     grid.monitorWindowResize = false; // turn off autosizing
45457     grid.autoHeight = false;
45458     grid.autoWidth = false;
45459     this.grid = grid;
45460     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45461 };
45462
45463 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45464     getId : function(){
45465         return this.grid.id;
45466     },
45467     
45468     /**
45469      * Returns the grid for this panel
45470      * @return {Roo.grid.Grid} 
45471      */
45472     getGrid : function(){
45473         return this.grid;    
45474     },
45475     
45476     setSize : function(width, height){
45477         if(!this.ignoreResize(width, height)){
45478             var grid = this.grid;
45479             var size = this.adjustForComponents(width, height);
45480             grid.getGridEl().setSize(size.width, size.height);
45481             grid.autoSize();
45482         }
45483     },
45484     
45485     beforeSlide : function(){
45486         this.grid.getView().scroller.clip();
45487     },
45488     
45489     afterSlide : function(){
45490         this.grid.getView().scroller.unclip();
45491     },
45492     
45493     destroy : function(){
45494         this.grid.destroy();
45495         delete this.grid;
45496         Roo.GridPanel.superclass.destroy.call(this); 
45497     }
45498 });
45499
45500
45501 /**
45502  * @class Roo.NestedLayoutPanel
45503  * @extends Roo.ContentPanel
45504  * @constructor
45505  * Create a new NestedLayoutPanel.
45506  * 
45507  * 
45508  * @param {Roo.BorderLayout} layout The layout for this panel
45509  * @param {String/Object} config A string to set only the title or a config object
45510  */
45511 Roo.NestedLayoutPanel = function(layout, config)
45512 {
45513     // construct with only one argument..
45514     /* FIXME - implement nicer consturctors
45515     if (layout.layout) {
45516         config = layout;
45517         layout = config.layout;
45518         delete config.layout;
45519     }
45520     if (layout.xtype && !layout.getEl) {
45521         // then layout needs constructing..
45522         layout = Roo.factory(layout, Roo);
45523     }
45524     */
45525     
45526     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45527     
45528     layout.monitorWindowResize = false; // turn off autosizing
45529     this.layout = layout;
45530     this.layout.getEl().addClass("x-layout-nested-layout");
45531     
45532     
45533     
45534 };
45535
45536 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45537
45538     setSize : function(width, height){
45539         if(!this.ignoreResize(width, height)){
45540             var size = this.adjustForComponents(width, height);
45541             var el = this.layout.getEl();
45542             el.setSize(size.width, size.height);
45543             var touch = el.dom.offsetWidth;
45544             this.layout.layout();
45545             // ie requires a double layout on the first pass
45546             if(Roo.isIE && !this.initialized){
45547                 this.initialized = true;
45548                 this.layout.layout();
45549             }
45550         }
45551     },
45552     
45553     // activate all subpanels if not currently active..
45554     
45555     setActiveState : function(active){
45556         this.active = active;
45557         if(!active){
45558             this.fireEvent("deactivate", this);
45559             return;
45560         }
45561         
45562         this.fireEvent("activate", this);
45563         // not sure if this should happen before or after..
45564         if (!this.layout) {
45565             return; // should not happen..
45566         }
45567         var reg = false;
45568         for (var r in this.layout.regions) {
45569             reg = this.layout.getRegion(r);
45570             if (reg.getActivePanel()) {
45571                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45572                 reg.setActivePanel(reg.getActivePanel());
45573                 continue;
45574             }
45575             if (!reg.panels.length) {
45576                 continue;
45577             }
45578             reg.showPanel(reg.getPanel(0));
45579         }
45580         
45581         
45582         
45583         
45584     },
45585     
45586     /**
45587      * Returns the nested BorderLayout for this panel
45588      * @return {Roo.BorderLayout} 
45589      */
45590     getLayout : function(){
45591         return this.layout;
45592     },
45593     
45594      /**
45595      * Adds a xtype elements to the layout of the nested panel
45596      * <pre><code>
45597
45598 panel.addxtype({
45599        xtype : 'ContentPanel',
45600        region: 'west',
45601        items: [ .... ]
45602    }
45603 );
45604
45605 panel.addxtype({
45606         xtype : 'NestedLayoutPanel',
45607         region: 'west',
45608         layout: {
45609            center: { },
45610            west: { }   
45611         },
45612         items : [ ... list of content panels or nested layout panels.. ]
45613    }
45614 );
45615 </code></pre>
45616      * @param {Object} cfg Xtype definition of item to add.
45617      */
45618     addxtype : function(cfg) {
45619         return this.layout.addxtype(cfg);
45620     
45621     }
45622 });
45623
45624 Roo.ScrollPanel = function(el, config, content){
45625     config = config || {};
45626     config.fitToFrame = true;
45627     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45628     
45629     this.el.dom.style.overflow = "hidden";
45630     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45631     this.el.removeClass("x-layout-inactive-content");
45632     this.el.on("mousewheel", this.onWheel, this);
45633
45634     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45635     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45636     up.unselectable(); down.unselectable();
45637     up.on("click", this.scrollUp, this);
45638     down.on("click", this.scrollDown, this);
45639     up.addClassOnOver("x-scroller-btn-over");
45640     down.addClassOnOver("x-scroller-btn-over");
45641     up.addClassOnClick("x-scroller-btn-click");
45642     down.addClassOnClick("x-scroller-btn-click");
45643     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45644
45645     this.resizeEl = this.el;
45646     this.el = wrap; this.up = up; this.down = down;
45647 };
45648
45649 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45650     increment : 100,
45651     wheelIncrement : 5,
45652     scrollUp : function(){
45653         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45654     },
45655
45656     scrollDown : function(){
45657         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45658     },
45659
45660     afterScroll : function(){
45661         var el = this.resizeEl;
45662         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45663         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45664         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45665     },
45666
45667     setSize : function(){
45668         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45669         this.afterScroll();
45670     },
45671
45672     onWheel : function(e){
45673         var d = e.getWheelDelta();
45674         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45675         this.afterScroll();
45676         e.stopEvent();
45677     },
45678
45679     setContent : function(content, loadScripts){
45680         this.resizeEl.update(content, loadScripts);
45681     }
45682
45683 });
45684
45685
45686
45687
45688
45689
45690
45691
45692
45693 /**
45694  * @class Roo.TreePanel
45695  * @extends Roo.ContentPanel
45696  * @constructor
45697  * Create a new TreePanel. - defaults to fit/scoll contents.
45698  * @param {String/Object} config A string to set only the panel's title, or a config object
45699  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45700  */
45701 Roo.TreePanel = function(config){
45702     var el = config.el;
45703     var tree = config.tree;
45704     delete config.tree; 
45705     delete config.el; // hopefull!
45706     
45707     // wrapper for IE7 strict & safari scroll issue
45708     
45709     var treeEl = el.createChild();
45710     config.resizeEl = treeEl;
45711     
45712     
45713     
45714     Roo.TreePanel.superclass.constructor.call(this, el, config);
45715  
45716  
45717     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45718     //console.log(tree);
45719     this.on('activate', function()
45720     {
45721         if (this.tree.rendered) {
45722             return;
45723         }
45724         //console.log('render tree');
45725         this.tree.render();
45726     });
45727     
45728     this.on('resize',  function (cp, w, h) {
45729             this.tree.innerCt.setWidth(w);
45730             this.tree.innerCt.setHeight(h);
45731             this.tree.innerCt.setStyle('overflow-y', 'auto');
45732     });
45733
45734         
45735     
45736 };
45737
45738 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
45739     fitToFrame : true,
45740     autoScroll : true
45741 });
45742
45743
45744
45745
45746
45747
45748
45749
45750
45751
45752
45753 /*
45754  * Based on:
45755  * Ext JS Library 1.1.1
45756  * Copyright(c) 2006-2007, Ext JS, LLC.
45757  *
45758  * Originally Released Under LGPL - original licence link has changed is not relivant.
45759  *
45760  * Fork - LGPL
45761  * <script type="text/javascript">
45762  */
45763  
45764
45765 /**
45766  * @class Roo.ReaderLayout
45767  * @extends Roo.BorderLayout
45768  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45769  * center region containing two nested regions (a top one for a list view and one for item preview below),
45770  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45771  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45772  * expedites the setup of the overall layout and regions for this common application style.
45773  * Example:
45774  <pre><code>
45775 var reader = new Roo.ReaderLayout();
45776 var CP = Roo.ContentPanel;  // shortcut for adding
45777
45778 reader.beginUpdate();
45779 reader.add("north", new CP("north", "North"));
45780 reader.add("west", new CP("west", {title: "West"}));
45781 reader.add("east", new CP("east", {title: "East"}));
45782
45783 reader.regions.listView.add(new CP("listView", "List"));
45784 reader.regions.preview.add(new CP("preview", "Preview"));
45785 reader.endUpdate();
45786 </code></pre>
45787 * @constructor
45788 * Create a new ReaderLayout
45789 * @param {Object} config Configuration options
45790 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45791 * document.body if omitted)
45792 */
45793 Roo.ReaderLayout = function(config, renderTo){
45794     var c = config || {size:{}};
45795     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45796         north: c.north !== false ? Roo.apply({
45797             split:false,
45798             initialSize: 32,
45799             titlebar: false
45800         }, c.north) : false,
45801         west: c.west !== false ? Roo.apply({
45802             split:true,
45803             initialSize: 200,
45804             minSize: 175,
45805             maxSize: 400,
45806             titlebar: true,
45807             collapsible: true,
45808             animate: true,
45809             margins:{left:5,right:0,bottom:5,top:5},
45810             cmargins:{left:5,right:5,bottom:5,top:5}
45811         }, c.west) : false,
45812         east: c.east !== false ? Roo.apply({
45813             split:true,
45814             initialSize: 200,
45815             minSize: 175,
45816             maxSize: 400,
45817             titlebar: true,
45818             collapsible: true,
45819             animate: true,
45820             margins:{left:0,right:5,bottom:5,top:5},
45821             cmargins:{left:5,right:5,bottom:5,top:5}
45822         }, c.east) : false,
45823         center: Roo.apply({
45824             tabPosition: 'top',
45825             autoScroll:false,
45826             closeOnTab: true,
45827             titlebar:false,
45828             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45829         }, c.center)
45830     });
45831
45832     this.el.addClass('x-reader');
45833
45834     this.beginUpdate();
45835
45836     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45837         south: c.preview !== false ? Roo.apply({
45838             split:true,
45839             initialSize: 200,
45840             minSize: 100,
45841             autoScroll:true,
45842             collapsible:true,
45843             titlebar: true,
45844             cmargins:{top:5,left:0, right:0, bottom:0}
45845         }, c.preview) : false,
45846         center: Roo.apply({
45847             autoScroll:false,
45848             titlebar:false,
45849             minHeight:200
45850         }, c.listView)
45851     });
45852     this.add('center', new Roo.NestedLayoutPanel(inner,
45853             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45854
45855     this.endUpdate();
45856
45857     this.regions.preview = inner.getRegion('south');
45858     this.regions.listView = inner.getRegion('center');
45859 };
45860
45861 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45862  * Based on:
45863  * Ext JS Library 1.1.1
45864  * Copyright(c) 2006-2007, Ext JS, LLC.
45865  *
45866  * Originally Released Under LGPL - original licence link has changed is not relivant.
45867  *
45868  * Fork - LGPL
45869  * <script type="text/javascript">
45870  */
45871  
45872 /**
45873  * @class Roo.grid.Grid
45874  * @extends Roo.util.Observable
45875  * This class represents the primary interface of a component based grid control.
45876  * <br><br>Usage:<pre><code>
45877  var grid = new Roo.grid.Grid("my-container-id", {
45878      ds: myDataStore,
45879      cm: myColModel,
45880      selModel: mySelectionModel,
45881      autoSizeColumns: true,
45882      monitorWindowResize: false,
45883      trackMouseOver: true
45884  });
45885  // set any options
45886  grid.render();
45887  * </code></pre>
45888  * <b>Common Problems:</b><br/>
45889  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45890  * element will correct this<br/>
45891  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45892  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45893  * are unpredictable.<br/>
45894  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45895  * grid to calculate dimensions/offsets.<br/>
45896   * @constructor
45897  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45898  * The container MUST have some type of size defined for the grid to fill. The container will be
45899  * automatically set to position relative if it isn't already.
45900  * @param {Object} config A config object that sets properties on this grid.
45901  */
45902 Roo.grid.Grid = function(container, config){
45903         // initialize the container
45904         this.container = Roo.get(container);
45905         this.container.update("");
45906         this.container.setStyle("overflow", "hidden");
45907     this.container.addClass('x-grid-container');
45908
45909     this.id = this.container.id;
45910
45911     Roo.apply(this, config);
45912     // check and correct shorthanded configs
45913     if(this.ds){
45914         this.dataSource = this.ds;
45915         delete this.ds;
45916     }
45917     if(this.cm){
45918         this.colModel = this.cm;
45919         delete this.cm;
45920     }
45921     if(this.sm){
45922         this.selModel = this.sm;
45923         delete this.sm;
45924     }
45925
45926     if (this.selModel) {
45927         this.selModel = Roo.factory(this.selModel, Roo.grid);
45928         this.sm = this.selModel;
45929         this.sm.xmodule = this.xmodule || false;
45930     }
45931     if (typeof(this.colModel.config) == 'undefined') {
45932         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45933         this.cm = this.colModel;
45934         this.cm.xmodule = this.xmodule || false;
45935     }
45936     if (this.dataSource) {
45937         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45938         this.ds = this.dataSource;
45939         this.ds.xmodule = this.xmodule || false;
45940         
45941     }
45942     
45943     
45944     
45945     if(this.width){
45946         this.container.setWidth(this.width);
45947     }
45948
45949     if(this.height){
45950         this.container.setHeight(this.height);
45951     }
45952     /** @private */
45953         this.addEvents({
45954             // raw events
45955             /**
45956              * @event click
45957              * The raw click event for the entire grid.
45958              * @param {Roo.EventObject} e
45959              */
45960             "click" : true,
45961             /**
45962              * @event dblclick
45963              * The raw dblclick event for the entire grid.
45964              * @param {Roo.EventObject} e
45965              */
45966             "dblclick" : true,
45967             /**
45968              * @event contextmenu
45969              * The raw contextmenu event for the entire grid.
45970              * @param {Roo.EventObject} e
45971              */
45972             "contextmenu" : true,
45973             /**
45974              * @event mousedown
45975              * The raw mousedown event for the entire grid.
45976              * @param {Roo.EventObject} e
45977              */
45978             "mousedown" : true,
45979             /**
45980              * @event mouseup
45981              * The raw mouseup event for the entire grid.
45982              * @param {Roo.EventObject} e
45983              */
45984             "mouseup" : true,
45985             /**
45986              * @event mouseover
45987              * The raw mouseover event for the entire grid.
45988              * @param {Roo.EventObject} e
45989              */
45990             "mouseover" : true,
45991             /**
45992              * @event mouseout
45993              * The raw mouseout event for the entire grid.
45994              * @param {Roo.EventObject} e
45995              */
45996             "mouseout" : true,
45997             /**
45998              * @event keypress
45999              * The raw keypress event for the entire grid.
46000              * @param {Roo.EventObject} e
46001              */
46002             "keypress" : true,
46003             /**
46004              * @event keydown
46005              * The raw keydown event for the entire grid.
46006              * @param {Roo.EventObject} e
46007              */
46008             "keydown" : true,
46009
46010             // custom events
46011
46012             /**
46013              * @event cellclick
46014              * Fires when a cell is clicked
46015              * @param {Grid} this
46016              * @param {Number} rowIndex
46017              * @param {Number} columnIndex
46018              * @param {Roo.EventObject} e
46019              */
46020             "cellclick" : true,
46021             /**
46022              * @event celldblclick
46023              * Fires when a cell is double clicked
46024              * @param {Grid} this
46025              * @param {Number} rowIndex
46026              * @param {Number} columnIndex
46027              * @param {Roo.EventObject} e
46028              */
46029             "celldblclick" : true,
46030             /**
46031              * @event rowclick
46032              * Fires when a row is clicked
46033              * @param {Grid} this
46034              * @param {Number} rowIndex
46035              * @param {Roo.EventObject} e
46036              */
46037             "rowclick" : true,
46038             /**
46039              * @event rowdblclick
46040              * Fires when a row is double clicked
46041              * @param {Grid} this
46042              * @param {Number} rowIndex
46043              * @param {Roo.EventObject} e
46044              */
46045             "rowdblclick" : true,
46046             /**
46047              * @event headerclick
46048              * Fires when a header is clicked
46049              * @param {Grid} this
46050              * @param {Number} columnIndex
46051              * @param {Roo.EventObject} e
46052              */
46053             "headerclick" : true,
46054             /**
46055              * @event headerdblclick
46056              * Fires when a header cell is double clicked
46057              * @param {Grid} this
46058              * @param {Number} columnIndex
46059              * @param {Roo.EventObject} e
46060              */
46061             "headerdblclick" : true,
46062             /**
46063              * @event rowcontextmenu
46064              * Fires when a row is right clicked
46065              * @param {Grid} this
46066              * @param {Number} rowIndex
46067              * @param {Roo.EventObject} e
46068              */
46069             "rowcontextmenu" : true,
46070             /**
46071          * @event cellcontextmenu
46072          * Fires when a cell is right clicked
46073          * @param {Grid} this
46074          * @param {Number} rowIndex
46075          * @param {Number} cellIndex
46076          * @param {Roo.EventObject} e
46077          */
46078          "cellcontextmenu" : true,
46079             /**
46080              * @event headercontextmenu
46081              * Fires when a header is right clicked
46082              * @param {Grid} this
46083              * @param {Number} columnIndex
46084              * @param {Roo.EventObject} e
46085              */
46086             "headercontextmenu" : true,
46087             /**
46088              * @event bodyscroll
46089              * Fires when the body element is scrolled
46090              * @param {Number} scrollLeft
46091              * @param {Number} scrollTop
46092              */
46093             "bodyscroll" : true,
46094             /**
46095              * @event columnresize
46096              * Fires when the user resizes a column
46097              * @param {Number} columnIndex
46098              * @param {Number} newSize
46099              */
46100             "columnresize" : true,
46101             /**
46102              * @event columnmove
46103              * Fires when the user moves a column
46104              * @param {Number} oldIndex
46105              * @param {Number} newIndex
46106              */
46107             "columnmove" : true,
46108             /**
46109              * @event startdrag
46110              * Fires when row(s) start being dragged
46111              * @param {Grid} this
46112              * @param {Roo.GridDD} dd The drag drop object
46113              * @param {event} e The raw browser event
46114              */
46115             "startdrag" : true,
46116             /**
46117              * @event enddrag
46118              * Fires when a drag operation is complete
46119              * @param {Grid} this
46120              * @param {Roo.GridDD} dd The drag drop object
46121              * @param {event} e The raw browser event
46122              */
46123             "enddrag" : true,
46124             /**
46125              * @event dragdrop
46126              * Fires when dragged row(s) are dropped on a valid DD target
46127              * @param {Grid} this
46128              * @param {Roo.GridDD} dd The drag drop object
46129              * @param {String} targetId The target drag drop object
46130              * @param {event} e The raw browser event
46131              */
46132             "dragdrop" : true,
46133             /**
46134              * @event dragover
46135              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46136              * @param {Grid} this
46137              * @param {Roo.GridDD} dd The drag drop object
46138              * @param {String} targetId The target drag drop object
46139              * @param {event} e The raw browser event
46140              */
46141             "dragover" : true,
46142             /**
46143              * @event dragenter
46144              *  Fires when the dragged row(s) first cross another DD target while being dragged
46145              * @param {Grid} this
46146              * @param {Roo.GridDD} dd The drag drop object
46147              * @param {String} targetId The target drag drop object
46148              * @param {event} e The raw browser event
46149              */
46150             "dragenter" : true,
46151             /**
46152              * @event dragout
46153              * Fires when the dragged row(s) leave another DD target while being dragged
46154              * @param {Grid} this
46155              * @param {Roo.GridDD} dd The drag drop object
46156              * @param {String} targetId The target drag drop object
46157              * @param {event} e The raw browser event
46158              */
46159             "dragout" : true,
46160         /**
46161          * @event render
46162          * Fires when the grid is rendered
46163          * @param {Grid} grid
46164          */
46165         render : true
46166     });
46167
46168     Roo.grid.Grid.superclass.constructor.call(this);
46169 };
46170 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46171     
46172     /**
46173      * @cfg {String} ddGroup - drag drop group.
46174          */
46175     
46176     /**
46177      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46178          */
46179         minColumnWidth : 25,
46180
46181     /**
46182          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46183          * <b>on initial render.</b> It is more efficient to explicitly size the columns
46184          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46185          */
46186         autoSizeColumns : false,
46187
46188         /**
46189          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46190          */
46191         autoSizeHeaders : true,
46192
46193         /**
46194          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46195          */
46196         monitorWindowResize : true,
46197
46198         /**
46199          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46200          * rows measured to get a columns size. Default is 0 (all rows).
46201          */
46202         maxRowsToMeasure : 0,
46203
46204         /**
46205          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46206          */
46207         trackMouseOver : true,
46208
46209     /**
46210          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46211          */
46212     
46213         /**
46214          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46215          */
46216         enableDragDrop : false,
46217
46218         /**
46219          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46220          */
46221         enableColumnMove : true,
46222
46223         /**
46224          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46225          */
46226         enableColumnHide : true,
46227
46228         /**
46229          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46230          */
46231         enableRowHeightSync : false,
46232
46233         /**
46234          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46235          */
46236         stripeRows : true,
46237
46238         /**
46239          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46240          */
46241         autoHeight : false,
46242
46243     /**
46244      * @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.
46245      */
46246     autoExpandColumn : false,
46247
46248     /**
46249     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46250     * Default is 50.
46251     */
46252     autoExpandMin : 50,
46253
46254     /**
46255     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46256     */
46257     autoExpandMax : 1000,
46258
46259     /**
46260          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46261          */
46262         view : null,
46263
46264         /**
46265      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46266          */
46267         loadMask : false,
46268
46269     // private
46270     rendered : false,
46271
46272     /**
46273     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46274     * of a fixed width. Default is false.
46275     */
46276     /**
46277     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46278     */
46279     /**
46280      * Called once after all setup has been completed and the grid is ready to be rendered.
46281      * @return {Roo.grid.Grid} this
46282      */
46283     render : function(){
46284         var c = this.container;
46285         // try to detect autoHeight/width mode
46286         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46287             this.autoHeight = true;
46288         }
46289         var view = this.getView();
46290         view.init(this);
46291
46292         c.on("click", this.onClick, this);
46293         c.on("dblclick", this.onDblClick, this);
46294         c.on("contextmenu", this.onContextMenu, this);
46295         c.on("keydown", this.onKeyDown, this);
46296
46297         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46298
46299         this.getSelectionModel().init(this);
46300
46301         view.render();
46302
46303         if(this.loadMask){
46304             this.loadMask = new Roo.LoadMask(this.container,
46305                     Roo.apply({store:this.dataSource}, this.loadMask));
46306         }
46307         
46308         
46309         if (this.toolbar && this.toolbar.xtype) {
46310             this.toolbar.container = this.getView().getHeaderPanel(true);
46311             this.toolbar = new Ext.Toolbar(this.toolbar);
46312         }
46313         if (this.footer && this.footer.xtype) {
46314             this.footer.dataSource = this.getDataSource();
46315             this.footer.container = this.getView().getFooterPanel(true);
46316             this.footer = Roo.factory(this.footer, Roo);
46317         }
46318         this.rendered = true;
46319         this.fireEvent('render', this);
46320         return this;
46321     },
46322
46323         /**
46324          * Reconfigures the grid to use a different Store and Column Model.
46325          * The View will be bound to the new objects and refreshed.
46326          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46327          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46328          */
46329     reconfigure : function(dataSource, colModel){
46330         if(this.loadMask){
46331             this.loadMask.destroy();
46332             this.loadMask = new Roo.LoadMask(this.container,
46333                     Roo.apply({store:dataSource}, this.loadMask));
46334         }
46335         this.view.bind(dataSource, colModel);
46336         this.dataSource = dataSource;
46337         this.colModel = colModel;
46338         this.view.refresh(true);
46339     },
46340
46341     // private
46342     onKeyDown : function(e){
46343         this.fireEvent("keydown", e);
46344     },
46345
46346     /**
46347      * Destroy this grid.
46348      * @param {Boolean} removeEl True to remove the element
46349      */
46350     destroy : function(removeEl, keepListeners){
46351         if(this.loadMask){
46352             this.loadMask.destroy();
46353         }
46354         var c = this.container;
46355         c.removeAllListeners();
46356         this.view.destroy();
46357         this.colModel.purgeListeners();
46358         if(!keepListeners){
46359             this.purgeListeners();
46360         }
46361         c.update("");
46362         if(removeEl === true){
46363             c.remove();
46364         }
46365     },
46366
46367     // private
46368     processEvent : function(name, e){
46369         this.fireEvent(name, e);
46370         var t = e.getTarget();
46371         var v = this.view;
46372         var header = v.findHeaderIndex(t);
46373         if(header !== false){
46374             this.fireEvent("header" + name, this, header, e);
46375         }else{
46376             var row = v.findRowIndex(t);
46377             var cell = v.findCellIndex(t);
46378             if(row !== false){
46379                 this.fireEvent("row" + name, this, row, e);
46380                 if(cell !== false){
46381                     this.fireEvent("cell" + name, this, row, cell, e);
46382                 }
46383             }
46384         }
46385     },
46386
46387     // private
46388     onClick : function(e){
46389         this.processEvent("click", e);
46390     },
46391
46392     // private
46393     onContextMenu : function(e, t){
46394         this.processEvent("contextmenu", e);
46395     },
46396
46397     // private
46398     onDblClick : function(e){
46399         this.processEvent("dblclick", e);
46400     },
46401
46402     // private
46403     walkCells : function(row, col, step, fn, scope){
46404         var cm = this.colModel, clen = cm.getColumnCount();
46405         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46406         if(step < 0){
46407             if(col < 0){
46408                 row--;
46409                 first = false;
46410             }
46411             while(row >= 0){
46412                 if(!first){
46413                     col = clen-1;
46414                 }
46415                 first = false;
46416                 while(col >= 0){
46417                     if(fn.call(scope || this, row, col, cm) === true){
46418                         return [row, col];
46419                     }
46420                     col--;
46421                 }
46422                 row--;
46423             }
46424         } else {
46425             if(col >= clen){
46426                 row++;
46427                 first = false;
46428             }
46429             while(row < rlen){
46430                 if(!first){
46431                     col = 0;
46432                 }
46433                 first = false;
46434                 while(col < clen){
46435                     if(fn.call(scope || this, row, col, cm) === true){
46436                         return [row, col];
46437                     }
46438                     col++;
46439                 }
46440                 row++;
46441             }
46442         }
46443         return null;
46444     },
46445
46446     // private
46447     getSelections : function(){
46448         return this.selModel.getSelections();
46449     },
46450
46451     /**
46452      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46453      * but if manual update is required this method will initiate it.
46454      */
46455     autoSize : function(){
46456         if(this.rendered){
46457             this.view.layout();
46458             if(this.view.adjustForScroll){
46459                 this.view.adjustForScroll();
46460             }
46461         }
46462     },
46463
46464     /**
46465      * Returns the grid's underlying element.
46466      * @return {Element} The element
46467      */
46468     getGridEl : function(){
46469         return this.container;
46470     },
46471
46472     // private for compatibility, overridden by editor grid
46473     stopEditing : function(){},
46474
46475     /**
46476      * Returns the grid's SelectionModel.
46477      * @return {SelectionModel}
46478      */
46479     getSelectionModel : function(){
46480         if(!this.selModel){
46481             this.selModel = new Roo.grid.RowSelectionModel();
46482         }
46483         return this.selModel;
46484     },
46485
46486     /**
46487      * Returns the grid's DataSource.
46488      * @return {DataSource}
46489      */
46490     getDataSource : function(){
46491         return this.dataSource;
46492     },
46493
46494     /**
46495      * Returns the grid's ColumnModel.
46496      * @return {ColumnModel}
46497      */
46498     getColumnModel : function(){
46499         return this.colModel;
46500     },
46501
46502     /**
46503      * Returns the grid's GridView object.
46504      * @return {GridView}
46505      */
46506     getView : function(){
46507         if(!this.view){
46508             this.view = new Roo.grid.GridView(this.viewConfig);
46509         }
46510         return this.view;
46511     },
46512     /**
46513      * Called to get grid's drag proxy text, by default returns this.ddText.
46514      * @return {String}
46515      */
46516     getDragDropText : function(){
46517         var count = this.selModel.getCount();
46518         return String.format(this.ddText, count, count == 1 ? '' : 's');
46519     }
46520 });
46521 /**
46522  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46523  * %0 is replaced with the number of selected rows.
46524  * @type String
46525  */
46526 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46527  * Based on:
46528  * Ext JS Library 1.1.1
46529  * Copyright(c) 2006-2007, Ext JS, LLC.
46530  *
46531  * Originally Released Under LGPL - original licence link has changed is not relivant.
46532  *
46533  * Fork - LGPL
46534  * <script type="text/javascript">
46535  */
46536  
46537 Roo.grid.AbstractGridView = function(){
46538         this.grid = null;
46539         
46540         this.events = {
46541             "beforerowremoved" : true,
46542             "beforerowsinserted" : true,
46543             "beforerefresh" : true,
46544             "rowremoved" : true,
46545             "rowsinserted" : true,
46546             "rowupdated" : true,
46547             "refresh" : true
46548         };
46549     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46550 };
46551
46552 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46553     rowClass : "x-grid-row",
46554     cellClass : "x-grid-cell",
46555     tdClass : "x-grid-td",
46556     hdClass : "x-grid-hd",
46557     splitClass : "x-grid-hd-split",
46558     
46559         init: function(grid){
46560         this.grid = grid;
46561                 var cid = this.grid.getGridEl().id;
46562         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46563         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46564         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46565         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46566         },
46567         
46568         getColumnRenderers : function(){
46569         var renderers = [];
46570         var cm = this.grid.colModel;
46571         var colCount = cm.getColumnCount();
46572         for(var i = 0; i < colCount; i++){
46573             renderers[i] = cm.getRenderer(i);
46574         }
46575         return renderers;
46576     },
46577     
46578     getColumnIds : function(){
46579         var ids = [];
46580         var cm = this.grid.colModel;
46581         var colCount = cm.getColumnCount();
46582         for(var i = 0; i < colCount; i++){
46583             ids[i] = cm.getColumnId(i);
46584         }
46585         return ids;
46586     },
46587     
46588     getDataIndexes : function(){
46589         if(!this.indexMap){
46590             this.indexMap = this.buildIndexMap();
46591         }
46592         return this.indexMap.colToData;
46593     },
46594     
46595     getColumnIndexByDataIndex : function(dataIndex){
46596         if(!this.indexMap){
46597             this.indexMap = this.buildIndexMap();
46598         }
46599         return this.indexMap.dataToCol[dataIndex];
46600     },
46601     
46602     /**
46603      * Set a css style for a column dynamically. 
46604      * @param {Number} colIndex The index of the column
46605      * @param {String} name The css property name
46606      * @param {String} value The css value
46607      */
46608     setCSSStyle : function(colIndex, name, value){
46609         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46610         Roo.util.CSS.updateRule(selector, name, value);
46611     },
46612     
46613     generateRules : function(cm){
46614         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46615         Roo.util.CSS.removeStyleSheet(rulesId);
46616         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46617             var cid = cm.getColumnId(i);
46618             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46619                          this.tdSelector, cid, " {\n}\n",
46620                          this.hdSelector, cid, " {\n}\n",
46621                          this.splitSelector, cid, " {\n}\n");
46622         }
46623         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46624     }
46625 });/*
46626  * Based on:
46627  * Ext JS Library 1.1.1
46628  * Copyright(c) 2006-2007, Ext JS, LLC.
46629  *
46630  * Originally Released Under LGPL - original licence link has changed is not relivant.
46631  *
46632  * Fork - LGPL
46633  * <script type="text/javascript">
46634  */
46635
46636 // private
46637 // This is a support class used internally by the Grid components
46638 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46639     this.grid = grid;
46640     this.view = grid.getView();
46641     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46642     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46643     if(hd2){
46644         this.setHandleElId(Roo.id(hd));
46645         this.setOuterHandleElId(Roo.id(hd2));
46646     }
46647     this.scroll = false;
46648 };
46649 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46650     maxDragWidth: 120,
46651     getDragData : function(e){
46652         var t = Roo.lib.Event.getTarget(e);
46653         var h = this.view.findHeaderCell(t);
46654         if(h){
46655             return {ddel: h.firstChild, header:h};
46656         }
46657         return false;
46658     },
46659
46660     onInitDrag : function(e){
46661         this.view.headersDisabled = true;
46662         var clone = this.dragData.ddel.cloneNode(true);
46663         clone.id = Roo.id();
46664         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46665         this.proxy.update(clone);
46666         return true;
46667     },
46668
46669     afterValidDrop : function(){
46670         var v = this.view;
46671         setTimeout(function(){
46672             v.headersDisabled = false;
46673         }, 50);
46674     },
46675
46676     afterInvalidDrop : function(){
46677         var v = this.view;
46678         setTimeout(function(){
46679             v.headersDisabled = false;
46680         }, 50);
46681     }
46682 });
46683 /*
46684  * Based on:
46685  * Ext JS Library 1.1.1
46686  * Copyright(c) 2006-2007, Ext JS, LLC.
46687  *
46688  * Originally Released Under LGPL - original licence link has changed is not relivant.
46689  *
46690  * Fork - LGPL
46691  * <script type="text/javascript">
46692  */
46693 // private
46694 // This is a support class used internally by the Grid components
46695 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46696     this.grid = grid;
46697     this.view = grid.getView();
46698     // split the proxies so they don't interfere with mouse events
46699     this.proxyTop = Roo.DomHelper.append(document.body, {
46700         cls:"col-move-top", html:"&#160;"
46701     }, true);
46702     this.proxyBottom = Roo.DomHelper.append(document.body, {
46703         cls:"col-move-bottom", html:"&#160;"
46704     }, true);
46705     this.proxyTop.hide = this.proxyBottom.hide = function(){
46706         this.setLeftTop(-100,-100);
46707         this.setStyle("visibility", "hidden");
46708     };
46709     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46710     // temporarily disabled
46711     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46712     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46713 };
46714 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46715     proxyOffsets : [-4, -9],
46716     fly: Roo.Element.fly,
46717
46718     getTargetFromEvent : function(e){
46719         var t = Roo.lib.Event.getTarget(e);
46720         var cindex = this.view.findCellIndex(t);
46721         if(cindex !== false){
46722             return this.view.getHeaderCell(cindex);
46723         }
46724     },
46725
46726     nextVisible : function(h){
46727         var v = this.view, cm = this.grid.colModel;
46728         h = h.nextSibling;
46729         while(h){
46730             if(!cm.isHidden(v.getCellIndex(h))){
46731                 return h;
46732             }
46733             h = h.nextSibling;
46734         }
46735         return null;
46736     },
46737
46738     prevVisible : function(h){
46739         var v = this.view, cm = this.grid.colModel;
46740         h = h.prevSibling;
46741         while(h){
46742             if(!cm.isHidden(v.getCellIndex(h))){
46743                 return h;
46744             }
46745             h = h.prevSibling;
46746         }
46747         return null;
46748     },
46749
46750     positionIndicator : function(h, n, e){
46751         var x = Roo.lib.Event.getPageX(e);
46752         var r = Roo.lib.Dom.getRegion(n.firstChild);
46753         var px, pt, py = r.top + this.proxyOffsets[1];
46754         if((r.right - x) <= (r.right-r.left)/2){
46755             px = r.right+this.view.borderWidth;
46756             pt = "after";
46757         }else{
46758             px = r.left;
46759             pt = "before";
46760         }
46761         var oldIndex = this.view.getCellIndex(h);
46762         var newIndex = this.view.getCellIndex(n);
46763
46764         if(this.grid.colModel.isFixed(newIndex)){
46765             return false;
46766         }
46767
46768         var locked = this.grid.colModel.isLocked(newIndex);
46769
46770         if(pt == "after"){
46771             newIndex++;
46772         }
46773         if(oldIndex < newIndex){
46774             newIndex--;
46775         }
46776         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46777             return false;
46778         }
46779         px +=  this.proxyOffsets[0];
46780         this.proxyTop.setLeftTop(px, py);
46781         this.proxyTop.show();
46782         if(!this.bottomOffset){
46783             this.bottomOffset = this.view.mainHd.getHeight();
46784         }
46785         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46786         this.proxyBottom.show();
46787         return pt;
46788     },
46789
46790     onNodeEnter : function(n, dd, e, data){
46791         if(data.header != n){
46792             this.positionIndicator(data.header, n, e);
46793         }
46794     },
46795
46796     onNodeOver : function(n, dd, e, data){
46797         var result = false;
46798         if(data.header != n){
46799             result = this.positionIndicator(data.header, n, e);
46800         }
46801         if(!result){
46802             this.proxyTop.hide();
46803             this.proxyBottom.hide();
46804         }
46805         return result ? this.dropAllowed : this.dropNotAllowed;
46806     },
46807
46808     onNodeOut : function(n, dd, e, data){
46809         this.proxyTop.hide();
46810         this.proxyBottom.hide();
46811     },
46812
46813     onNodeDrop : function(n, dd, e, data){
46814         var h = data.header;
46815         if(h != n){
46816             var cm = this.grid.colModel;
46817             var x = Roo.lib.Event.getPageX(e);
46818             var r = Roo.lib.Dom.getRegion(n.firstChild);
46819             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46820             var oldIndex = this.view.getCellIndex(h);
46821             var newIndex = this.view.getCellIndex(n);
46822             var locked = cm.isLocked(newIndex);
46823             if(pt == "after"){
46824                 newIndex++;
46825             }
46826             if(oldIndex < newIndex){
46827                 newIndex--;
46828             }
46829             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46830                 return false;
46831             }
46832             cm.setLocked(oldIndex, locked, true);
46833             cm.moveColumn(oldIndex, newIndex);
46834             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46835             return true;
46836         }
46837         return false;
46838     }
46839 });
46840 /*
46841  * Based on:
46842  * Ext JS Library 1.1.1
46843  * Copyright(c) 2006-2007, Ext JS, LLC.
46844  *
46845  * Originally Released Under LGPL - original licence link has changed is not relivant.
46846  *
46847  * Fork - LGPL
46848  * <script type="text/javascript">
46849  */
46850   
46851 /**
46852  * @class Roo.grid.GridView
46853  * @extends Roo.util.Observable
46854  *
46855  * @constructor
46856  * @param {Object} config
46857  */
46858 Roo.grid.GridView = function(config){
46859     Roo.grid.GridView.superclass.constructor.call(this);
46860     this.el = null;
46861
46862     Roo.apply(this, config);
46863 };
46864
46865 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46866
46867     /**
46868      * Override this function to apply custom css classes to rows during rendering
46869      * @param {Record} record The record
46870      * @param {Number} index
46871      * @method getRowClass
46872      */
46873     rowClass : "x-grid-row",
46874
46875     cellClass : "x-grid-col",
46876
46877     tdClass : "x-grid-td",
46878
46879     hdClass : "x-grid-hd",
46880
46881     splitClass : "x-grid-split",
46882
46883     sortClasses : ["sort-asc", "sort-desc"],
46884
46885     enableMoveAnim : false,
46886
46887     hlColor: "C3DAF9",
46888
46889     dh : Roo.DomHelper,
46890
46891     fly : Roo.Element.fly,
46892
46893     css : Roo.util.CSS,
46894
46895     borderWidth: 1,
46896
46897     splitOffset: 3,
46898
46899     scrollIncrement : 22,
46900
46901     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46902
46903     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46904
46905     bind : function(ds, cm){
46906         if(this.ds){
46907             this.ds.un("load", this.onLoad, this);
46908             this.ds.un("datachanged", this.onDataChange, this);
46909             this.ds.un("add", this.onAdd, this);
46910             this.ds.un("remove", this.onRemove, this);
46911             this.ds.un("update", this.onUpdate, this);
46912             this.ds.un("clear", this.onClear, this);
46913         }
46914         if(ds){
46915             ds.on("load", this.onLoad, this);
46916             ds.on("datachanged", this.onDataChange, this);
46917             ds.on("add", this.onAdd, this);
46918             ds.on("remove", this.onRemove, this);
46919             ds.on("update", this.onUpdate, this);
46920             ds.on("clear", this.onClear, this);
46921         }
46922         this.ds = ds;
46923
46924         if(this.cm){
46925             this.cm.un("widthchange", this.onColWidthChange, this);
46926             this.cm.un("headerchange", this.onHeaderChange, this);
46927             this.cm.un("hiddenchange", this.onHiddenChange, this);
46928             this.cm.un("columnmoved", this.onColumnMove, this);
46929             this.cm.un("columnlockchange", this.onColumnLock, this);
46930         }
46931         if(cm){
46932             this.generateRules(cm);
46933             cm.on("widthchange", this.onColWidthChange, this);
46934             cm.on("headerchange", this.onHeaderChange, this);
46935             cm.on("hiddenchange", this.onHiddenChange, this);
46936             cm.on("columnmoved", this.onColumnMove, this);
46937             cm.on("columnlockchange", this.onColumnLock, this);
46938         }
46939         this.cm = cm;
46940     },
46941
46942     init: function(grid){
46943                 Roo.grid.GridView.superclass.init.call(this, grid);
46944
46945                 this.bind(grid.dataSource, grid.colModel);
46946
46947             grid.on("headerclick", this.handleHeaderClick, this);
46948
46949         if(grid.trackMouseOver){
46950             grid.on("mouseover", this.onRowOver, this);
46951                 grid.on("mouseout", this.onRowOut, this);
46952             }
46953             grid.cancelTextSelection = function(){};
46954                 this.gridId = grid.id;
46955
46956                 var tpls = this.templates || {};
46957
46958                 if(!tpls.master){
46959                     tpls.master = new Roo.Template(
46960                        '<div class="x-grid" hidefocus="true">',
46961                           '<div class="x-grid-topbar"></div>',
46962                           '<div class="x-grid-scroller"><div></div></div>',
46963                           '<div class="x-grid-locked">',
46964                               '<div class="x-grid-header">{lockedHeader}</div>',
46965                               '<div class="x-grid-body">{lockedBody}</div>',
46966                           "</div>",
46967                           '<div class="x-grid-viewport">',
46968                               '<div class="x-grid-header">{header}</div>',
46969                               '<div class="x-grid-body">{body}</div>',
46970                           "</div>",
46971                           '<div class="x-grid-bottombar"></div>',
46972                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46973                           '<div class="x-grid-resize-proxy">&#160;</div>',
46974                        "</div>"
46975                     );
46976                     tpls.master.disableformats = true;
46977                 }
46978
46979                 if(!tpls.header){
46980                     tpls.header = new Roo.Template(
46981                        '<table border="0" cellspacing="0" cellpadding="0">',
46982                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46983                        "</table>{splits}"
46984                     );
46985                     tpls.header.disableformats = true;
46986                 }
46987                 tpls.header.compile();
46988
46989                 if(!tpls.hcell){
46990                     tpls.hcell = new Roo.Template(
46991                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46992                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46993                         "</div></td>"
46994                      );
46995                      tpls.hcell.disableFormats = true;
46996                 }
46997                 tpls.hcell.compile();
46998
46999                 if(!tpls.hsplit){
47000                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47001                     tpls.hsplit.disableFormats = true;
47002                 }
47003                 tpls.hsplit.compile();
47004
47005                 if(!tpls.body){
47006                     tpls.body = new Roo.Template(
47007                        '<table border="0" cellspacing="0" cellpadding="0">',
47008                        "<tbody>{rows}</tbody>",
47009                        "</table>"
47010                     );
47011                     tpls.body.disableFormats = true;
47012                 }
47013                 tpls.body.compile();
47014
47015                 if(!tpls.row){
47016                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47017                     tpls.row.disableFormats = true;
47018                 }
47019                 tpls.row.compile();
47020
47021                 if(!tpls.cell){
47022                     tpls.cell = new Roo.Template(
47023                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47024                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47025                         "</td>"
47026                     );
47027             tpls.cell.disableFormats = true;
47028         }
47029                 tpls.cell.compile();
47030
47031                 this.templates = tpls;
47032         },
47033
47034         // remap these for backwards compat
47035     onColWidthChange : function(){
47036         this.updateColumns.apply(this, arguments);
47037     },
47038     onHeaderChange : function(){
47039         this.updateHeaders.apply(this, arguments);
47040     }, 
47041     onHiddenChange : function(){
47042         this.handleHiddenChange.apply(this, arguments);
47043     },
47044     onColumnMove : function(){
47045         this.handleColumnMove.apply(this, arguments);
47046     },
47047     onColumnLock : function(){
47048         this.handleLockChange.apply(this, arguments);
47049     },
47050
47051     onDataChange : function(){
47052         this.refresh();
47053         this.updateHeaderSortState();
47054     },
47055
47056         onClear : function(){
47057         this.refresh();
47058     },
47059
47060         onUpdate : function(ds, record){
47061         this.refreshRow(record);
47062     },
47063
47064     refreshRow : function(record){
47065         var ds = this.ds, index;
47066         if(typeof record == 'number'){
47067             index = record;
47068             record = ds.getAt(index);
47069         }else{
47070             index = ds.indexOf(record);
47071         }
47072         this.insertRows(ds, index, index, true);
47073         this.onRemove(ds, record, index+1, true);
47074         this.syncRowHeights(index, index);
47075         this.layout();
47076         this.fireEvent("rowupdated", this, index, record);
47077     },
47078
47079     onAdd : function(ds, records, index){
47080         this.insertRows(ds, index, index + (records.length-1));
47081     },
47082
47083     onRemove : function(ds, record, index, isUpdate){
47084         if(isUpdate !== true){
47085             this.fireEvent("beforerowremoved", this, index, record);
47086         }
47087         var bt = this.getBodyTable(), lt = this.getLockedTable();
47088         if(bt.rows[index]){
47089             bt.firstChild.removeChild(bt.rows[index]);
47090         }
47091         if(lt.rows[index]){
47092             lt.firstChild.removeChild(lt.rows[index]);
47093         }
47094         if(isUpdate !== true){
47095             this.stripeRows(index);
47096             this.syncRowHeights(index, index);
47097             this.layout();
47098             this.fireEvent("rowremoved", this, index, record);
47099         }
47100     },
47101
47102     onLoad : function(){
47103         this.scrollToTop();
47104     },
47105
47106     /**
47107      * Scrolls the grid to the top
47108      */
47109     scrollToTop : function(){
47110         if(this.scroller){
47111             this.scroller.dom.scrollTop = 0;
47112             this.syncScroll();
47113         }
47114     },
47115
47116     /**
47117      * Gets a panel in the header of the grid that can be used for toolbars etc.
47118      * After modifying the contents of this panel a call to grid.autoSize() may be
47119      * required to register any changes in size.
47120      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47121      * @return Roo.Element
47122      */
47123     getHeaderPanel : function(doShow){
47124         if(doShow){
47125             this.headerPanel.show();
47126         }
47127         return this.headerPanel;
47128         },
47129
47130         /**
47131      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47132      * After modifying the contents of this panel a call to grid.autoSize() may be
47133      * required to register any changes in size.
47134      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47135      * @return Roo.Element
47136      */
47137     getFooterPanel : function(doShow){
47138         if(doShow){
47139             this.footerPanel.show();
47140         }
47141         return this.footerPanel;
47142         },
47143
47144         initElements : function(){
47145             var E = Roo.Element;
47146             var el = this.grid.getGridEl().dom.firstChild;
47147             var cs = el.childNodes;
47148
47149             this.el = new E(el);
47150             this.headerPanel = new E(el.firstChild);
47151             this.headerPanel.enableDisplayMode("block");
47152
47153         this.scroller = new E(cs[1]);
47154             this.scrollSizer = new E(this.scroller.dom.firstChild);
47155
47156             this.lockedWrap = new E(cs[2]);
47157             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47158             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47159
47160             this.mainWrap = new E(cs[3]);
47161             this.mainHd = new E(this.mainWrap.dom.firstChild);
47162             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47163
47164             this.footerPanel = new E(cs[4]);
47165             this.footerPanel.enableDisplayMode("block");
47166
47167         this.focusEl = new E(cs[5]);
47168         this.focusEl.swallowEvent("click", true);
47169         this.resizeProxy = new E(cs[6]);
47170
47171             this.headerSelector = String.format(
47172                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47173                this.lockedHd.id, this.mainHd.id
47174             );
47175
47176             this.splitterSelector = String.format(
47177                '#{0} div.x-grid-split, #{1} div.x-grid-split',
47178                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47179             );
47180     },
47181     idToCssName : function(s)
47182     {
47183         return s.replace(/[^a-z0-9]+/ig, '-');
47184     },
47185
47186         getHeaderCell : function(index){
47187             return Roo.DomQuery.select(this.headerSelector)[index];
47188         },
47189
47190         getHeaderCellMeasure : function(index){
47191             return this.getHeaderCell(index).firstChild;
47192         },
47193
47194         getHeaderCellText : function(index){
47195             return this.getHeaderCell(index).firstChild.firstChild;
47196         },
47197
47198         getLockedTable : function(){
47199             return this.lockedBody.dom.firstChild;
47200         },
47201
47202         getBodyTable : function(){
47203             return this.mainBody.dom.firstChild;
47204         },
47205
47206         getLockedRow : function(index){
47207             return this.getLockedTable().rows[index];
47208         },
47209
47210         getRow : function(index){
47211             return this.getBodyTable().rows[index];
47212         },
47213
47214         getRowComposite : function(index){
47215             if(!this.rowEl){
47216                 this.rowEl = new Roo.CompositeElementLite();
47217             }
47218         var els = [], lrow, mrow;
47219         if(lrow = this.getLockedRow(index)){
47220             els.push(lrow);
47221         }
47222         if(mrow = this.getRow(index)){
47223             els.push(mrow);
47224         }
47225         this.rowEl.elements = els;
47226             return this.rowEl;
47227         },
47228
47229         getCell : function(rowIndex, colIndex){
47230             var locked = this.cm.getLockedCount();
47231             var source;
47232             if(colIndex < locked){
47233                 source = this.lockedBody.dom.firstChild;
47234             }else{
47235                 source = this.mainBody.dom.firstChild;
47236                 colIndex -= locked;
47237             }
47238         return source.rows[rowIndex].childNodes[colIndex];
47239         },
47240
47241         getCellText : function(rowIndex, colIndex){
47242             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47243         },
47244
47245         getCellBox : function(cell){
47246             var b = this.fly(cell).getBox();
47247         if(Roo.isOpera){ // opera fails to report the Y
47248             b.y = cell.offsetTop + this.mainBody.getY();
47249         }
47250         return b;
47251     },
47252
47253     getCellIndex : function(cell){
47254         var id = String(cell.className).match(this.cellRE);
47255         if(id){
47256             return parseInt(id[1], 10);
47257         }
47258         return 0;
47259     },
47260
47261     findHeaderIndex : function(n){
47262         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47263         return r ? this.getCellIndex(r) : false;
47264     },
47265
47266     findHeaderCell : function(n){
47267         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47268         return r ? r : false;
47269     },
47270
47271     findRowIndex : function(n){
47272         if(!n){
47273             return false;
47274         }
47275         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47276         return r ? r.rowIndex : false;
47277     },
47278
47279     findCellIndex : function(node){
47280         var stop = this.el.dom;
47281         while(node && node != stop){
47282             if(this.findRE.test(node.className)){
47283                 return this.getCellIndex(node);
47284             }
47285             node = node.parentNode;
47286         }
47287         return false;
47288     },
47289
47290     getColumnId : function(index){
47291             return this.cm.getColumnId(index);
47292         },
47293
47294         getSplitters : function(){
47295             if(this.splitterSelector){
47296                return Roo.DomQuery.select(this.splitterSelector);
47297             }else{
47298                 return null;
47299             }
47300         },
47301
47302         getSplitter : function(index){
47303             return this.getSplitters()[index];
47304         },
47305
47306     onRowOver : function(e, t){
47307         var row;
47308         if((row = this.findRowIndex(t)) !== false){
47309             this.getRowComposite(row).addClass("x-grid-row-over");
47310         }
47311     },
47312
47313     onRowOut : function(e, t){
47314         var row;
47315         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47316             this.getRowComposite(row).removeClass("x-grid-row-over");
47317         }
47318     },
47319
47320     renderHeaders : function(){
47321             var cm = this.cm;
47322         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47323         var cb = [], lb = [], sb = [], lsb = [], p = {};
47324         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47325             p.cellId = "x-grid-hd-0-" + i;
47326             p.splitId = "x-grid-csplit-0-" + i;
47327             p.id = cm.getColumnId(i);
47328             p.title = cm.getColumnTooltip(i) || "";
47329             p.value = cm.getColumnHeader(i) || "";
47330             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47331             if(!cm.isLocked(i)){
47332                 cb[cb.length] = ct.apply(p);
47333                 sb[sb.length] = st.apply(p);
47334             }else{
47335                 lb[lb.length] = ct.apply(p);
47336                 lsb[lsb.length] = st.apply(p);
47337             }
47338         }
47339         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47340                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47341         },
47342
47343         updateHeaders : function(){
47344         var html = this.renderHeaders();
47345         this.lockedHd.update(html[0]);
47346         this.mainHd.update(html[1]);
47347     },
47348
47349     /**
47350      * Focuses the specified row.
47351      * @param {Number} row The row index
47352      */
47353     focusRow : function(row){
47354         var x = this.scroller.dom.scrollLeft;
47355         this.focusCell(row, 0, false);
47356         this.scroller.dom.scrollLeft = x;
47357     },
47358
47359     /**
47360      * Focuses the specified cell.
47361      * @param {Number} row The row index
47362      * @param {Number} col The column index
47363      * @param {Boolean} hscroll false to disable horizontal scrolling
47364      */
47365     focusCell : function(row, col, hscroll){
47366         var el = this.ensureVisible(row, col, hscroll);
47367         this.focusEl.alignTo(el, "tl-tl");
47368         if(Roo.isGecko){
47369             this.focusEl.focus();
47370         }else{
47371             this.focusEl.focus.defer(1, this.focusEl);
47372         }
47373     },
47374
47375     /**
47376      * Scrolls the specified cell into view
47377      * @param {Number} row The row index
47378      * @param {Number} col The column index
47379      * @param {Boolean} hscroll false to disable horizontal scrolling
47380      */
47381     ensureVisible : function(row, col, hscroll){
47382         if(typeof row != "number"){
47383             row = row.rowIndex;
47384         }
47385         if(row < 0 && row >= this.ds.getCount()){
47386             return;
47387         }
47388         col = (col !== undefined ? col : 0);
47389         var cm = this.grid.colModel;
47390         while(cm.isHidden(col)){
47391             col++;
47392         }
47393
47394         var el = this.getCell(row, col);
47395         if(!el){
47396             return;
47397         }
47398         var c = this.scroller.dom;
47399
47400         var ctop = parseInt(el.offsetTop, 10);
47401         var cleft = parseInt(el.offsetLeft, 10);
47402         var cbot = ctop + el.offsetHeight;
47403         var cright = cleft + el.offsetWidth;
47404
47405         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47406         var stop = parseInt(c.scrollTop, 10);
47407         var sleft = parseInt(c.scrollLeft, 10);
47408         var sbot = stop + ch;
47409         var sright = sleft + c.clientWidth;
47410
47411         if(ctop < stop){
47412                 c.scrollTop = ctop;
47413         }else if(cbot > sbot){
47414             c.scrollTop = cbot-ch;
47415         }
47416
47417         if(hscroll !== false){
47418             if(cleft < sleft){
47419                 c.scrollLeft = cleft;
47420             }else if(cright > sright){
47421                 c.scrollLeft = cright-c.clientWidth;
47422             }
47423         }
47424         return el;
47425     },
47426
47427     updateColumns : function(){
47428         this.grid.stopEditing();
47429         var cm = this.grid.colModel, colIds = this.getColumnIds();
47430         //var totalWidth = cm.getTotalWidth();
47431         var pos = 0;
47432         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47433             //if(cm.isHidden(i)) continue;
47434             var w = cm.getColumnWidth(i);
47435             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47436             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47437         }
47438         this.updateSplitters();
47439     },
47440
47441     generateRules : function(cm){
47442         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47443         Roo.util.CSS.removeStyleSheet(rulesId);
47444         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47445             var cid = cm.getColumnId(i);
47446             var align = '';
47447             if(cm.config[i].align){
47448                 align = 'text-align:'+cm.config[i].align+';';
47449             }
47450             var hidden = '';
47451             if(cm.isHidden(i)){
47452                 hidden = 'display:none;';
47453             }
47454             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47455             ruleBuf.push(
47456                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47457                     this.hdSelector, cid, " {\n", align, width, "}\n",
47458                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47459                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47460         }
47461         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47462     },
47463
47464     updateSplitters : function(){
47465         var cm = this.cm, s = this.getSplitters();
47466         if(s){ // splitters not created yet
47467             var pos = 0, locked = true;
47468             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47469                 if(cm.isHidden(i)) continue;
47470                 var w = cm.getColumnWidth(i);
47471                 if(!cm.isLocked(i) && locked){
47472                     pos = 0;
47473                     locked = false;
47474                 }
47475                 pos += w;
47476                 s[i].style.left = (pos-this.splitOffset) + "px";
47477             }
47478         }
47479     },
47480
47481     handleHiddenChange : function(colModel, colIndex, hidden){
47482         if(hidden){
47483             this.hideColumn(colIndex);
47484         }else{
47485             this.unhideColumn(colIndex);
47486         }
47487     },
47488
47489     hideColumn : function(colIndex){
47490         var cid = this.getColumnId(colIndex);
47491         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47492         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47493         if(Roo.isSafari){
47494             this.updateHeaders();
47495         }
47496         this.updateSplitters();
47497         this.layout();
47498     },
47499
47500     unhideColumn : function(colIndex){
47501         var cid = this.getColumnId(colIndex);
47502         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47503         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47504
47505         if(Roo.isSafari){
47506             this.updateHeaders();
47507         }
47508         this.updateSplitters();
47509         this.layout();
47510     },
47511
47512     insertRows : function(dm, firstRow, lastRow, isUpdate){
47513         if(firstRow == 0 && lastRow == dm.getCount()-1){
47514             this.refresh();
47515         }else{
47516             if(!isUpdate){
47517                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47518             }
47519             var s = this.getScrollState();
47520             var markup = this.renderRows(firstRow, lastRow);
47521             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47522             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47523             this.restoreScroll(s);
47524             if(!isUpdate){
47525                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47526                 this.syncRowHeights(firstRow, lastRow);
47527                 this.stripeRows(firstRow);
47528                 this.layout();
47529             }
47530         }
47531     },
47532
47533     bufferRows : function(markup, target, index){
47534         var before = null, trows = target.rows, tbody = target.tBodies[0];
47535         if(index < trows.length){
47536             before = trows[index];
47537         }
47538         var b = document.createElement("div");
47539         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47540         var rows = b.firstChild.rows;
47541         for(var i = 0, len = rows.length; i < len; i++){
47542             if(before){
47543                 tbody.insertBefore(rows[0], before);
47544             }else{
47545                 tbody.appendChild(rows[0]);
47546             }
47547         }
47548         b.innerHTML = "";
47549         b = null;
47550     },
47551
47552     deleteRows : function(dm, firstRow, lastRow){
47553         if(dm.getRowCount()<1){
47554             this.fireEvent("beforerefresh", this);
47555             this.mainBody.update("");
47556             this.lockedBody.update("");
47557             this.fireEvent("refresh", this);
47558         }else{
47559             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47560             var bt = this.getBodyTable();
47561             var tbody = bt.firstChild;
47562             var rows = bt.rows;
47563             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47564                 tbody.removeChild(rows[firstRow]);
47565             }
47566             this.stripeRows(firstRow);
47567             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47568         }
47569     },
47570
47571     updateRows : function(dataSource, firstRow, lastRow){
47572         var s = this.getScrollState();
47573         this.refresh();
47574         this.restoreScroll(s);
47575     },
47576
47577     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47578         if(!noRefresh){
47579            this.refresh();
47580         }
47581         this.updateHeaderSortState();
47582     },
47583
47584     getScrollState : function(){
47585         var sb = this.scroller.dom;
47586         return {left: sb.scrollLeft, top: sb.scrollTop};
47587     },
47588
47589     stripeRows : function(startRow){
47590         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47591             return;
47592         }
47593         startRow = startRow || 0;
47594         var rows = this.getBodyTable().rows;
47595         var lrows = this.getLockedTable().rows;
47596         var cls = ' x-grid-row-alt ';
47597         for(var i = startRow, len = rows.length; i < len; i++){
47598             var row = rows[i], lrow = lrows[i];
47599             var isAlt = ((i+1) % 2 == 0);
47600             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47601             if(isAlt == hasAlt){
47602                 continue;
47603             }
47604             if(isAlt){
47605                 row.className += " x-grid-row-alt";
47606             }else{
47607                 row.className = row.className.replace("x-grid-row-alt", "");
47608             }
47609             if(lrow){
47610                 lrow.className = row.className;
47611             }
47612         }
47613     },
47614
47615     restoreScroll : function(state){
47616         var sb = this.scroller.dom;
47617         sb.scrollLeft = state.left;
47618         sb.scrollTop = state.top;
47619         this.syncScroll();
47620     },
47621
47622     syncScroll : function(){
47623         var sb = this.scroller.dom;
47624         var sh = this.mainHd.dom;
47625         var bs = this.mainBody.dom;
47626         var lv = this.lockedBody.dom;
47627         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47628         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47629     },
47630
47631     handleScroll : function(e){
47632         this.syncScroll();
47633         var sb = this.scroller.dom;
47634         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47635         e.stopEvent();
47636     },
47637
47638     handleWheel : function(e){
47639         var d = e.getWheelDelta();
47640         this.scroller.dom.scrollTop -= d*22;
47641         // set this here to prevent jumpy scrolling on large tables
47642         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47643         e.stopEvent();
47644     },
47645
47646     renderRows : function(startRow, endRow){
47647         // pull in all the crap needed to render rows
47648         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47649         var colCount = cm.getColumnCount();
47650
47651         if(ds.getCount() < 1){
47652             return ["", ""];
47653         }
47654
47655         // build a map for all the columns
47656         var cs = [];
47657         for(var i = 0; i < colCount; i++){
47658             var name = cm.getDataIndex(i);
47659             cs[i] = {
47660                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47661                 renderer : cm.getRenderer(i),
47662                 id : cm.getColumnId(i),
47663                 locked : cm.isLocked(i)
47664             };
47665         }
47666
47667         startRow = startRow || 0;
47668         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47669
47670         // records to render
47671         var rs = ds.getRange(startRow, endRow);
47672
47673         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47674     },
47675
47676     // As much as I hate to duplicate code, this was branched because FireFox really hates
47677     // [].join("") on strings. The performance difference was substantial enough to
47678     // branch this function
47679     doRender : Roo.isGecko ?
47680             function(cs, rs, ds, startRow, colCount, stripe){
47681                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47682                 // buffers
47683                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47684                 for(var j = 0, len = rs.length; j < len; j++){
47685                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47686                     for(var i = 0; i < colCount; i++){
47687                         c = cs[i];
47688                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47689                         p.id = c.id;
47690                         p.css = p.attr = "";
47691                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47692                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47693                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47694                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47695                         }
47696                         var markup = ct.apply(p);
47697                         if(!c.locked){
47698                             cb+= markup;
47699                         }else{
47700                             lcb+= markup;
47701                         }
47702                     }
47703                     var alt = [];
47704                     if(stripe && ((rowIndex+1) % 2 == 0)){
47705                         alt[0] = "x-grid-row-alt";
47706                     }
47707                     if(r.dirty){
47708                         alt[1] = " x-grid-dirty-row";
47709                     }
47710                     rp.cells = lcb;
47711                     if(this.getRowClass){
47712                         alt[2] = this.getRowClass(r, rowIndex);
47713                     }
47714                     rp.alt = alt.join(" ");
47715                     lbuf+= rt.apply(rp);
47716                     rp.cells = cb;
47717                     buf+=  rt.apply(rp);
47718                 }
47719                 return [lbuf, buf];
47720             } :
47721             function(cs, rs, ds, startRow, colCount, stripe){
47722                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47723                 // buffers
47724                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47725                 for(var j = 0, len = rs.length; j < len; j++){
47726                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47727                     for(var i = 0; i < colCount; i++){
47728                         c = cs[i];
47729                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47730                         p.id = c.id;
47731                         p.css = p.attr = "";
47732                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47733                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47734                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47735                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47736                         }
47737                         var markup = ct.apply(p);
47738                         if(!c.locked){
47739                             cb[cb.length] = markup;
47740                         }else{
47741                             lcb[lcb.length] = markup;
47742                         }
47743                     }
47744                     var alt = [];
47745                     if(stripe && ((rowIndex+1) % 2 == 0)){
47746                         alt[0] = "x-grid-row-alt";
47747                     }
47748                     if(r.dirty){
47749                         alt[1] = " x-grid-dirty-row";
47750                     }
47751                     rp.cells = lcb;
47752                     if(this.getRowClass){
47753                         alt[2] = this.getRowClass(r, rowIndex);
47754                     }
47755                     rp.alt = alt.join(" ");
47756                     rp.cells = lcb.join("");
47757                     lbuf[lbuf.length] = rt.apply(rp);
47758                     rp.cells = cb.join("");
47759                     buf[buf.length] =  rt.apply(rp);
47760                 }
47761                 return [lbuf.join(""), buf.join("")];
47762             },
47763
47764     renderBody : function(){
47765         var markup = this.renderRows();
47766         var bt = this.templates.body;
47767         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47768     },
47769
47770     /**
47771      * Refreshes the grid
47772      * @param {Boolean} headersToo
47773      */
47774     refresh : function(headersToo){
47775         this.fireEvent("beforerefresh", this);
47776         this.grid.stopEditing();
47777         var result = this.renderBody();
47778         this.lockedBody.update(result[0]);
47779         this.mainBody.update(result[1]);
47780         if(headersToo === true){
47781             this.updateHeaders();
47782             this.updateColumns();
47783             this.updateSplitters();
47784             this.updateHeaderSortState();
47785         }
47786         this.syncRowHeights();
47787         this.layout();
47788         this.fireEvent("refresh", this);
47789     },
47790
47791     handleColumnMove : function(cm, oldIndex, newIndex){
47792         this.indexMap = null;
47793         var s = this.getScrollState();
47794         this.refresh(true);
47795         this.restoreScroll(s);
47796         this.afterMove(newIndex);
47797     },
47798
47799     afterMove : function(colIndex){
47800         if(this.enableMoveAnim && Roo.enableFx){
47801             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47802         }
47803     },
47804
47805     updateCell : function(dm, rowIndex, dataIndex){
47806         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47807         if(typeof colIndex == "undefined"){ // not present in grid
47808             return;
47809         }
47810         var cm = this.grid.colModel;
47811         var cell = this.getCell(rowIndex, colIndex);
47812         var cellText = this.getCellText(rowIndex, colIndex);
47813
47814         var p = {
47815             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47816             id : cm.getColumnId(colIndex),
47817             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47818         };
47819         var renderer = cm.getRenderer(colIndex);
47820         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47821         if(typeof val == "undefined" || val === "") val = "&#160;";
47822         cellText.innerHTML = val;
47823         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47824         this.syncRowHeights(rowIndex, rowIndex);
47825     },
47826
47827     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47828         var maxWidth = 0;
47829         if(this.grid.autoSizeHeaders){
47830             var h = this.getHeaderCellMeasure(colIndex);
47831             maxWidth = Math.max(maxWidth, h.scrollWidth);
47832         }
47833         var tb, index;
47834         if(this.cm.isLocked(colIndex)){
47835             tb = this.getLockedTable();
47836             index = colIndex;
47837         }else{
47838             tb = this.getBodyTable();
47839             index = colIndex - this.cm.getLockedCount();
47840         }
47841         if(tb && tb.rows){
47842             var rows = tb.rows;
47843             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47844             for(var i = 0; i < stopIndex; i++){
47845                 var cell = rows[i].childNodes[index].firstChild;
47846                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47847             }
47848         }
47849         return maxWidth + /*margin for error in IE*/ 5;
47850     },
47851     /**
47852      * Autofit a column to its content.
47853      * @param {Number} colIndex
47854      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47855      */
47856      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47857          if(this.cm.isHidden(colIndex)){
47858              return; // can't calc a hidden column
47859          }
47860         if(forceMinSize){
47861             var cid = this.cm.getColumnId(colIndex);
47862             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47863            if(this.grid.autoSizeHeaders){
47864                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47865            }
47866         }
47867         var newWidth = this.calcColumnWidth(colIndex);
47868         this.cm.setColumnWidth(colIndex,
47869             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47870         if(!suppressEvent){
47871             this.grid.fireEvent("columnresize", colIndex, newWidth);
47872         }
47873     },
47874
47875     /**
47876      * Autofits all columns to their content and then expands to fit any extra space in the grid
47877      */
47878      autoSizeColumns : function(){
47879         var cm = this.grid.colModel;
47880         var colCount = cm.getColumnCount();
47881         for(var i = 0; i < colCount; i++){
47882             this.autoSizeColumn(i, true, true);
47883         }
47884         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47885             this.fitColumns();
47886         }else{
47887             this.updateColumns();
47888             this.layout();
47889         }
47890     },
47891
47892     /**
47893      * Autofits all columns to the grid's width proportionate with their current size
47894      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47895      */
47896     fitColumns : function(reserveScrollSpace){
47897         var cm = this.grid.colModel;
47898         var colCount = cm.getColumnCount();
47899         var cols = [];
47900         var width = 0;
47901         var i, w;
47902         for (i = 0; i < colCount; i++){
47903             if(!cm.isHidden(i) && !cm.isFixed(i)){
47904                 w = cm.getColumnWidth(i);
47905                 cols.push(i);
47906                 cols.push(w);
47907                 width += w;
47908             }
47909         }
47910         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47911         if(reserveScrollSpace){
47912             avail -= 17;
47913         }
47914         var frac = (avail - cm.getTotalWidth())/width;
47915         while (cols.length){
47916             w = cols.pop();
47917             i = cols.pop();
47918             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47919         }
47920         this.updateColumns();
47921         this.layout();
47922     },
47923
47924     onRowSelect : function(rowIndex){
47925         var row = this.getRowComposite(rowIndex);
47926         row.addClass("x-grid-row-selected");
47927     },
47928
47929     onRowDeselect : function(rowIndex){
47930         var row = this.getRowComposite(rowIndex);
47931         row.removeClass("x-grid-row-selected");
47932     },
47933
47934     onCellSelect : function(row, col){
47935         var cell = this.getCell(row, col);
47936         if(cell){
47937             Roo.fly(cell).addClass("x-grid-cell-selected");
47938         }
47939     },
47940
47941     onCellDeselect : function(row, col){
47942         var cell = this.getCell(row, col);
47943         if(cell){
47944             Roo.fly(cell).removeClass("x-grid-cell-selected");
47945         }
47946     },
47947
47948     updateHeaderSortState : function(){
47949         var state = this.ds.getSortState();
47950         if(!state){
47951             return;
47952         }
47953         this.sortState = state;
47954         var sortColumn = this.cm.findColumnIndex(state.field);
47955         if(sortColumn != -1){
47956             var sortDir = state.direction;
47957             var sc = this.sortClasses;
47958             var hds = this.el.select(this.headerSelector).removeClass(sc);
47959             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47960         }
47961     },
47962
47963     handleHeaderClick : function(g, index){
47964         if(this.headersDisabled){
47965             return;
47966         }
47967         var dm = g.dataSource, cm = g.colModel;
47968             if(!cm.isSortable(index)){
47969             return;
47970         }
47971             g.stopEditing();
47972         dm.sort(cm.getDataIndex(index));
47973     },
47974
47975
47976     destroy : function(){
47977         if(this.colMenu){
47978             this.colMenu.removeAll();
47979             Roo.menu.MenuMgr.unregister(this.colMenu);
47980             this.colMenu.getEl().remove();
47981             delete this.colMenu;
47982         }
47983         if(this.hmenu){
47984             this.hmenu.removeAll();
47985             Roo.menu.MenuMgr.unregister(this.hmenu);
47986             this.hmenu.getEl().remove();
47987             delete this.hmenu;
47988         }
47989         if(this.grid.enableColumnMove){
47990             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47991             if(dds){
47992                 for(var dd in dds){
47993                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47994                         var elid = dds[dd].dragElId;
47995                         dds[dd].unreg();
47996                         Roo.get(elid).remove();
47997                     } else if(dds[dd].config.isTarget){
47998                         dds[dd].proxyTop.remove();
47999                         dds[dd].proxyBottom.remove();
48000                         dds[dd].unreg();
48001                     }
48002                     if(Roo.dd.DDM.locationCache[dd]){
48003                         delete Roo.dd.DDM.locationCache[dd];
48004                     }
48005                 }
48006                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48007             }
48008         }
48009         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48010         this.bind(null, null);
48011         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48012     },
48013
48014     handleLockChange : function(){
48015         this.refresh(true);
48016     },
48017
48018     onDenyColumnLock : function(){
48019
48020     },
48021
48022     onDenyColumnHide : function(){
48023
48024     },
48025
48026     handleHdMenuClick : function(item){
48027         var index = this.hdCtxIndex;
48028         var cm = this.cm, ds = this.ds;
48029         switch(item.id){
48030             case "asc":
48031                 ds.sort(cm.getDataIndex(index), "ASC");
48032                 break;
48033             case "desc":
48034                 ds.sort(cm.getDataIndex(index), "DESC");
48035                 break;
48036             case "lock":
48037                 var lc = cm.getLockedCount();
48038                 if(cm.getColumnCount(true) <= lc+1){
48039                     this.onDenyColumnLock();
48040                     return;
48041                 }
48042                 if(lc != index){
48043                     cm.setLocked(index, true, true);
48044                     cm.moveColumn(index, lc);
48045                     this.grid.fireEvent("columnmove", index, lc);
48046                 }else{
48047                     cm.setLocked(index, true);
48048                 }
48049             break;
48050             case "unlock":
48051                 var lc = cm.getLockedCount();
48052                 if((lc-1) != index){
48053                     cm.setLocked(index, false, true);
48054                     cm.moveColumn(index, lc-1);
48055                     this.grid.fireEvent("columnmove", index, lc-1);
48056                 }else{
48057                     cm.setLocked(index, false);
48058                 }
48059             break;
48060             default:
48061                 index = cm.getIndexById(item.id.substr(4));
48062                 if(index != -1){
48063                     if(item.checked && cm.getColumnCount(true) <= 1){
48064                         this.onDenyColumnHide();
48065                         return false;
48066                     }
48067                     cm.setHidden(index, item.checked);
48068                 }
48069         }
48070         return true;
48071     },
48072
48073     beforeColMenuShow : function(){
48074         var cm = this.cm,  colCount = cm.getColumnCount();
48075         this.colMenu.removeAll();
48076         for(var i = 0; i < colCount; i++){
48077             this.colMenu.add(new Roo.menu.CheckItem({
48078                 id: "col-"+cm.getColumnId(i),
48079                 text: cm.getColumnHeader(i),
48080                 checked: !cm.isHidden(i),
48081                 hideOnClick:false
48082             }));
48083         }
48084     },
48085
48086     handleHdCtx : function(g, index, e){
48087         e.stopEvent();
48088         var hd = this.getHeaderCell(index);
48089         this.hdCtxIndex = index;
48090         var ms = this.hmenu.items, cm = this.cm;
48091         ms.get("asc").setDisabled(!cm.isSortable(index));
48092         ms.get("desc").setDisabled(!cm.isSortable(index));
48093         if(this.grid.enableColLock !== false){
48094             ms.get("lock").setDisabled(cm.isLocked(index));
48095             ms.get("unlock").setDisabled(!cm.isLocked(index));
48096         }
48097         this.hmenu.show(hd, "tl-bl");
48098     },
48099
48100     handleHdOver : function(e){
48101         var hd = this.findHeaderCell(e.getTarget());
48102         if(hd && !this.headersDisabled){
48103             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48104                this.fly(hd).addClass("x-grid-hd-over");
48105             }
48106         }
48107     },
48108
48109     handleHdOut : function(e){
48110         var hd = this.findHeaderCell(e.getTarget());
48111         if(hd){
48112             this.fly(hd).removeClass("x-grid-hd-over");
48113         }
48114     },
48115
48116     handleSplitDblClick : function(e, t){
48117         var i = this.getCellIndex(t);
48118         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48119             this.autoSizeColumn(i, true);
48120             this.layout();
48121         }
48122     },
48123
48124     render : function(){
48125
48126         var cm = this.cm;
48127         var colCount = cm.getColumnCount();
48128
48129         if(this.grid.monitorWindowResize === true){
48130             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48131         }
48132         var header = this.renderHeaders();
48133         var body = this.templates.body.apply({rows:""});
48134         var html = this.templates.master.apply({
48135             lockedBody: body,
48136             body: body,
48137             lockedHeader: header[0],
48138             header: header[1]
48139         });
48140
48141         //this.updateColumns();
48142
48143         this.grid.getGridEl().dom.innerHTML = html;
48144
48145         this.initElements();
48146         
48147         // a kludge to fix the random scolling effect in webkit
48148         this.el.on("scroll", function() {
48149             this.el.dom.scrollTop=0; // hopefully not recursive..
48150         },this);
48151
48152         this.scroller.on("scroll", this.handleScroll, this);
48153         this.lockedBody.on("mousewheel", this.handleWheel, this);
48154         this.mainBody.on("mousewheel", this.handleWheel, this);
48155
48156         this.mainHd.on("mouseover", this.handleHdOver, this);
48157         this.mainHd.on("mouseout", this.handleHdOut, this);
48158         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48159                 {delegate: "."+this.splitClass});
48160
48161         this.lockedHd.on("mouseover", this.handleHdOver, this);
48162         this.lockedHd.on("mouseout", this.handleHdOut, this);
48163         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48164                 {delegate: "."+this.splitClass});
48165
48166         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48167             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48168         }
48169
48170         this.updateSplitters();
48171
48172         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48173             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48174             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48175         }
48176
48177         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48178             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48179             this.hmenu.add(
48180                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48181                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48182             );
48183             if(this.grid.enableColLock !== false){
48184                 this.hmenu.add('-',
48185                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48186                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48187                 );
48188             }
48189             if(this.grid.enableColumnHide !== false){
48190
48191                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48192                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48193                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48194
48195                 this.hmenu.add('-',
48196                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48197                 );
48198             }
48199             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48200
48201             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48202         }
48203
48204         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48205             this.dd = new Roo.grid.GridDragZone(this.grid, {
48206                 ddGroup : this.grid.ddGroup || 'GridDD'
48207             });
48208         }
48209
48210         /*
48211         for(var i = 0; i < colCount; i++){
48212             if(cm.isHidden(i)){
48213                 this.hideColumn(i);
48214             }
48215             if(cm.config[i].align){
48216                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48217                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48218             }
48219         }*/
48220         
48221         this.updateHeaderSortState();
48222
48223         this.beforeInitialResize();
48224         this.layout(true);
48225
48226         // two part rendering gives faster view to the user
48227         this.renderPhase2.defer(1, this);
48228     },
48229
48230     renderPhase2 : function(){
48231         // render the rows now
48232         this.refresh();
48233         if(this.grid.autoSizeColumns){
48234             this.autoSizeColumns();
48235         }
48236     },
48237
48238     beforeInitialResize : function(){
48239
48240     },
48241
48242     onColumnSplitterMoved : function(i, w){
48243         this.userResized = true;
48244         var cm = this.grid.colModel;
48245         cm.setColumnWidth(i, w, true);
48246         var cid = cm.getColumnId(i);
48247         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48248         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48249         this.updateSplitters();
48250         this.layout();
48251         this.grid.fireEvent("columnresize", i, w);
48252     },
48253
48254     syncRowHeights : function(startIndex, endIndex){
48255         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48256             startIndex = startIndex || 0;
48257             var mrows = this.getBodyTable().rows;
48258             var lrows = this.getLockedTable().rows;
48259             var len = mrows.length-1;
48260             endIndex = Math.min(endIndex || len, len);
48261             for(var i = startIndex; i <= endIndex; i++){
48262                 var m = mrows[i], l = lrows[i];
48263                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48264                 m.style.height = l.style.height = h + "px";
48265             }
48266         }
48267     },
48268
48269     layout : function(initialRender, is2ndPass){
48270         var g = this.grid;
48271         var auto = g.autoHeight;
48272         var scrollOffset = 16;
48273         var c = g.getGridEl(), cm = this.cm,
48274                 expandCol = g.autoExpandColumn,
48275                 gv = this;
48276         //c.beginMeasure();
48277
48278         if(!c.dom.offsetWidth){ // display:none?
48279             if(initialRender){
48280                 this.lockedWrap.show();
48281                 this.mainWrap.show();
48282             }
48283             return;
48284         }
48285
48286         var hasLock = this.cm.isLocked(0);
48287
48288         var tbh = this.headerPanel.getHeight();
48289         var bbh = this.footerPanel.getHeight();
48290
48291         if(auto){
48292             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48293             var newHeight = ch + c.getBorderWidth("tb");
48294             if(g.maxHeight){
48295                 newHeight = Math.min(g.maxHeight, newHeight);
48296             }
48297             c.setHeight(newHeight);
48298         }
48299
48300         if(g.autoWidth){
48301             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48302         }
48303
48304         var s = this.scroller;
48305
48306         var csize = c.getSize(true);
48307
48308         this.el.setSize(csize.width, csize.height);
48309
48310         this.headerPanel.setWidth(csize.width);
48311         this.footerPanel.setWidth(csize.width);
48312
48313         var hdHeight = this.mainHd.getHeight();
48314         var vw = csize.width;
48315         var vh = csize.height - (tbh + bbh);
48316
48317         s.setSize(vw, vh);
48318
48319         var bt = this.getBodyTable();
48320         var ltWidth = hasLock ?
48321                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48322
48323         var scrollHeight = bt.offsetHeight;
48324         var scrollWidth = ltWidth + bt.offsetWidth;
48325         var vscroll = false, hscroll = false;
48326
48327         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48328
48329         var lw = this.lockedWrap, mw = this.mainWrap;
48330         var lb = this.lockedBody, mb = this.mainBody;
48331
48332         setTimeout(function(){
48333             var t = s.dom.offsetTop;
48334             var w = s.dom.clientWidth,
48335                 h = s.dom.clientHeight;
48336
48337             lw.setTop(t);
48338             lw.setSize(ltWidth, h);
48339
48340             mw.setLeftTop(ltWidth, t);
48341             mw.setSize(w-ltWidth, h);
48342
48343             lb.setHeight(h-hdHeight);
48344             mb.setHeight(h-hdHeight);
48345
48346             if(is2ndPass !== true && !gv.userResized && expandCol){
48347                 // high speed resize without full column calculation
48348                 
48349                 var ci = cm.getIndexById(expandCol);
48350                 if (ci < 0) {
48351                     ci = cm.findColumnIndex(expandCol);
48352                 }
48353                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48354                 var expandId = cm.getColumnId(ci);
48355                 var  tw = cm.getTotalWidth(false);
48356                 var currentWidth = cm.getColumnWidth(ci);
48357                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48358                 if(currentWidth != cw){
48359                     cm.setColumnWidth(ci, cw, true);
48360                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48361                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48362                     gv.updateSplitters();
48363                     gv.layout(false, true);
48364                 }
48365             }
48366
48367             if(initialRender){
48368                 lw.show();
48369                 mw.show();
48370             }
48371             //c.endMeasure();
48372         }, 10);
48373     },
48374
48375     onWindowResize : function(){
48376         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48377             return;
48378         }
48379         this.layout();
48380     },
48381
48382     appendFooter : function(parentEl){
48383         return null;
48384     },
48385
48386     sortAscText : "Sort Ascending",
48387     sortDescText : "Sort Descending",
48388     lockText : "Lock Column",
48389     unlockText : "Unlock Column",
48390     columnsText : "Columns"
48391 });
48392
48393
48394 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48395     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48396     this.proxy.el.addClass('x-grid3-col-dd');
48397 };
48398
48399 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48400     handleMouseDown : function(e){
48401
48402     },
48403
48404     callHandleMouseDown : function(e){
48405         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48406     }
48407 });
48408 /*
48409  * Based on:
48410  * Ext JS Library 1.1.1
48411  * Copyright(c) 2006-2007, Ext JS, LLC.
48412  *
48413  * Originally Released Under LGPL - original licence link has changed is not relivant.
48414  *
48415  * Fork - LGPL
48416  * <script type="text/javascript">
48417  */
48418  
48419 // private
48420 // This is a support class used internally by the Grid components
48421 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48422     this.grid = grid;
48423     this.view = grid.getView();
48424     this.proxy = this.view.resizeProxy;
48425     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48426         "gridSplitters" + this.grid.getGridEl().id, {
48427         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48428     });
48429     this.setHandleElId(Roo.id(hd));
48430     this.setOuterHandleElId(Roo.id(hd2));
48431     this.scroll = false;
48432 };
48433 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48434     fly: Roo.Element.fly,
48435
48436     b4StartDrag : function(x, y){
48437         this.view.headersDisabled = true;
48438         this.proxy.setHeight(this.view.mainWrap.getHeight());
48439         var w = this.cm.getColumnWidth(this.cellIndex);
48440         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48441         this.resetConstraints();
48442         this.setXConstraint(minw, 1000);
48443         this.setYConstraint(0, 0);
48444         this.minX = x - minw;
48445         this.maxX = x + 1000;
48446         this.startPos = x;
48447         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48448     },
48449
48450
48451     handleMouseDown : function(e){
48452         ev = Roo.EventObject.setEvent(e);
48453         var t = this.fly(ev.getTarget());
48454         if(t.hasClass("x-grid-split")){
48455             this.cellIndex = this.view.getCellIndex(t.dom);
48456             this.split = t.dom;
48457             this.cm = this.grid.colModel;
48458             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48459                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48460             }
48461         }
48462     },
48463
48464     endDrag : function(e){
48465         this.view.headersDisabled = false;
48466         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48467         var diff = endX - this.startPos;
48468         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48469     },
48470
48471     autoOffset : function(){
48472         this.setDelta(0,0);
48473     }
48474 });/*
48475  * Based on:
48476  * Ext JS Library 1.1.1
48477  * Copyright(c) 2006-2007, Ext JS, LLC.
48478  *
48479  * Originally Released Under LGPL - original licence link has changed is not relivant.
48480  *
48481  * Fork - LGPL
48482  * <script type="text/javascript">
48483  */
48484  
48485 // private
48486 // This is a support class used internally by the Grid components
48487 Roo.grid.GridDragZone = function(grid, config){
48488     this.view = grid.getView();
48489     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48490     if(this.view.lockedBody){
48491         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48492         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48493     }
48494     this.scroll = false;
48495     this.grid = grid;
48496     this.ddel = document.createElement('div');
48497     this.ddel.className = 'x-grid-dd-wrap';
48498 };
48499
48500 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48501     ddGroup : "GridDD",
48502
48503     getDragData : function(e){
48504         var t = Roo.lib.Event.getTarget(e);
48505         var rowIndex = this.view.findRowIndex(t);
48506         if(rowIndex !== false){
48507             var sm = this.grid.selModel;
48508             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48509               //  sm.mouseDown(e, t);
48510             //}
48511             if (e.hasModifier()){
48512                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48513             }
48514             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48515         }
48516         return false;
48517     },
48518
48519     onInitDrag : function(e){
48520         var data = this.dragData;
48521         this.ddel.innerHTML = this.grid.getDragDropText();
48522         this.proxy.update(this.ddel);
48523         // fire start drag?
48524     },
48525
48526     afterRepair : function(){
48527         this.dragging = false;
48528     },
48529
48530     getRepairXY : function(e, data){
48531         return false;
48532     },
48533
48534     onEndDrag : function(data, e){
48535         // fire end drag?
48536     },
48537
48538     onValidDrop : function(dd, e, id){
48539         // fire drag drop?
48540         this.hideProxy();
48541     },
48542
48543     beforeInvalidDrop : function(e, id){
48544
48545     }
48546 });/*
48547  * Based on:
48548  * Ext JS Library 1.1.1
48549  * Copyright(c) 2006-2007, Ext JS, LLC.
48550  *
48551  * Originally Released Under LGPL - original licence link has changed is not relivant.
48552  *
48553  * Fork - LGPL
48554  * <script type="text/javascript">
48555  */
48556  
48557
48558 /**
48559  * @class Roo.grid.ColumnModel
48560  * @extends Roo.util.Observable
48561  * This is the default implementation of a ColumnModel used by the Grid. It defines
48562  * the columns in the grid.
48563  * <br>Usage:<br>
48564  <pre><code>
48565  var colModel = new Roo.grid.ColumnModel([
48566         {header: "Ticker", width: 60, sortable: true, locked: true},
48567         {header: "Company Name", width: 150, sortable: true},
48568         {header: "Market Cap.", width: 100, sortable: true},
48569         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48570         {header: "Employees", width: 100, sortable: true, resizable: false}
48571  ]);
48572  </code></pre>
48573  * <p>
48574  
48575  * The config options listed for this class are options which may appear in each
48576  * individual column definition.
48577  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48578  * @constructor
48579  * @param {Object} config An Array of column config objects. See this class's
48580  * config objects for details.
48581 */
48582 Roo.grid.ColumnModel = function(config){
48583         /**
48584      * The config passed into the constructor
48585      */
48586     this.config = config;
48587     this.lookup = {};
48588
48589     // if no id, create one
48590     // if the column does not have a dataIndex mapping,
48591     // map it to the order it is in the config
48592     for(var i = 0, len = config.length; i < len; i++){
48593         var c = config[i];
48594         if(typeof c.dataIndex == "undefined"){
48595             c.dataIndex = i;
48596         }
48597         if(typeof c.renderer == "string"){
48598             c.renderer = Roo.util.Format[c.renderer];
48599         }
48600         if(typeof c.id == "undefined"){
48601             c.id = Roo.id();
48602         }
48603         if(c.editor && c.editor.xtype){
48604             c.editor  = Roo.factory(c.editor, Roo.grid);
48605         }
48606         if(c.editor && c.editor.isFormField){
48607             c.editor = new Roo.grid.GridEditor(c.editor);
48608         }
48609         this.lookup[c.id] = c;
48610     }
48611
48612     /**
48613      * The width of columns which have no width specified (defaults to 100)
48614      * @type Number
48615      */
48616     this.defaultWidth = 100;
48617
48618     /**
48619      * Default sortable of columns which have no sortable specified (defaults to false)
48620      * @type Boolean
48621      */
48622     this.defaultSortable = false;
48623
48624     this.addEvents({
48625         /**
48626              * @event widthchange
48627              * Fires when the width of a column changes.
48628              * @param {ColumnModel} this
48629              * @param {Number} columnIndex The column index
48630              * @param {Number} newWidth The new width
48631              */
48632             "widthchange": true,
48633         /**
48634              * @event headerchange
48635              * Fires when the text of a header changes.
48636              * @param {ColumnModel} this
48637              * @param {Number} columnIndex The column index
48638              * @param {Number} newText The new header text
48639              */
48640             "headerchange": true,
48641         /**
48642              * @event hiddenchange
48643              * Fires when a column is hidden or "unhidden".
48644              * @param {ColumnModel} this
48645              * @param {Number} columnIndex The column index
48646              * @param {Boolean} hidden true if hidden, false otherwise
48647              */
48648             "hiddenchange": true,
48649             /**
48650          * @event columnmoved
48651          * Fires when a column is moved.
48652          * @param {ColumnModel} this
48653          * @param {Number} oldIndex
48654          * @param {Number} newIndex
48655          */
48656         "columnmoved" : true,
48657         /**
48658          * @event columlockchange
48659          * Fires when a column's locked state is changed
48660          * @param {ColumnModel} this
48661          * @param {Number} colIndex
48662          * @param {Boolean} locked true if locked
48663          */
48664         "columnlockchange" : true
48665     });
48666     Roo.grid.ColumnModel.superclass.constructor.call(this);
48667 };
48668 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48669     /**
48670      * @cfg {String} header The header text to display in the Grid view.
48671      */
48672     /**
48673      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48674      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48675      * specified, the column's index is used as an index into the Record's data Array.
48676      */
48677     /**
48678      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48679      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48680      */
48681     /**
48682      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48683      * Defaults to the value of the {@link #defaultSortable} property.
48684      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48685      */
48686     /**
48687      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48688      */
48689     /**
48690      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48691      */
48692     /**
48693      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48694      */
48695     /**
48696      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48697      */
48698     /**
48699      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48700      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48701      * default renderer uses the raw data value.
48702      */
48703        /**
48704      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48705      */
48706     /**
48707      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48708      */
48709
48710     /**
48711      * Returns the id of the column at the specified index.
48712      * @param {Number} index The column index
48713      * @return {String} the id
48714      */
48715     getColumnId : function(index){
48716         return this.config[index].id;
48717     },
48718
48719     /**
48720      * Returns the column for a specified id.
48721      * @param {String} id The column id
48722      * @return {Object} the column
48723      */
48724     getColumnById : function(id){
48725         return this.lookup[id];
48726     },
48727
48728     
48729     /**
48730      * Returns the column for a specified dataIndex.
48731      * @param {String} dataIndex The column dataIndex
48732      * @return {Object|Boolean} the column or false if not found
48733      */
48734     getColumnByDataIndex: function(dataIndex){
48735         var index = this.findColumnIndex(dataIndex);
48736         return index > -1 ? this.config[index] : false;
48737     },
48738     
48739     /**
48740      * Returns the index for a specified column id.
48741      * @param {String} id The column id
48742      * @return {Number} the index, or -1 if not found
48743      */
48744     getIndexById : function(id){
48745         for(var i = 0, len = this.config.length; i < len; i++){
48746             if(this.config[i].id == id){
48747                 return i;
48748             }
48749         }
48750         return -1;
48751     },
48752     
48753     /**
48754      * Returns the index for a specified column dataIndex.
48755      * @param {String} dataIndex The column dataIndex
48756      * @return {Number} the index, or -1 if not found
48757      */
48758     
48759     findColumnIndex : function(dataIndex){
48760         for(var i = 0, len = this.config.length; i < len; i++){
48761             if(this.config[i].dataIndex == dataIndex){
48762                 return i;
48763             }
48764         }
48765         return -1;
48766     },
48767     
48768     
48769     moveColumn : function(oldIndex, newIndex){
48770         var c = this.config[oldIndex];
48771         this.config.splice(oldIndex, 1);
48772         this.config.splice(newIndex, 0, c);
48773         this.dataMap = null;
48774         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48775     },
48776
48777     isLocked : function(colIndex){
48778         return this.config[colIndex].locked === true;
48779     },
48780
48781     setLocked : function(colIndex, value, suppressEvent){
48782         if(this.isLocked(colIndex) == value){
48783             return;
48784         }
48785         this.config[colIndex].locked = value;
48786         if(!suppressEvent){
48787             this.fireEvent("columnlockchange", this, colIndex, value);
48788         }
48789     },
48790
48791     getTotalLockedWidth : function(){
48792         var totalWidth = 0;
48793         for(var i = 0; i < this.config.length; i++){
48794             if(this.isLocked(i) && !this.isHidden(i)){
48795                 this.totalWidth += this.getColumnWidth(i);
48796             }
48797         }
48798         return totalWidth;
48799     },
48800
48801     getLockedCount : function(){
48802         for(var i = 0, len = this.config.length; i < len; i++){
48803             if(!this.isLocked(i)){
48804                 return i;
48805             }
48806         }
48807     },
48808
48809     /**
48810      * Returns the number of columns.
48811      * @return {Number}
48812      */
48813     getColumnCount : function(visibleOnly){
48814         if(visibleOnly === true){
48815             var c = 0;
48816             for(var i = 0, len = this.config.length; i < len; i++){
48817                 if(!this.isHidden(i)){
48818                     c++;
48819                 }
48820             }
48821             return c;
48822         }
48823         return this.config.length;
48824     },
48825
48826     /**
48827      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48828      * @param {Function} fn
48829      * @param {Object} scope (optional)
48830      * @return {Array} result
48831      */
48832     getColumnsBy : function(fn, scope){
48833         var r = [];
48834         for(var i = 0, len = this.config.length; i < len; i++){
48835             var c = this.config[i];
48836             if(fn.call(scope||this, c, i) === true){
48837                 r[r.length] = c;
48838             }
48839         }
48840         return r;
48841     },
48842
48843     /**
48844      * Returns true if the specified column is sortable.
48845      * @param {Number} col The column index
48846      * @return {Boolean}
48847      */
48848     isSortable : function(col){
48849         if(typeof this.config[col].sortable == "undefined"){
48850             return this.defaultSortable;
48851         }
48852         return this.config[col].sortable;
48853     },
48854
48855     /**
48856      * Returns the rendering (formatting) function defined for the column.
48857      * @param {Number} col The column index.
48858      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48859      */
48860     getRenderer : function(col){
48861         if(!this.config[col].renderer){
48862             return Roo.grid.ColumnModel.defaultRenderer;
48863         }
48864         return this.config[col].renderer;
48865     },
48866
48867     /**
48868      * Sets the rendering (formatting) function for a column.
48869      * @param {Number} col The column index
48870      * @param {Function} fn The function to use to process the cell's raw data
48871      * to return HTML markup for the grid view. The render function is called with
48872      * the following parameters:<ul>
48873      * <li>Data value.</li>
48874      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48875      * <li>css A CSS style string to apply to the table cell.</li>
48876      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48877      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48878      * <li>Row index</li>
48879      * <li>Column index</li>
48880      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48881      */
48882     setRenderer : function(col, fn){
48883         this.config[col].renderer = fn;
48884     },
48885
48886     /**
48887      * Returns the width for the specified column.
48888      * @param {Number} col The column index
48889      * @return {Number}
48890      */
48891     getColumnWidth : function(col){
48892         return this.config[col].width || this.defaultWidth;
48893     },
48894
48895     /**
48896      * Sets the width for a column.
48897      * @param {Number} col The column index
48898      * @param {Number} width The new width
48899      */
48900     setColumnWidth : function(col, width, suppressEvent){
48901         this.config[col].width = width;
48902         this.totalWidth = null;
48903         if(!suppressEvent){
48904              this.fireEvent("widthchange", this, col, width);
48905         }
48906     },
48907
48908     /**
48909      * Returns the total width of all columns.
48910      * @param {Boolean} includeHidden True to include hidden column widths
48911      * @return {Number}
48912      */
48913     getTotalWidth : function(includeHidden){
48914         if(!this.totalWidth){
48915             this.totalWidth = 0;
48916             for(var i = 0, len = this.config.length; i < len; i++){
48917                 if(includeHidden || !this.isHidden(i)){
48918                     this.totalWidth += this.getColumnWidth(i);
48919                 }
48920             }
48921         }
48922         return this.totalWidth;
48923     },
48924
48925     /**
48926      * Returns the header for the specified column.
48927      * @param {Number} col The column index
48928      * @return {String}
48929      */
48930     getColumnHeader : function(col){
48931         return this.config[col].header;
48932     },
48933
48934     /**
48935      * Sets the header for a column.
48936      * @param {Number} col The column index
48937      * @param {String} header The new header
48938      */
48939     setColumnHeader : function(col, header){
48940         this.config[col].header = header;
48941         this.fireEvent("headerchange", this, col, header);
48942     },
48943
48944     /**
48945      * Returns the tooltip for the specified column.
48946      * @param {Number} col The column index
48947      * @return {String}
48948      */
48949     getColumnTooltip : function(col){
48950             return this.config[col].tooltip;
48951     },
48952     /**
48953      * Sets the tooltip for a column.
48954      * @param {Number} col The column index
48955      * @param {String} tooltip The new tooltip
48956      */
48957     setColumnTooltip : function(col, tooltip){
48958             this.config[col].tooltip = tooltip;
48959     },
48960
48961     /**
48962      * Returns the dataIndex for the specified column.
48963      * @param {Number} col The column index
48964      * @return {Number}
48965      */
48966     getDataIndex : function(col){
48967         return this.config[col].dataIndex;
48968     },
48969
48970     /**
48971      * Sets the dataIndex for a column.
48972      * @param {Number} col The column index
48973      * @param {Number} dataIndex The new dataIndex
48974      */
48975     setDataIndex : function(col, dataIndex){
48976         this.config[col].dataIndex = dataIndex;
48977     },
48978
48979     
48980     
48981     /**
48982      * Returns true if the cell is editable.
48983      * @param {Number} colIndex The column index
48984      * @param {Number} rowIndex The row index
48985      * @return {Boolean}
48986      */
48987     isCellEditable : function(colIndex, rowIndex){
48988         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48989     },
48990
48991     /**
48992      * Returns the editor defined for the cell/column.
48993      * return false or null to disable editing.
48994      * @param {Number} colIndex The column index
48995      * @param {Number} rowIndex The row index
48996      * @return {Object}
48997      */
48998     getCellEditor : function(colIndex, rowIndex){
48999         return this.config[colIndex].editor;
49000     },
49001
49002     /**
49003      * Sets if a column is editable.
49004      * @param {Number} col The column index
49005      * @param {Boolean} editable True if the column is editable
49006      */
49007     setEditable : function(col, editable){
49008         this.config[col].editable = editable;
49009     },
49010
49011
49012     /**
49013      * Returns true if the column is hidden.
49014      * @param {Number} colIndex The column index
49015      * @return {Boolean}
49016      */
49017     isHidden : function(colIndex){
49018         return this.config[colIndex].hidden;
49019     },
49020
49021
49022     /**
49023      * Returns true if the column width cannot be changed
49024      */
49025     isFixed : function(colIndex){
49026         return this.config[colIndex].fixed;
49027     },
49028
49029     /**
49030      * Returns true if the column can be resized
49031      * @return {Boolean}
49032      */
49033     isResizable : function(colIndex){
49034         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49035     },
49036     /**
49037      * Sets if a column is hidden.
49038      * @param {Number} colIndex The column index
49039      * @param {Boolean} hidden True if the column is hidden
49040      */
49041     setHidden : function(colIndex, hidden){
49042         this.config[colIndex].hidden = hidden;
49043         this.totalWidth = null;
49044         this.fireEvent("hiddenchange", this, colIndex, hidden);
49045     },
49046
49047     /**
49048      * Sets the editor for a column.
49049      * @param {Number} col The column index
49050      * @param {Object} editor The editor object
49051      */
49052     setEditor : function(col, editor){
49053         this.config[col].editor = editor;
49054     }
49055 });
49056
49057 Roo.grid.ColumnModel.defaultRenderer = function(value){
49058         if(typeof value == "string" && value.length < 1){
49059             return "&#160;";
49060         }
49061         return value;
49062 };
49063
49064 // Alias for backwards compatibility
49065 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49066 /*
49067  * Based on:
49068  * Ext JS Library 1.1.1
49069  * Copyright(c) 2006-2007, Ext JS, LLC.
49070  *
49071  * Originally Released Under LGPL - original licence link has changed is not relivant.
49072  *
49073  * Fork - LGPL
49074  * <script type="text/javascript">
49075  */
49076
49077 /**
49078  * @class Roo.grid.AbstractSelectionModel
49079  * @extends Roo.util.Observable
49080  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49081  * implemented by descendant classes.  This class should not be directly instantiated.
49082  * @constructor
49083  */
49084 Roo.grid.AbstractSelectionModel = function(){
49085     this.locked = false;
49086     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49087 };
49088
49089 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49090     /** @ignore Called by the grid automatically. Do not call directly. */
49091     init : function(grid){
49092         this.grid = grid;
49093         this.initEvents();
49094     },
49095
49096     /**
49097      * Locks the selections.
49098      */
49099     lock : function(){
49100         this.locked = true;
49101     },
49102
49103     /**
49104      * Unlocks the selections.
49105      */
49106     unlock : function(){
49107         this.locked = false;
49108     },
49109
49110     /**
49111      * Returns true if the selections are locked.
49112      * @return {Boolean}
49113      */
49114     isLocked : function(){
49115         return this.locked;
49116     }
49117 });/*
49118  * Based on:
49119  * Ext JS Library 1.1.1
49120  * Copyright(c) 2006-2007, Ext JS, LLC.
49121  *
49122  * Originally Released Under LGPL - original licence link has changed is not relivant.
49123  *
49124  * Fork - LGPL
49125  * <script type="text/javascript">
49126  */
49127 /**
49128  * @extends Roo.grid.AbstractSelectionModel
49129  * @class Roo.grid.RowSelectionModel
49130  * The default SelectionModel used by {@link Roo.grid.Grid}.
49131  * It supports multiple selections and keyboard selection/navigation. 
49132  * @constructor
49133  * @param {Object} config
49134  */
49135 Roo.grid.RowSelectionModel = function(config){
49136     Roo.apply(this, config);
49137     this.selections = new Roo.util.MixedCollection(false, function(o){
49138         return o.id;
49139     });
49140
49141     this.last = false;
49142     this.lastActive = false;
49143
49144     this.addEvents({
49145         /**
49146              * @event selectionchange
49147              * Fires when the selection changes
49148              * @param {SelectionModel} this
49149              */
49150             "selectionchange" : true,
49151         /**
49152              * @event afterselectionchange
49153              * Fires after the selection changes (eg. by key press or clicking)
49154              * @param {SelectionModel} this
49155              */
49156             "afterselectionchange" : true,
49157         /**
49158              * @event beforerowselect
49159              * Fires when a row is selected being selected, return false to cancel.
49160              * @param {SelectionModel} this
49161              * @param {Number} rowIndex The selected index
49162              * @param {Boolean} keepExisting False if other selections will be cleared
49163              */
49164             "beforerowselect" : true,
49165         /**
49166              * @event rowselect
49167              * Fires when a row is selected.
49168              * @param {SelectionModel} this
49169              * @param {Number} rowIndex The selected index
49170              * @param {Roo.data.Record} r The record
49171              */
49172             "rowselect" : true,
49173         /**
49174              * @event rowdeselect
49175              * Fires when a row is deselected.
49176              * @param {SelectionModel} this
49177              * @param {Number} rowIndex The selected index
49178              */
49179         "rowdeselect" : true
49180     });
49181     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49182     this.locked = false;
49183 };
49184
49185 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49186     /**
49187      * @cfg {Boolean} singleSelect
49188      * True to allow selection of only one row at a time (defaults to false)
49189      */
49190     singleSelect : false,
49191
49192     // private
49193     initEvents : function(){
49194
49195         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49196             this.grid.on("mousedown", this.handleMouseDown, this);
49197         }else{ // allow click to work like normal
49198             this.grid.on("rowclick", this.handleDragableRowClick, this);
49199         }
49200
49201         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49202             "up" : function(e){
49203                 if(!e.shiftKey){
49204                     this.selectPrevious(e.shiftKey);
49205                 }else if(this.last !== false && this.lastActive !== false){
49206                     var last = this.last;
49207                     this.selectRange(this.last,  this.lastActive-1);
49208                     this.grid.getView().focusRow(this.lastActive);
49209                     if(last !== false){
49210                         this.last = last;
49211                     }
49212                 }else{
49213                     this.selectFirstRow();
49214                 }
49215                 this.fireEvent("afterselectionchange", this);
49216             },
49217             "down" : function(e){
49218                 if(!e.shiftKey){
49219                     this.selectNext(e.shiftKey);
49220                 }else if(this.last !== false && this.lastActive !== false){
49221                     var last = this.last;
49222                     this.selectRange(this.last,  this.lastActive+1);
49223                     this.grid.getView().focusRow(this.lastActive);
49224                     if(last !== false){
49225                         this.last = last;
49226                     }
49227                 }else{
49228                     this.selectFirstRow();
49229                 }
49230                 this.fireEvent("afterselectionchange", this);
49231             },
49232             scope: this
49233         });
49234
49235         var view = this.grid.view;
49236         view.on("refresh", this.onRefresh, this);
49237         view.on("rowupdated", this.onRowUpdated, this);
49238         view.on("rowremoved", this.onRemove, this);
49239     },
49240
49241     // private
49242     onRefresh : function(){
49243         var ds = this.grid.dataSource, i, v = this.grid.view;
49244         var s = this.selections;
49245         s.each(function(r){
49246             if((i = ds.indexOfId(r.id)) != -1){
49247                 v.onRowSelect(i);
49248             }else{
49249                 s.remove(r);
49250             }
49251         });
49252     },
49253
49254     // private
49255     onRemove : function(v, index, r){
49256         this.selections.remove(r);
49257     },
49258
49259     // private
49260     onRowUpdated : function(v, index, r){
49261         if(this.isSelected(r)){
49262             v.onRowSelect(index);
49263         }
49264     },
49265
49266     /**
49267      * Select records.
49268      * @param {Array} records The records to select
49269      * @param {Boolean} keepExisting (optional) True to keep existing selections
49270      */
49271     selectRecords : function(records, keepExisting){
49272         if(!keepExisting){
49273             this.clearSelections();
49274         }
49275         var ds = this.grid.dataSource;
49276         for(var i = 0, len = records.length; i < len; i++){
49277             this.selectRow(ds.indexOf(records[i]), true);
49278         }
49279     },
49280
49281     /**
49282      * Gets the number of selected rows.
49283      * @return {Number}
49284      */
49285     getCount : function(){
49286         return this.selections.length;
49287     },
49288
49289     /**
49290      * Selects the first row in the grid.
49291      */
49292     selectFirstRow : function(){
49293         this.selectRow(0);
49294     },
49295
49296     /**
49297      * Select the last row.
49298      * @param {Boolean} keepExisting (optional) True to keep existing selections
49299      */
49300     selectLastRow : function(keepExisting){
49301         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49302     },
49303
49304     /**
49305      * Selects the row immediately following the last selected row.
49306      * @param {Boolean} keepExisting (optional) True to keep existing selections
49307      */
49308     selectNext : function(keepExisting){
49309         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49310             this.selectRow(this.last+1, keepExisting);
49311             this.grid.getView().focusRow(this.last);
49312         }
49313     },
49314
49315     /**
49316      * Selects the row that precedes the last selected row.
49317      * @param {Boolean} keepExisting (optional) True to keep existing selections
49318      */
49319     selectPrevious : function(keepExisting){
49320         if(this.last){
49321             this.selectRow(this.last-1, keepExisting);
49322             this.grid.getView().focusRow(this.last);
49323         }
49324     },
49325
49326     /**
49327      * Returns the selected records
49328      * @return {Array} Array of selected records
49329      */
49330     getSelections : function(){
49331         return [].concat(this.selections.items);
49332     },
49333
49334     /**
49335      * Returns the first selected record.
49336      * @return {Record}
49337      */
49338     getSelected : function(){
49339         return this.selections.itemAt(0);
49340     },
49341
49342
49343     /**
49344      * Clears all selections.
49345      */
49346     clearSelections : function(fast){
49347         if(this.locked) return;
49348         if(fast !== true){
49349             var ds = this.grid.dataSource;
49350             var s = this.selections;
49351             s.each(function(r){
49352                 this.deselectRow(ds.indexOfId(r.id));
49353             }, this);
49354             s.clear();
49355         }else{
49356             this.selections.clear();
49357         }
49358         this.last = false;
49359     },
49360
49361
49362     /**
49363      * Selects all rows.
49364      */
49365     selectAll : function(){
49366         if(this.locked) return;
49367         this.selections.clear();
49368         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49369             this.selectRow(i, true);
49370         }
49371     },
49372
49373     /**
49374      * Returns True if there is a selection.
49375      * @return {Boolean}
49376      */
49377     hasSelection : function(){
49378         return this.selections.length > 0;
49379     },
49380
49381     /**
49382      * Returns True if the specified row is selected.
49383      * @param {Number/Record} record The record or index of the record to check
49384      * @return {Boolean}
49385      */
49386     isSelected : function(index){
49387         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49388         return (r && this.selections.key(r.id) ? true : false);
49389     },
49390
49391     /**
49392      * Returns True if the specified record id is selected.
49393      * @param {String} id The id of record to check
49394      * @return {Boolean}
49395      */
49396     isIdSelected : function(id){
49397         return (this.selections.key(id) ? true : false);
49398     },
49399
49400     // private
49401     handleMouseDown : function(e, t){
49402         var view = this.grid.getView(), rowIndex;
49403         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49404             return;
49405         };
49406         if(e.shiftKey && this.last !== false){
49407             var last = this.last;
49408             this.selectRange(last, rowIndex, e.ctrlKey);
49409             this.last = last; // reset the last
49410             view.focusRow(rowIndex);
49411         }else{
49412             var isSelected = this.isSelected(rowIndex);
49413             if(e.button !== 0 && isSelected){
49414                 view.focusRow(rowIndex);
49415             }else if(e.ctrlKey && isSelected){
49416                 this.deselectRow(rowIndex);
49417             }else if(!isSelected){
49418                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49419                 view.focusRow(rowIndex);
49420             }
49421         }
49422         this.fireEvent("afterselectionchange", this);
49423     },
49424     // private
49425     handleDragableRowClick :  function(grid, rowIndex, e) 
49426     {
49427         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49428             this.selectRow(rowIndex, false);
49429             grid.view.focusRow(rowIndex);
49430              this.fireEvent("afterselectionchange", this);
49431         }
49432     },
49433     
49434     /**
49435      * Selects multiple rows.
49436      * @param {Array} rows Array of the indexes of the row to select
49437      * @param {Boolean} keepExisting (optional) True to keep existing selections
49438      */
49439     selectRows : function(rows, keepExisting){
49440         if(!keepExisting){
49441             this.clearSelections();
49442         }
49443         for(var i = 0, len = rows.length; i < len; i++){
49444             this.selectRow(rows[i], true);
49445         }
49446     },
49447
49448     /**
49449      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49450      * @param {Number} startRow The index of the first row in the range
49451      * @param {Number} endRow The index of the last row in the range
49452      * @param {Boolean} keepExisting (optional) True to retain existing selections
49453      */
49454     selectRange : function(startRow, endRow, keepExisting){
49455         if(this.locked) return;
49456         if(!keepExisting){
49457             this.clearSelections();
49458         }
49459         if(startRow <= endRow){
49460             for(var i = startRow; i <= endRow; i++){
49461                 this.selectRow(i, true);
49462             }
49463         }else{
49464             for(var i = startRow; i >= endRow; i--){
49465                 this.selectRow(i, true);
49466             }
49467         }
49468     },
49469
49470     /**
49471      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49472      * @param {Number} startRow The index of the first row in the range
49473      * @param {Number} endRow The index of the last row in the range
49474      */
49475     deselectRange : function(startRow, endRow, preventViewNotify){
49476         if(this.locked) return;
49477         for(var i = startRow; i <= endRow; i++){
49478             this.deselectRow(i, preventViewNotify);
49479         }
49480     },
49481
49482     /**
49483      * Selects a row.
49484      * @param {Number} row The index of the row to select
49485      * @param {Boolean} keepExisting (optional) True to keep existing selections
49486      */
49487     selectRow : function(index, keepExisting, preventViewNotify){
49488         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49489         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49490             if(!keepExisting || this.singleSelect){
49491                 this.clearSelections();
49492             }
49493             var r = this.grid.dataSource.getAt(index);
49494             this.selections.add(r);
49495             this.last = this.lastActive = index;
49496             if(!preventViewNotify){
49497                 this.grid.getView().onRowSelect(index);
49498             }
49499             this.fireEvent("rowselect", this, index, r);
49500             this.fireEvent("selectionchange", this);
49501         }
49502     },
49503
49504     /**
49505      * Deselects a row.
49506      * @param {Number} row The index of the row to deselect
49507      */
49508     deselectRow : function(index, preventViewNotify){
49509         if(this.locked) return;
49510         if(this.last == index){
49511             this.last = false;
49512         }
49513         if(this.lastActive == index){
49514             this.lastActive = false;
49515         }
49516         var r = this.grid.dataSource.getAt(index);
49517         this.selections.remove(r);
49518         if(!preventViewNotify){
49519             this.grid.getView().onRowDeselect(index);
49520         }
49521         this.fireEvent("rowdeselect", this, index);
49522         this.fireEvent("selectionchange", this);
49523     },
49524
49525     // private
49526     restoreLast : function(){
49527         if(this._last){
49528             this.last = this._last;
49529         }
49530     },
49531
49532     // private
49533     acceptsNav : function(row, col, cm){
49534         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49535     },
49536
49537     // private
49538     onEditorKey : function(field, e){
49539         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49540         if(k == e.TAB){
49541             e.stopEvent();
49542             ed.completeEdit();
49543             if(e.shiftKey){
49544                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49545             }else{
49546                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49547             }
49548         }else if(k == e.ENTER && !e.ctrlKey){
49549             e.stopEvent();
49550             ed.completeEdit();
49551             if(e.shiftKey){
49552                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49553             }else{
49554                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49555             }
49556         }else if(k == e.ESC){
49557             ed.cancelEdit();
49558         }
49559         if(newCell){
49560             g.startEditing(newCell[0], newCell[1]);
49561         }
49562     }
49563 });/*
49564  * Based on:
49565  * Ext JS Library 1.1.1
49566  * Copyright(c) 2006-2007, Ext JS, LLC.
49567  *
49568  * Originally Released Under LGPL - original licence link has changed is not relivant.
49569  *
49570  * Fork - LGPL
49571  * <script type="text/javascript">
49572  */
49573 /**
49574  * @class Roo.grid.CellSelectionModel
49575  * @extends Roo.grid.AbstractSelectionModel
49576  * This class provides the basic implementation for cell selection in a grid.
49577  * @constructor
49578  * @param {Object} config The object containing the configuration of this model.
49579  */
49580 Roo.grid.CellSelectionModel = function(config){
49581     Roo.apply(this, config);
49582
49583     this.selection = null;
49584
49585     this.addEvents({
49586         /**
49587              * @event beforerowselect
49588              * Fires before a cell is selected.
49589              * @param {SelectionModel} this
49590              * @param {Number} rowIndex The selected row index
49591              * @param {Number} colIndex The selected cell index
49592              */
49593             "beforecellselect" : true,
49594         /**
49595              * @event cellselect
49596              * Fires when a cell is selected.
49597              * @param {SelectionModel} this
49598              * @param {Number} rowIndex The selected row index
49599              * @param {Number} colIndex The selected cell index
49600              */
49601             "cellselect" : true,
49602         /**
49603              * @event selectionchange
49604              * Fires when the active selection changes.
49605              * @param {SelectionModel} this
49606              * @param {Object} selection null for no selection or an object (o) with two properties
49607                 <ul>
49608                 <li>o.record: the record object for the row the selection is in</li>
49609                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49610                 </ul>
49611              */
49612             "selectionchange" : true
49613     });
49614     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49615 };
49616
49617 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49618
49619     /** @ignore */
49620     initEvents : function(){
49621         this.grid.on("mousedown", this.handleMouseDown, this);
49622         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49623         var view = this.grid.view;
49624         view.on("refresh", this.onViewChange, this);
49625         view.on("rowupdated", this.onRowUpdated, this);
49626         view.on("beforerowremoved", this.clearSelections, this);
49627         view.on("beforerowsinserted", this.clearSelections, this);
49628         if(this.grid.isEditor){
49629             this.grid.on("beforeedit", this.beforeEdit,  this);
49630         }
49631     },
49632
49633         //private
49634     beforeEdit : function(e){
49635         this.select(e.row, e.column, false, true, e.record);
49636     },
49637
49638         //private
49639     onRowUpdated : function(v, index, r){
49640         if(this.selection && this.selection.record == r){
49641             v.onCellSelect(index, this.selection.cell[1]);
49642         }
49643     },
49644
49645         //private
49646     onViewChange : function(){
49647         this.clearSelections(true);
49648     },
49649
49650         /**
49651          * Returns the currently selected cell,.
49652          * @return {Array} The selected cell (row, column) or null if none selected.
49653          */
49654     getSelectedCell : function(){
49655         return this.selection ? this.selection.cell : null;
49656     },
49657
49658     /**
49659      * Clears all selections.
49660      * @param {Boolean} true to prevent the gridview from being notified about the change.
49661      */
49662     clearSelections : function(preventNotify){
49663         var s = this.selection;
49664         if(s){
49665             if(preventNotify !== true){
49666                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49667             }
49668             this.selection = null;
49669             this.fireEvent("selectionchange", this, null);
49670         }
49671     },
49672
49673     /**
49674      * Returns true if there is a selection.
49675      * @return {Boolean}
49676      */
49677     hasSelection : function(){
49678         return this.selection ? true : false;
49679     },
49680
49681     /** @ignore */
49682     handleMouseDown : function(e, t){
49683         var v = this.grid.getView();
49684         if(this.isLocked()){
49685             return;
49686         };
49687         var row = v.findRowIndex(t);
49688         var cell = v.findCellIndex(t);
49689         if(row !== false && cell !== false){
49690             this.select(row, cell);
49691         }
49692     },
49693
49694     /**
49695      * Selects a cell.
49696      * @param {Number} rowIndex
49697      * @param {Number} collIndex
49698      */
49699     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49700         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49701             this.clearSelections();
49702             r = r || this.grid.dataSource.getAt(rowIndex);
49703             this.selection = {
49704                 record : r,
49705                 cell : [rowIndex, colIndex]
49706             };
49707             if(!preventViewNotify){
49708                 var v = this.grid.getView();
49709                 v.onCellSelect(rowIndex, colIndex);
49710                 if(preventFocus !== true){
49711                     v.focusCell(rowIndex, colIndex);
49712                 }
49713             }
49714             this.fireEvent("cellselect", this, rowIndex, colIndex);
49715             this.fireEvent("selectionchange", this, this.selection);
49716         }
49717     },
49718
49719         //private
49720     isSelectable : function(rowIndex, colIndex, cm){
49721         return !cm.isHidden(colIndex);
49722     },
49723
49724     /** @ignore */
49725     handleKeyDown : function(e){
49726         Roo.log('Cell Sel Model handleKeyDown');
49727         if(!e.isNavKeyPress()){
49728             return;
49729         }
49730         var g = this.grid, s = this.selection;
49731         if(!s){
49732             e.stopEvent();
49733             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49734             if(cell){
49735                 this.select(cell[0], cell[1]);
49736             }
49737             return;
49738         }
49739         var sm = this;
49740         var walk = function(row, col, step){
49741             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49742         };
49743         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49744         var newCell;
49745
49746         switch(k){
49747             case e.TAB:
49748                 // handled by onEditorKey
49749                 if (g.isEditor && g.editing) {
49750                     return;
49751                 }
49752                 if(e.shiftKey){
49753                      newCell = walk(r, c-1, -1);
49754                 }else{
49755                      newCell = walk(r, c+1, 1);
49756                 }
49757              break;
49758              case e.DOWN:
49759                  newCell = walk(r+1, c, 1);
49760              break;
49761              case e.UP:
49762                  newCell = walk(r-1, c, -1);
49763              break;
49764              case e.RIGHT:
49765                  newCell = walk(r, c+1, 1);
49766              break;
49767              case e.LEFT:
49768                  newCell = walk(r, c-1, -1);
49769              break;
49770              case e.ENTER:
49771                  if(g.isEditor && !g.editing){
49772                     g.startEditing(r, c);
49773                     e.stopEvent();
49774                     return;
49775                 }
49776              break;
49777         };
49778         if(newCell){
49779             this.select(newCell[0], newCell[1]);
49780             e.stopEvent();
49781         }
49782     },
49783
49784     acceptsNav : function(row, col, cm){
49785         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49786     },
49787
49788     onEditorKey : function(field, e){
49789         
49790         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49791         ///Roo.log('onEditorKey' + k);
49792         
49793         if(k == e.TAB){
49794             if(e.shiftKey){
49795                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49796             }else{
49797                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49798             }
49799             e.stopEvent();
49800         }else if(k == e.ENTER && !e.ctrlKey){
49801             ed.completeEdit();
49802             e.stopEvent();
49803             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49804         }else if(k == e.ESC){
49805             ed.cancelEdit();
49806         }
49807         
49808         
49809         if(newCell){
49810             //Roo.log('next cell after edit');
49811             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
49812         }
49813     }
49814 });/*
49815  * Based on:
49816  * Ext JS Library 1.1.1
49817  * Copyright(c) 2006-2007, Ext JS, LLC.
49818  *
49819  * Originally Released Under LGPL - original licence link has changed is not relivant.
49820  *
49821  * Fork - LGPL
49822  * <script type="text/javascript">
49823  */
49824  
49825 /**
49826  * @class Roo.grid.EditorGrid
49827  * @extends Roo.grid.Grid
49828  * Class for creating and editable grid.
49829  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49830  * The container MUST have some type of size defined for the grid to fill. The container will be 
49831  * automatically set to position relative if it isn't already.
49832  * @param {Object} dataSource The data model to bind to
49833  * @param {Object} colModel The column model with info about this grid's columns
49834  */
49835 Roo.grid.EditorGrid = function(container, config){
49836     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49837     this.getGridEl().addClass("xedit-grid");
49838
49839     if(!this.selModel){
49840         this.selModel = new Roo.grid.CellSelectionModel();
49841     }
49842
49843     this.activeEditor = null;
49844
49845         this.addEvents({
49846             /**
49847              * @event beforeedit
49848              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49849              * <ul style="padding:5px;padding-left:16px;">
49850              * <li>grid - This grid</li>
49851              * <li>record - The record being edited</li>
49852              * <li>field - The field name being edited</li>
49853              * <li>value - The value for the field being edited.</li>
49854              * <li>row - The grid row index</li>
49855              * <li>column - The grid column index</li>
49856              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49857              * </ul>
49858              * @param {Object} e An edit event (see above for description)
49859              */
49860             "beforeedit" : true,
49861             /**
49862              * @event afteredit
49863              * Fires after a cell is edited. <br />
49864              * <ul style="padding:5px;padding-left:16px;">
49865              * <li>grid - This grid</li>
49866              * <li>record - The record being edited</li>
49867              * <li>field - The field name being edited</li>
49868              * <li>value - The value being set</li>
49869              * <li>originalValue - The original value for the field, before the edit.</li>
49870              * <li>row - The grid row index</li>
49871              * <li>column - The grid column index</li>
49872              * </ul>
49873              * @param {Object} e An edit event (see above for description)
49874              */
49875             "afteredit" : true,
49876             /**
49877              * @event validateedit
49878              * Fires after a cell is edited, but before the value is set in the record. 
49879          * You can use this to modify the value being set in the field, Return false
49880              * to cancel the change. The edit event object has the following properties <br />
49881              * <ul style="padding:5px;padding-left:16px;">
49882          * <li>editor - This editor</li>
49883              * <li>grid - This grid</li>
49884              * <li>record - The record being edited</li>
49885              * <li>field - The field name being edited</li>
49886              * <li>value - The value being set</li>
49887              * <li>originalValue - The original value for the field, before the edit.</li>
49888              * <li>row - The grid row index</li>
49889              * <li>column - The grid column index</li>
49890              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49891              * </ul>
49892              * @param {Object} e An edit event (see above for description)
49893              */
49894             "validateedit" : true
49895         });
49896     this.on("bodyscroll", this.stopEditing,  this);
49897     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49898 };
49899
49900 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49901     /**
49902      * @cfg {Number} clicksToEdit
49903      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49904      */
49905     clicksToEdit: 2,
49906
49907     // private
49908     isEditor : true,
49909     // private
49910     trackMouseOver: false, // causes very odd FF errors
49911
49912     onCellDblClick : function(g, row, col){
49913         this.startEditing(row, col);
49914     },
49915
49916     onEditComplete : function(ed, value, startValue){
49917         this.editing = false;
49918         this.activeEditor = null;
49919         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49920         var r = ed.record;
49921         var field = this.colModel.getDataIndex(ed.col);
49922         var e = {
49923             grid: this,
49924             record: r,
49925             field: field,
49926             originalValue: startValue,
49927             value: value,
49928             row: ed.row,
49929             column: ed.col,
49930             cancel:false,
49931             editor: ed
49932         };
49933         if(String(value) !== String(startValue)){
49934             
49935             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49936                 r.set(field, e.value);
49937                 delete e.cancel; //?? why!!!
49938                 this.fireEvent("afteredit", e);
49939             }
49940         } else {
49941             this.fireEvent("afteredit", e); // always fir it!
49942         }
49943         this.view.focusCell(ed.row, ed.col);
49944     },
49945
49946     /**
49947      * Starts editing the specified for the specified row/column
49948      * @param {Number} rowIndex
49949      * @param {Number} colIndex
49950      */
49951     startEditing : function(row, col){
49952         this.stopEditing();
49953         if(this.colModel.isCellEditable(col, row)){
49954             this.view.ensureVisible(row, col, true);
49955             var r = this.dataSource.getAt(row);
49956             var field = this.colModel.getDataIndex(col);
49957             var e = {
49958                 grid: this,
49959                 record: r,
49960                 field: field,
49961                 value: r.data[field],
49962                 row: row,
49963                 column: col,
49964                 cancel:false
49965             };
49966             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49967                 this.editing = true;
49968                 var ed = this.colModel.getCellEditor(col, row);
49969                 
49970                 if (!ed) {
49971                     return;
49972                 }
49973                 if(!ed.rendered){
49974                     ed.render(ed.parentEl || document.body);
49975                 }
49976                 ed.field.reset();
49977                 (function(){ // complex but required for focus issues in safari, ie and opera
49978                     ed.row = row;
49979                     ed.col = col;
49980                     ed.record = r;
49981                     ed.on("complete", this.onEditComplete, this, {single: true});
49982                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
49983                     this.activeEditor = ed;
49984                     var v = r.data[field];
49985                     ed.startEdit(this.view.getCell(row, col), v);
49986                 }).defer(50, this);
49987             }
49988         }
49989     },
49990         
49991     /**
49992      * Stops any active editing
49993      */
49994     stopEditing : function(){
49995         if(this.activeEditor){
49996             this.activeEditor.completeEdit();
49997         }
49998         this.activeEditor = null;
49999     }
50000 });/*
50001  * Based on:
50002  * Ext JS Library 1.1.1
50003  * Copyright(c) 2006-2007, Ext JS, LLC.
50004  *
50005  * Originally Released Under LGPL - original licence link has changed is not relivant.
50006  *
50007  * Fork - LGPL
50008  * <script type="text/javascript">
50009  */
50010
50011 // private - not really -- you end up using it !
50012 // This is a support class used internally by the Grid components
50013
50014 /**
50015  * @class Roo.grid.GridEditor
50016  * @extends Roo.Editor
50017  * Class for creating and editable grid elements.
50018  * @param {Object} config any settings (must include field)
50019  */
50020 Roo.grid.GridEditor = function(field, config){
50021     if (!config && field.field) {
50022         config = field;
50023         field = Roo.factory(config.field, Roo.form);
50024     }
50025     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50026     field.monitorTab = false;
50027 };
50028
50029 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50030     
50031     /**
50032      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50033      */
50034     
50035     alignment: "tl-tl",
50036     autoSize: "width",
50037     hideEl : false,
50038     cls: "x-small-editor x-grid-editor",
50039     shim:false,
50040     shadow:"frame"
50041 });/*
50042  * Based on:
50043  * Ext JS Library 1.1.1
50044  * Copyright(c) 2006-2007, Ext JS, LLC.
50045  *
50046  * Originally Released Under LGPL - original licence link has changed is not relivant.
50047  *
50048  * Fork - LGPL
50049  * <script type="text/javascript">
50050  */
50051   
50052
50053   
50054 Roo.grid.PropertyRecord = Roo.data.Record.create([
50055     {name:'name',type:'string'},  'value'
50056 ]);
50057
50058
50059 Roo.grid.PropertyStore = function(grid, source){
50060     this.grid = grid;
50061     this.store = new Roo.data.Store({
50062         recordType : Roo.grid.PropertyRecord
50063     });
50064     this.store.on('update', this.onUpdate,  this);
50065     if(source){
50066         this.setSource(source);
50067     }
50068     Roo.grid.PropertyStore.superclass.constructor.call(this);
50069 };
50070
50071
50072
50073 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50074     setSource : function(o){
50075         this.source = o;
50076         this.store.removeAll();
50077         var data = [];
50078         for(var k in o){
50079             if(this.isEditableValue(o[k])){
50080                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50081             }
50082         }
50083         this.store.loadRecords({records: data}, {}, true);
50084     },
50085
50086     onUpdate : function(ds, record, type){
50087         if(type == Roo.data.Record.EDIT){
50088             var v = record.data['value'];
50089             var oldValue = record.modified['value'];
50090             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50091                 this.source[record.id] = v;
50092                 record.commit();
50093                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50094             }else{
50095                 record.reject();
50096             }
50097         }
50098     },
50099
50100     getProperty : function(row){
50101        return this.store.getAt(row);
50102     },
50103
50104     isEditableValue: function(val){
50105         if(val && val instanceof Date){
50106             return true;
50107         }else if(typeof val == 'object' || typeof val == 'function'){
50108             return false;
50109         }
50110         return true;
50111     },
50112
50113     setValue : function(prop, value){
50114         this.source[prop] = value;
50115         this.store.getById(prop).set('value', value);
50116     },
50117
50118     getSource : function(){
50119         return this.source;
50120     }
50121 });
50122
50123 Roo.grid.PropertyColumnModel = function(grid, store){
50124     this.grid = grid;
50125     var g = Roo.grid;
50126     g.PropertyColumnModel.superclass.constructor.call(this, [
50127         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50128         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50129     ]);
50130     this.store = store;
50131     this.bselect = Roo.DomHelper.append(document.body, {
50132         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50133             {tag: 'option', value: 'true', html: 'true'},
50134             {tag: 'option', value: 'false', html: 'false'}
50135         ]
50136     });
50137     Roo.id(this.bselect);
50138     var f = Roo.form;
50139     this.editors = {
50140         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50141         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50142         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50143         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50144         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50145     };
50146     this.renderCellDelegate = this.renderCell.createDelegate(this);
50147     this.renderPropDelegate = this.renderProp.createDelegate(this);
50148 };
50149
50150 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50151     
50152     
50153     nameText : 'Name',
50154     valueText : 'Value',
50155     
50156     dateFormat : 'm/j/Y',
50157     
50158     
50159     renderDate : function(dateVal){
50160         return dateVal.dateFormat(this.dateFormat);
50161     },
50162
50163     renderBool : function(bVal){
50164         return bVal ? 'true' : 'false';
50165     },
50166
50167     isCellEditable : function(colIndex, rowIndex){
50168         return colIndex == 1;
50169     },
50170
50171     getRenderer : function(col){
50172         return col == 1 ?
50173             this.renderCellDelegate : this.renderPropDelegate;
50174     },
50175
50176     renderProp : function(v){
50177         return this.getPropertyName(v);
50178     },
50179
50180     renderCell : function(val){
50181         var rv = val;
50182         if(val instanceof Date){
50183             rv = this.renderDate(val);
50184         }else if(typeof val == 'boolean'){
50185             rv = this.renderBool(val);
50186         }
50187         return Roo.util.Format.htmlEncode(rv);
50188     },
50189
50190     getPropertyName : function(name){
50191         var pn = this.grid.propertyNames;
50192         return pn && pn[name] ? pn[name] : name;
50193     },
50194
50195     getCellEditor : function(colIndex, rowIndex){
50196         var p = this.store.getProperty(rowIndex);
50197         var n = p.data['name'], val = p.data['value'];
50198         
50199         if(typeof(this.grid.customEditors[n]) == 'string'){
50200             return this.editors[this.grid.customEditors[n]];
50201         }
50202         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50203             return this.grid.customEditors[n];
50204         }
50205         if(val instanceof Date){
50206             return this.editors['date'];
50207         }else if(typeof val == 'number'){
50208             return this.editors['number'];
50209         }else if(typeof val == 'boolean'){
50210             return this.editors['boolean'];
50211         }else{
50212             return this.editors['string'];
50213         }
50214     }
50215 });
50216
50217 /**
50218  * @class Roo.grid.PropertyGrid
50219  * @extends Roo.grid.EditorGrid
50220  * This class represents the  interface of a component based property grid control.
50221  * <br><br>Usage:<pre><code>
50222  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50223       
50224  });
50225  // set any options
50226  grid.render();
50227  * </code></pre>
50228   
50229  * @constructor
50230  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50231  * The container MUST have some type of size defined for the grid to fill. The container will be
50232  * automatically set to position relative if it isn't already.
50233  * @param {Object} config A config object that sets properties on this grid.
50234  */
50235 Roo.grid.PropertyGrid = function(container, config){
50236     config = config || {};
50237     var store = new Roo.grid.PropertyStore(this);
50238     this.store = store;
50239     var cm = new Roo.grid.PropertyColumnModel(this, store);
50240     store.store.sort('name', 'ASC');
50241     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50242         ds: store.store,
50243         cm: cm,
50244         enableColLock:false,
50245         enableColumnMove:false,
50246         stripeRows:false,
50247         trackMouseOver: false,
50248         clicksToEdit:1
50249     }, config));
50250     this.getGridEl().addClass('x-props-grid');
50251     this.lastEditRow = null;
50252     this.on('columnresize', this.onColumnResize, this);
50253     this.addEvents({
50254          /**
50255              * @event beforepropertychange
50256              * Fires before a property changes (return false to stop?)
50257              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50258              * @param {String} id Record Id
50259              * @param {String} newval New Value
50260          * @param {String} oldval Old Value
50261              */
50262         "beforepropertychange": true,
50263         /**
50264              * @event propertychange
50265              * Fires after a property changes
50266              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50267              * @param {String} id Record Id
50268              * @param {String} newval New Value
50269          * @param {String} oldval Old Value
50270              */
50271         "propertychange": true
50272     });
50273     this.customEditors = this.customEditors || {};
50274 };
50275 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50276     
50277      /**
50278      * @cfg {Object} customEditors map of colnames=> custom editors.
50279      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50280      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50281      * false disables editing of the field.
50282          */
50283     
50284       /**
50285      * @cfg {Object} propertyNames map of property Names to their displayed value
50286          */
50287     
50288     render : function(){
50289         Roo.grid.PropertyGrid.superclass.render.call(this);
50290         this.autoSize.defer(100, this);
50291     },
50292
50293     autoSize : function(){
50294         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50295         if(this.view){
50296             this.view.fitColumns();
50297         }
50298     },
50299
50300     onColumnResize : function(){
50301         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50302         this.autoSize();
50303     },
50304     /**
50305      * Sets the data for the Grid
50306      * accepts a Key => Value object of all the elements avaiable.
50307      * @param {Object} data  to appear in grid.
50308      */
50309     setSource : function(source){
50310         this.store.setSource(source);
50311         //this.autoSize();
50312     },
50313     /**
50314      * Gets all the data from the grid.
50315      * @return {Object} data  data stored in grid
50316      */
50317     getSource : function(){
50318         return this.store.getSource();
50319     }
50320 });/*
50321  * Based on:
50322  * Ext JS Library 1.1.1
50323  * Copyright(c) 2006-2007, Ext JS, LLC.
50324  *
50325  * Originally Released Under LGPL - original licence link has changed is not relivant.
50326  *
50327  * Fork - LGPL
50328  * <script type="text/javascript">
50329  */
50330  
50331 /**
50332  * @class Roo.LoadMask
50333  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50334  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50335  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50336  * element's UpdateManager load indicator and will be destroyed after the initial load.
50337  * @constructor
50338  * Create a new LoadMask
50339  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50340  * @param {Object} config The config object
50341  */
50342 Roo.LoadMask = function(el, config){
50343     this.el = Roo.get(el);
50344     Roo.apply(this, config);
50345     if(this.store){
50346         this.store.on('beforeload', this.onBeforeLoad, this);
50347         this.store.on('load', this.onLoad, this);
50348         this.store.on('loadexception', this.onLoad, this);
50349         this.removeMask = false;
50350     }else{
50351         var um = this.el.getUpdateManager();
50352         um.showLoadIndicator = false; // disable the default indicator
50353         um.on('beforeupdate', this.onBeforeLoad, this);
50354         um.on('update', this.onLoad, this);
50355         um.on('failure', this.onLoad, this);
50356         this.removeMask = true;
50357     }
50358 };
50359
50360 Roo.LoadMask.prototype = {
50361     /**
50362      * @cfg {Boolean} removeMask
50363      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50364      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50365      */
50366     /**
50367      * @cfg {String} msg
50368      * The text to display in a centered loading message box (defaults to 'Loading...')
50369      */
50370     msg : 'Loading...',
50371     /**
50372      * @cfg {String} msgCls
50373      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50374      */
50375     msgCls : 'x-mask-loading',
50376
50377     /**
50378      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50379      * @type Boolean
50380      */
50381     disabled: false,
50382
50383     /**
50384      * Disables the mask to prevent it from being displayed
50385      */
50386     disable : function(){
50387        this.disabled = true;
50388     },
50389
50390     /**
50391      * Enables the mask so that it can be displayed
50392      */
50393     enable : function(){
50394         this.disabled = false;
50395     },
50396
50397     // private
50398     onLoad : function(){
50399         this.el.unmask(this.removeMask);
50400     },
50401
50402     // private
50403     onBeforeLoad : function(){
50404         if(!this.disabled){
50405             this.el.mask(this.msg, this.msgCls);
50406         }
50407     },
50408
50409     // private
50410     destroy : function(){
50411         if(this.store){
50412             this.store.un('beforeload', this.onBeforeLoad, this);
50413             this.store.un('load', this.onLoad, this);
50414             this.store.un('loadexception', this.onLoad, this);
50415         }else{
50416             var um = this.el.getUpdateManager();
50417             um.un('beforeupdate', this.onBeforeLoad, this);
50418             um.un('update', this.onLoad, this);
50419             um.un('failure', this.onLoad, this);
50420         }
50421     }
50422 };/*
50423  * Based on:
50424  * Ext JS Library 1.1.1
50425  * Copyright(c) 2006-2007, Ext JS, LLC.
50426  *
50427  * Originally Released Under LGPL - original licence link has changed is not relivant.
50428  *
50429  * Fork - LGPL
50430  * <script type="text/javascript">
50431  */
50432 Roo.XTemplate = function(){
50433     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50434     var s = this.html;
50435
50436     s = ['<tpl>', s, '</tpl>'].join('');
50437
50438     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50439
50440     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50441     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50442     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50443     var m, id = 0;
50444     var tpls = [];
50445
50446     while(m = s.match(re)){
50447        var m2 = m[0].match(nameRe);
50448        var m3 = m[0].match(ifRe);
50449        var m4 = m[0].match(execRe);
50450        var exp = null, fn = null, exec = null;
50451        var name = m2 && m2[1] ? m2[1] : '';
50452        if(m3){
50453            exp = m3 && m3[1] ? m3[1] : null;
50454            if(exp){
50455                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50456            }
50457        }
50458        if(m4){
50459            exp = m4 && m4[1] ? m4[1] : null;
50460            if(exp){
50461                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50462            }
50463        }
50464        if(name){
50465            switch(name){
50466                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50467                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50468                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50469            }
50470        }
50471        tpls.push({
50472             id: id,
50473             target: name,
50474             exec: exec,
50475             test: fn,
50476             body: m[1]||''
50477         });
50478        s = s.replace(m[0], '{xtpl'+ id + '}');
50479        ++id;
50480     }
50481     for(var i = tpls.length-1; i >= 0; --i){
50482         this.compileTpl(tpls[i]);
50483     }
50484     this.master = tpls[tpls.length-1];
50485     this.tpls = tpls;
50486 };
50487 Roo.extend(Roo.XTemplate, Roo.Template, {
50488
50489     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50490
50491     applySubTemplate : function(id, values, parent){
50492         var t = this.tpls[id];
50493         if(t.test && !t.test.call(this, values, parent)){
50494             return '';
50495         }
50496         if(t.exec && t.exec.call(this, values, parent)){
50497             return '';
50498         }
50499         var vs = t.target ? t.target.call(this, values, parent) : values;
50500         parent = t.target ? values : parent;
50501         if(t.target && vs instanceof Array){
50502             var buf = [];
50503             for(var i = 0, len = vs.length; i < len; i++){
50504                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50505             }
50506             return buf.join('');
50507         }
50508         return t.compiled.call(this, vs, parent);
50509     },
50510
50511     compileTpl : function(tpl){
50512         var fm = Roo.util.Format;
50513         var useF = this.disableFormats !== true;
50514         var sep = Roo.isGecko ? "+" : ",";
50515         var fn = function(m, name, format, args){
50516             if(name.substr(0, 4) == 'xtpl'){
50517                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50518             }
50519             var v;
50520             if(name.indexOf('.') != -1){
50521                 v = name;
50522             }else{
50523                 v = "values['" + name + "']";
50524             }
50525             if(format && useF){
50526                 args = args ? ',' + args : "";
50527                 if(format.substr(0, 5) != "this."){
50528                     format = "fm." + format + '(';
50529                 }else{
50530                     format = 'this.call("'+ format.substr(5) + '", ';
50531                     args = ", values";
50532                 }
50533             }else{
50534                 args= ''; format = "("+v+" === undefined ? '' : ";
50535             }
50536             return "'"+ sep + format + v + args + ")"+sep+"'";
50537         };
50538         var body;
50539         // branched to use + in gecko and [].join() in others
50540         if(Roo.isGecko){
50541             body = "tpl.compiled = function(values, parent){ return '" +
50542                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50543                     "';};";
50544         }else{
50545             body = ["tpl.compiled = function(values, parent){ return ['"];
50546             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50547             body.push("'].join('');};");
50548             body = body.join('');
50549         }
50550         /** eval:var:zzzzzzz */
50551         eval(body);
50552         return this;
50553     },
50554
50555     applyTemplate : function(values){
50556         return this.master.compiled.call(this, values, {});
50557         var s = this.subs;
50558     },
50559
50560     apply : function(){
50561         return this.applyTemplate.apply(this, arguments);
50562     },
50563
50564     compile : function(){return this;}
50565 });
50566
50567 Roo.XTemplate.from = function(el){
50568     el = Roo.getDom(el);
50569     return new Roo.XTemplate(el.value || el.innerHTML);
50570 };/*
50571  * Original code for Roojs - LGPL
50572  * <script type="text/javascript">
50573  */
50574  
50575 /**
50576  * @class Roo.XComponent
50577  * A delayed Element creator...
50578  * 
50579  * Mypart.xyx = new Roo.XComponent({
50580
50581     parent : 'Mypart.xyz', // empty == document.element.!!
50582     order : '001',
50583     name : 'xxxx'
50584     region : 'xxxx'
50585     disabled : function() {} 
50586      
50587     tree : function() { // return an tree of xtype declared components
50588         var MODULE = this;
50589         return 
50590         {
50591             xtype : 'NestedLayoutPanel',
50592             // technicall
50593         }
50594      ]
50595  *})
50596  * @extends Roo.util.Observable
50597  * @constructor
50598  * @param cfg {Object} configuration of component
50599  * 
50600  */
50601 Roo.XComponent = function(cfg) {
50602     Roo.apply(this, cfg);
50603     this.addEvents({ 
50604         /**
50605              * @event built
50606              * Fires when this the componnt is built
50607              * @param {Roo.XComponent} c the component
50608              */
50609         'built' : true,
50610         /**
50611              * @event buildcomplete
50612              * Fires on the top level element when all elements have been built
50613              * @param {Roo.XComponent} c the top level component.
50614          */
50615         'buildcomplete' : true
50616         
50617     });
50618     
50619     Roo.XComponent.register(this);
50620     this.modules = false;
50621     this.el = false; // where the layout goes..
50622     
50623     
50624 }
50625 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50626     /**
50627      * @property el
50628      * The created element (with Roo.factory())
50629      * @type {Roo.Layout}
50630      */
50631     el  : false,
50632     
50633     /**
50634      * @property el
50635      * for BC  - use el in new code
50636      * @type {Roo.Layout}
50637      */
50638     panel : false,
50639     
50640     /**
50641      * @property layout
50642      * for BC  - use el in new code
50643      * @type {Roo.Layout}
50644      */
50645     layout : false,
50646     
50647      /**
50648      * @cfg {Function|boolean} disabled
50649      * If this module is disabled by some rule, return true from the funtion
50650      */
50651     disabled : false,
50652     
50653     /**
50654      * @cfg {String} parent 
50655      * Name of parent element which it get xtype added to..
50656      */
50657     parent: false,
50658     
50659     /**
50660      * @cfg {String} order
50661      * Used to set the order in which elements are created (usefull for multiple tabs)
50662      */
50663     
50664     order : false,
50665     /**
50666      * @cfg {String} name
50667      * String to display while loading.
50668      */
50669     name : false,
50670     /**
50671      * @cfg {Array} items
50672      * A single item array - the first element is the root of the tree..
50673      * It's done this way to stay compatible with the Xtype system...
50674      */
50675     items : false
50676      
50677      
50678     
50679 });
50680
50681 Roo.apply(Roo.XComponent, {
50682     
50683     /**
50684      * @property  buildCompleted
50685      * True when the builder has completed building the interface.
50686      * @type Boolean
50687      */
50688     buildCompleted : false,
50689      
50690     /**
50691      * @property  topModule
50692      * the upper most module - uses document.element as it's constructor.
50693      * @type Object
50694      */
50695      
50696     topModule  : false,
50697       
50698     /**
50699      * @property  modules
50700      * array of modules to be created by registration system.
50701      * @type Roo.XComponent
50702      */
50703     
50704     modules : [],
50705       
50706     
50707     /**
50708      * Register components to be built later.
50709      *
50710      * This solves the following issues
50711      * - Building is not done on page load, but after an authentication process has occured.
50712      * - Interface elements are registered on page load
50713      * - Parent Interface elements may not be loaded before child, so this handles that..
50714      * 
50715      *
50716      * example:
50717      * 
50718      * MyApp.register({
50719           order : '000001',
50720           module : 'Pman.Tab.projectMgr',
50721           region : 'center',
50722           parent : 'Pman.layout',
50723           disabled : false,  // or use a function..
50724         })
50725      
50726      * * @param {Object} details about module
50727      */
50728     register : function(obj) {
50729         this.modules.push(obj);
50730          
50731     },
50732     /**
50733      * convert a string to an object..
50734      * 
50735      */
50736     
50737     toObject : function(str)
50738     {
50739         if (!str || typeof(str) == 'object') {
50740             return str;
50741         }
50742         var ar = str.split('.');
50743         var rt, o;
50744         rt = ar.shift();
50745             /** eval:var:o */
50746         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50747         if (o === false) {
50748             throw "Module not found : " + str;
50749         }
50750         Roo.each(ar, function(e) {
50751             if (typeof(o[e]) == 'undefined') {
50752                 throw "Module not found : " + str;
50753             }
50754             o = o[e];
50755         });
50756         return o;
50757         
50758     },
50759     
50760     
50761     /**
50762      * move modules into their correct place in the tree..
50763      * 
50764      */
50765     preBuild : function ()
50766     {
50767         
50768         Roo.each(this.modules , function (obj)
50769         {
50770             obj.parent = this.toObject(obj.parent);
50771             
50772             if (!obj.parent) {
50773                 this.topModule = obj;
50774                 return;
50775             }
50776             
50777             if (!obj.parent.modules) {
50778                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50779                     function(o) { return o.order + '' }
50780                 );
50781             }
50782             
50783             obj.parent.modules.add(obj);
50784         }, this);
50785     },
50786     
50787      /**
50788      * make a list of modules to build.
50789      * @return {Array} list of modules. 
50790      */ 
50791     
50792     buildOrder : function()
50793     {
50794         var _this = this;
50795         var cmp = function(a,b) {   
50796             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50797         };
50798         
50799         if (!this.topModule || !this.topModule.modules) {
50800             throw "No top level modules to build";
50801         }
50802        
50803         // make a flat list in order of modules to build.
50804         var mods = [ this.topModule ];
50805         
50806         
50807         // add modules to their parents..
50808         var addMod = function(m) {
50809            // Roo.debug && Roo.log(m.modKey);
50810             
50811             mods.push(m);
50812             if (m.modules) {
50813                 m.modules.keySort('ASC',  cmp );
50814                 m.modules.each(addMod);
50815             }
50816             // not sure if this is used any more..
50817             if (m.finalize) {
50818                 m.finalize.name = m.name + " (clean up) ";
50819                 mods.push(m.finalize);
50820             }
50821             
50822         }
50823         this.topModule.modules.keySort('ASC',  cmp );
50824         this.topModule.modules.each(addMod);
50825         return mods;
50826     },
50827     
50828      /**
50829      * Build the registered modules.
50830      * @param {Object} parent element.
50831      * @param {Function} optional method to call after module has been added.
50832      * 
50833      */ 
50834    
50835     build : function() 
50836     {
50837         
50838         this.preBuild();
50839         var mods = this.buildOrder();
50840       
50841         //this.allmods = mods;
50842         //Roo.debug && Roo.log(mods);
50843         //return;
50844         if (!mods.length) { // should not happen
50845             throw "NO modules!!!";
50846         }
50847         
50848         
50849         
50850         // flash it up as modal - so we store the mask!?
50851         Roo.MessageBox.show({ title: 'loading' });
50852         Roo.MessageBox.show({
50853            title: "Please wait...",
50854            msg: "Building Interface...",
50855            width:450,
50856            progress:true,
50857            closable:false,
50858            modal: false
50859           
50860         });
50861         var total = mods.length;
50862         
50863         var _this = this;
50864         var progressRun = function() {
50865             if (!mods.length) {
50866                 Roo.debug && Roo.log('hide?');
50867                 Roo.MessageBox.hide();
50868                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
50869                 return;    
50870             }
50871             
50872             var m = mods.shift();
50873             Roo.debug && Roo.log(m);
50874             if (typeof(m) == 'function') { // not sure if this is supported any more..
50875                 m.call(this);
50876                 return progressRun.defer(10, _this);
50877             } 
50878             
50879             Roo.MessageBox.updateProgress(
50880                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50881                     " of " + total + 
50882                     (m.name ? (' - ' + m.name) : '')
50883                     );
50884             
50885          
50886             
50887             var disabled = (typeof(m.disabled) == 'function') ?
50888                 m.disabled.call(m.module.disabled) : m.disabled;    
50889             
50890             
50891             if (disabled) {
50892                 return progressRun(); // we do not update the display!
50893             }
50894             
50895             if (!m.parent) {
50896                 // it's a top level one..
50897                 var layoutbase = new Ext.BorderLayout(document.body, {
50898                
50899                     center: {
50900                          titlebar: false,
50901                          autoScroll:false,
50902                          closeOnTab: true,
50903                          tabPosition: 'top',
50904                          //resizeTabs: true,
50905                          alwaysShowTabs: true,
50906                          minTabWidth: 140
50907                     }
50908                 });
50909                 var tree = m.tree();
50910                 tree.region = 'center';
50911                 m.el = layoutbase.addxtype(tree);
50912                 m.panel = m.el;
50913                 m.layout = m.panel.layout;    
50914                 return progressRun.defer(10, _this);
50915             }
50916             
50917             var tree = m.tree();
50918             tree.region = tree.region || m.region;
50919             m.el = m.parent.el.addxtype(tree);
50920             m.fireEvent('built', m);
50921             m.panel = m.el;
50922             m.layout = m.panel.layout;    
50923             progressRun.defer(10, _this); 
50924             
50925         }
50926         progressRun.defer(1, _this);
50927      
50928         
50929         
50930     }
50931      
50932    
50933     
50934     
50935 });
50936  //<script type="text/javascript">
50937
50938
50939 /**
50940  * @class Roo.Login
50941  * @extends Roo.LayoutDialog
50942  * A generic Login Dialog..... - only one needed in theory!?!?
50943  *
50944  * Fires XComponent builder on success...
50945  * 
50946  * Sends 
50947  *    username,password, lang = for login actions.
50948  *    check = 1 for periodic checking that sesion is valid.
50949  *    passwordRequest = email request password
50950  *    logout = 1 = to logout
50951  * 
50952  * Affects: (this id="????" elements)
50953  *   loading  (removed) (used to indicate application is loading)
50954  *   loading-mask (hides) (used to hide application when it's building loading)
50955  *   
50956  * 
50957  * Usage: 
50958  *    
50959  * 
50960  * Myapp.login = Roo.Login({
50961      url: xxxx,
50962    
50963      realm : 'Myapp', 
50964      
50965      
50966      method : 'POST',
50967      
50968      
50969      * 
50970  })
50971  * 
50972  * 
50973  * 
50974  **/
50975  
50976 Roo.Login = function(cfg)
50977 {
50978     this.addEvents({
50979         'refreshed' : true
50980     });
50981     
50982     Roo.apply(this,cfg);
50983     
50984     Roo.onReady(function() {
50985         this.onLoad();
50986     }, this);
50987     // call parent..
50988     
50989    
50990     Roo.Login.superclass.constructor.call(this, this);
50991     //this.addxtype(this.items[0]);
50992     
50993     
50994 }
50995
50996
50997 Roo.extend(Roo.Login, Roo.LayoutDialog, {
50998     
50999     /**
51000      * @cfg {String} method
51001      * Method used to query for login details.
51002      */
51003     
51004     method : 'POST',
51005     /**
51006      * @cfg {String} url
51007      * URL to query login data. - eg. baseURL + '/Login.php'
51008      */
51009     url : '',
51010     
51011     /**
51012      * @property user
51013      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51014      * @type {Object} 
51015      */
51016     user : false,
51017     /**
51018      * @property checkFails
51019      * Number of times we have attempted to get authentication check, and failed.
51020      * @type {Number} 
51021      */
51022     checkFails : 0,
51023       /**
51024      * @property intervalID
51025      * The window interval that does the constant login checking.
51026      * @type {Number} 
51027      */
51028     intervalID : 0,
51029     
51030     
51031     onLoad : function() // called on page load...
51032     {
51033         // load 
51034          
51035         if (Roo.get('loading')) { // clear any loading indicator..
51036             Roo.get('loading').remove();
51037         }
51038         
51039         //this.switchLang('en'); // set the language to english..
51040        
51041         this.check({
51042             success:  function(response, opts)  {  // check successfull...
51043             
51044                 var res = this.processResponse(response);
51045                 this.checkFails =0;
51046                 if (!res.success) { // error!
51047                     this.checkFails = 5;
51048                     //console.log('call failure');
51049                     return this.failure(response,opts);
51050                 }
51051                 
51052                 if (!res.data.id) { // id=0 == login failure.
51053                     return this.show();
51054                 }
51055                 
51056                               
51057                         //console.log(success);
51058                 this.fillAuth(res.data);   
51059                 this.checkFails =0;
51060                 Roo.XComponent.build();
51061             },
51062             failure : this.show
51063         });
51064         
51065     }, 
51066     
51067     
51068     check: function(cfg) // called every so often to refresh cookie etc..
51069     {
51070         if (cfg.again) { // could be undefined..
51071             this.checkFails++;
51072         } else {
51073             this.checkFails = 0;
51074         }
51075         var _this = this;
51076         if (this.sending) {
51077             if ( this.checkFails > 4) {
51078                 Roo.MessageBox.alert("Error",  
51079                     "Error getting authentication status. - try reloading, or wait a while", function() {
51080                         _this.sending = false;
51081                     }); 
51082                 return;
51083             }
51084             cfg.again = true;
51085             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51086             return;
51087         }
51088         this.sending = true;
51089         
51090         Roo.Ajax.request({  
51091             url: this.url,
51092             params: {
51093                 getAuthUser: true
51094             },  
51095             method: this.method,
51096             success:  cfg.success || this.success,
51097             failure : cfg.failure || this.failure,
51098             scope : this,
51099             callCfg : cfg
51100               
51101         });  
51102     }, 
51103     
51104     
51105     logout: function()
51106     {
51107         window.onbeforeunload = function() { }; // false does not work for IE..
51108         this.user = false;
51109         var _this = this;
51110         
51111         Roo.Ajax.request({  
51112             url: this.url,
51113             params: {
51114                 logout: 1
51115             },  
51116             method: 'GET',
51117             failure : function() {
51118                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51119                     document.location = document.location.toString() + '?ts=' + Math.random();
51120                 });
51121                 
51122             },
51123             success : function() {
51124                 _this.user = false;
51125                 this.checkFails =0;
51126                 // fixme..
51127                 document.location = document.location.toString() + '?ts=' + Math.random();
51128             }
51129               
51130               
51131         }); 
51132     },
51133     
51134     processResponse : function (response)
51135     {
51136         var res = '';
51137         try {
51138             res = Roo.decode(response.responseText);
51139             // oops...
51140             if (typeof(res) != 'object') {
51141                 res = { success : false, errorMsg : res, errors : true };
51142             }
51143             if (typeof(res.success) == 'undefined') {
51144                 res.success = false;
51145             }
51146             
51147         } catch(e) {
51148             res = { success : false,  errorMsg : response.responseText, errors : true };
51149         }
51150         return res;
51151     },
51152     
51153     success : function(response, opts)  // check successfull...
51154     {  
51155         this.sending = false;
51156         var res = this.processResponse(response);
51157         if (!res.success) {
51158             return this.failure(response, opts);
51159         }
51160         if (!res.data || !res.data.id) {
51161             return this.failure(response,opts);
51162         }
51163         //console.log(res);
51164         this.fillAuth(res.data);
51165         
51166         this.checkFails =0;
51167         
51168     },
51169     
51170     
51171     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51172     {
51173         this.authUser = -1;
51174         this.sending = false;
51175         var res = this.processResponse(response);
51176         //console.log(res);
51177         if ( this.checkFails > 2) {
51178         
51179             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51180                 "Error getting authentication status. - try reloading"); 
51181             return;
51182         }
51183         opts.callCfg.again = true;
51184         this.check.defer(1000, this, [ opts.callCfg ]);
51185         return;  
51186     },
51187     
51188     
51189     
51190     fillAuth: function(au) {
51191         this.startAuthCheck();
51192         this.authUserId = au.id;
51193         this.authUser = au;
51194         this.lastChecked = new Date();
51195         this.fireEvent('refreshed', au);
51196         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51197         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51198         au.lang = au.lang || 'en';
51199         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51200         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51201         this.switchLang(au.lang );
51202         
51203      
51204         // open system... - -on setyp..
51205         if (this.authUserId  < 0) {
51206             Roo.MessageBox.alert("Warning", 
51207                 "This is an open system - please set up a admin user with a password.");  
51208         }
51209          
51210         //Pman.onload(); // which should do nothing if it's a re-auth result...
51211         
51212              
51213     },
51214     
51215     startAuthCheck : function() // starter for timeout checking..
51216     {
51217         if (this.intervalID) { // timer already in place...
51218             return false;
51219         }
51220         var _this = this;
51221         this.intervalID =  window.setInterval(function() {
51222               _this.check(false);
51223             }, 120000); // every 120 secs = 2mins..
51224         
51225         
51226     },
51227          
51228     
51229     switchLang : function (lang) 
51230     {
51231         _T = typeof(_T) == 'undefined' ? false : _T;
51232           if (!_T || !lang.length) {
51233             return;
51234         }
51235         
51236         if (!_T && lang != 'en') {
51237             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51238             return;
51239         }
51240         
51241         if (typeof(_T.en) == 'undefined') {
51242             _T.en = {};
51243             Roo.apply(_T.en, _T);
51244         }
51245         
51246         if (typeof(_T[lang]) == 'undefined') {
51247             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51248             return;
51249         }
51250         
51251         
51252         Roo.apply(_T, _T[lang]);
51253         // just need to set the text values for everything...
51254         var _this = this;
51255         /* this will not work ...
51256         if (this.form) { 
51257             
51258                
51259             function formLabel(name, val) {
51260                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51261             }
51262             
51263             formLabel('password', "Password"+':');
51264             formLabel('username', "Email Address"+':');
51265             formLabel('lang', "Language"+':');
51266             this.dialog.setTitle("Login");
51267             this.dialog.buttons[0].setText("Forgot Password");
51268             this.dialog.buttons[1].setText("Login");
51269         }
51270         */
51271         
51272         
51273     },
51274     
51275     
51276     title: "Login",
51277     modal: true,
51278     width:  350,
51279     //height: 230,
51280     height: 180,
51281     shadow: true,
51282     minWidth:200,
51283     minHeight:180,
51284     //proxyDrag: true,
51285     closable: false,
51286     draggable: false,
51287     collapsible: false,
51288     resizable: false,
51289     center: {  // needed??
51290         autoScroll:false,
51291         titlebar: false,
51292        // tabPosition: 'top',
51293         hideTabs: true,
51294         closeOnTab: true,
51295         alwaysShowTabs: false
51296     } ,
51297     listeners : {
51298         
51299         show  : function(dlg)
51300         {
51301             //console.log(this);
51302             this.form = this.layout.getRegion('center').activePanel.form;
51303             this.form.dialog = dlg;
51304             this.buttons[0].form = this.form;
51305             this.buttons[0].dialog = dlg;
51306             this.buttons[1].form = this.form;
51307             this.buttons[1].dialog = dlg;
51308            
51309            //this.resizeToLogo.defer(1000,this);
51310             // this is all related to resizing for logos..
51311             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51312            //// if (!sz) {
51313              //   this.resizeToLogo.defer(1000,this);
51314              //   return;
51315            // }
51316             //var w = Ext.lib.Dom.getViewWidth() - 100;
51317             //var h = Ext.lib.Dom.getViewHeight() - 100;
51318             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51319             //this.center();
51320             if (this.disabled) {
51321                 this.hide();
51322                 return;
51323             }
51324             
51325             if (this.user.id < 0) { // used for inital setup situations.
51326                 return;
51327             }
51328             
51329             if (this.intervalID) {
51330                 // remove the timer
51331                 window.clearInterval(this.intervalID);
51332                 this.intervalID = false;
51333             }
51334             
51335             
51336             if (Roo.get('loading')) {
51337                 Roo.get('loading').remove();
51338             }
51339             if (Roo.get('loading-mask')) {
51340                 Roo.get('loading-mask').hide();
51341             }
51342             
51343             //incomming._node = tnode;
51344             this.form.reset();
51345             //this.dialog.modal = !modal;
51346             //this.dialog.show();
51347             this.el.unmask(); 
51348             
51349             
51350             this.form.setValues({
51351                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51352                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51353             });
51354             
51355             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51356             if (this.form.findField('username').getValue().length > 0 ){
51357                 this.form.findField('password').focus();
51358             } else {
51359                this.form.findField('username').focus();
51360             }
51361     
51362         }
51363     },
51364     items : [
51365          {
51366        
51367             xtype : 'ContentPanel',
51368             xns : Roo,
51369             region: 'center',
51370             fitToFrame : true,
51371             
51372             items : [
51373     
51374                 {
51375                
51376                     xtype : 'Form',
51377                     xns : Roo.form,
51378                     labelWidth: 100,
51379                     style : 'margin: 10px;',
51380                     
51381                     listeners : {
51382                         actionfailed : function(f, act) {
51383                             // form can return { errors: .... }
51384                                 
51385                             //act.result.errors // invalid form element list...
51386                             //act.result.errorMsg// invalid form element list...
51387                             
51388                             this.dialog.el.unmask();
51389                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51390                                         "Login failed - communication error - try again.");
51391                                       
51392                         },
51393                         actioncomplete: function(re, act) {
51394                              
51395                             Roo.state.Manager.set(
51396                                 this.dialog.realm + '.username',  
51397                                     this.findField('username').getValue()
51398                             );
51399                             Roo.state.Manager.set(
51400                                 this.dialog.realm + '.lang',  
51401                                 this.findField('lang').getValue() 
51402                             );
51403                             
51404                             this.dialog.fillAuth(act.result.data);
51405                               
51406                             this.dialog.hide();
51407                             
51408                             if (Roo.get('loading-mask')) {
51409                                 Roo.get('loading-mask').show();
51410                             }
51411                             Roo.XComponent.build();
51412                             
51413                              
51414                             
51415                         }
51416                     },
51417                     items : [
51418                         {
51419                             xtype : 'TextField',
51420                             xns : Roo.form,
51421                             fieldLabel: "Email Address",
51422                             name: 'username',
51423                             width:200,
51424                             autoCreate : {tag: "input", type: "text", size: "20"}
51425                         },
51426                         {
51427                             xtype : 'TextField',
51428                             xns : Roo.form,
51429                             fieldLabel: "Password",
51430                             inputType: 'password',
51431                             name: 'password',
51432                             width:200,
51433                             autoCreate : {tag: "input", type: "text", size: "20"},
51434                             listeners : {
51435                                 specialkey : function(e,ev) {
51436                                     if (ev.keyCode == 13) {
51437                                         this.form.dialog.el.mask("Logging in");
51438                                         this.form.doAction('submit', {
51439                                             url: this.form.dialog.url,
51440                                             method: this.form.dialog.method
51441                                         });
51442                                     }
51443                                 }
51444                             }  
51445                         },
51446                         {
51447                             xtype : 'ComboBox',
51448                             xns : Roo.form,
51449                             fieldLabel: "Language",
51450                             name : 'langdisp',
51451                             store: {
51452                                 xtype : 'SimpleStore',
51453                                 fields: ['lang', 'ldisp'],
51454                                 data : [
51455                                     [ 'en', 'English' ],
51456                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51457                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51458                                 ]
51459                             },
51460                             
51461                             valueField : 'lang',
51462                             hiddenName:  'lang',
51463                             width: 200,
51464                             displayField:'ldisp',
51465                             typeAhead: false,
51466                             editable: false,
51467                             mode: 'local',
51468                             triggerAction: 'all',
51469                             emptyText:'Select a Language...',
51470                             selectOnFocus:true,
51471                             listeners : {
51472                                 select :  function(cb, rec, ix) {
51473                                     this.form.switchLang(rec.data.lang);
51474                                 }
51475                             }
51476                         
51477                         }
51478                     ]
51479                 }
51480                   
51481                 
51482             ]
51483         }
51484     ],
51485     buttons : [
51486         {
51487             xtype : 'Button',
51488             xns : 'Roo',
51489             text : "Forgot Password",
51490             listeners : {
51491                 click : function() {
51492                     //console.log(this);
51493                     var n = this.form.findField('username').getValue();
51494                     if (!n.length) {
51495                         Roo.MessageBox.alert("Error", "Fill in your email address");
51496                         return;
51497                     }
51498                     Roo.Ajax.request({
51499                         url: this.dialog.url,
51500                         params: {
51501                             passwordRequest: n
51502                         },
51503                         method: this.dialog.method,
51504                         success:  function(response, opts)  {  // check successfull...
51505                         
51506                             var res = this.dialog.processResponse(response);
51507                             if (!res.success) { // error!
51508                                Roo.MessageBox.alert("Error" ,
51509                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51510                                return;
51511                             }
51512                             Roo.MessageBox.alert("Notice" ,
51513                                 "Please check you email for the Password Reset message");
51514                         },
51515                         failure : function() {
51516                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51517                         }
51518                         
51519                     });
51520                 }
51521             }
51522         },
51523         {
51524             xtype : 'Button',
51525             xns : 'Roo',
51526             text : "Login",
51527             listeners : {
51528                 
51529                 click : function () {
51530                         
51531                     this.dialog.el.mask("Logging in");
51532                     this.form.doAction('submit', {
51533                             url: this.dialog.url,
51534                             method: this.dialog.method
51535                     });
51536                 }
51537             }
51538         }
51539     ]
51540   
51541   
51542 })
51543  
51544
51545
51546