e7415e5d461ebacfcaf8ba15e0a0ff46bbaa6384
[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         if(typeof o == "undefined" || o === null){
12775             return "null";
12776         }else if(o instanceof Array){
12777             return encodeArray(o);
12778         }else if(o instanceof Date){
12779             return encodeDate(o);
12780         }else if(typeof o == "string"){
12781             return encodeString(o);
12782         }else if(typeof o == "number"){
12783             return isFinite(o) ? String(o) : "null";
12784         }else if(typeof o == "boolean"){
12785             return String(o);
12786         }else {
12787             var a = ["{"], b, i, v;
12788             for (i in o) {
12789                 if(!useHasOwn || o.hasOwnProperty(i)) {
12790                     v = o[i];
12791                     switch (typeof v) {
12792                     case "undefined":
12793                     case "function":
12794                     case "unknown":
12795                         break;
12796                     default:
12797                         if(b){
12798                             a.push(',');
12799                         }
12800                         a.push(this.encode(i), ":",
12801                                 v === null ? "null" : this.encode(v));
12802                         b = true;
12803                     }
12804                 }
12805             }
12806             a.push("}");
12807             return a.join("");
12808         }
12809     };
12810     
12811     /**
12812      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12813      * @param {String} json The JSON string
12814      * @return {Object} The resulting object
12815      */
12816     this.decode = function(json){
12817         
12818         return /** eval:var:json */ eval("(" + json + ')');
12819     };
12820 })();
12821 /** 
12822  * Shorthand for {@link Roo.util.JSON#encode}
12823  * @member Roo encode 
12824  * @method */
12825 Roo.encode = Roo.util.JSON.encode;
12826 /** 
12827  * Shorthand for {@link Roo.util.JSON#decode}
12828  * @member Roo decode 
12829  * @method */
12830 Roo.decode = Roo.util.JSON.decode;
12831 /*
12832  * Based on:
12833  * Ext JS Library 1.1.1
12834  * Copyright(c) 2006-2007, Ext JS, LLC.
12835  *
12836  * Originally Released Under LGPL - original licence link has changed is not relivant.
12837  *
12838  * Fork - LGPL
12839  * <script type="text/javascript">
12840  */
12841  
12842 /**
12843  * @class Roo.util.Format
12844  * Reusable data formatting functions
12845  * @singleton
12846  */
12847 Roo.util.Format = function(){
12848     var trimRe = /^\s+|\s+$/g;
12849     return {
12850         /**
12851          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12852          * @param {String} value The string to truncate
12853          * @param {Number} length The maximum length to allow before truncating
12854          * @return {String} The converted text
12855          */
12856         ellipsis : function(value, len){
12857             if(value && value.length > len){
12858                 return value.substr(0, len-3)+"...";
12859             }
12860             return value;
12861         },
12862
12863         /**
12864          * Checks a reference and converts it to empty string if it is undefined
12865          * @param {Mixed} value Reference to check
12866          * @return {Mixed} Empty string if converted, otherwise the original value
12867          */
12868         undef : function(value){
12869             return typeof value != "undefined" ? value : "";
12870         },
12871
12872         /**
12873          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12874          * @param {String} value The string to encode
12875          * @return {String} The encoded text
12876          */
12877         htmlEncode : function(value){
12878             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12879         },
12880
12881         /**
12882          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12883          * @param {String} value The string to decode
12884          * @return {String} The decoded text
12885          */
12886         htmlDecode : function(value){
12887             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12888         },
12889
12890         /**
12891          * Trims any whitespace from either side of a string
12892          * @param {String} value The text to trim
12893          * @return {String} The trimmed text
12894          */
12895         trim : function(value){
12896             return String(value).replace(trimRe, "");
12897         },
12898
12899         /**
12900          * Returns a substring from within an original string
12901          * @param {String} value The original text
12902          * @param {Number} start The start index of the substring
12903          * @param {Number} length The length of the substring
12904          * @return {String} The substring
12905          */
12906         substr : function(value, start, length){
12907             return String(value).substr(start, length);
12908         },
12909
12910         /**
12911          * Converts a string to all lower case letters
12912          * @param {String} value The text to convert
12913          * @return {String} The converted text
12914          */
12915         lowercase : function(value){
12916             return String(value).toLowerCase();
12917         },
12918
12919         /**
12920          * Converts a string to all upper case letters
12921          * @param {String} value The text to convert
12922          * @return {String} The converted text
12923          */
12924         uppercase : function(value){
12925             return String(value).toUpperCase();
12926         },
12927
12928         /**
12929          * Converts the first character only of a string to upper case
12930          * @param {String} value The text to convert
12931          * @return {String} The converted text
12932          */
12933         capitalize : function(value){
12934             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12935         },
12936
12937         // private
12938         call : function(value, fn){
12939             if(arguments.length > 2){
12940                 var args = Array.prototype.slice.call(arguments, 2);
12941                 args.unshift(value);
12942                  
12943                 return /** eval:var:value */  eval(fn).apply(window, args);
12944             }else{
12945                 /** eval:var:value */
12946                 return /** eval:var:value */ eval(fn).call(window, value);
12947             }
12948         },
12949
12950         /**
12951          * Format a number as US currency
12952          * @param {Number/String} value The numeric value to format
12953          * @return {String} The formatted currency string
12954          */
12955         usMoney : function(v){
12956             v = (Math.round((v-0)*100))/100;
12957             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
12958             v = String(v);
12959             var ps = v.split('.');
12960             var whole = ps[0];
12961             var sub = ps[1] ? '.'+ ps[1] : '.00';
12962             var r = /(\d+)(\d{3})/;
12963             while (r.test(whole)) {
12964                 whole = whole.replace(r, '$1' + ',' + '$2');
12965             }
12966             return "$" + whole + sub ;
12967         },
12968
12969         /**
12970          * Parse a value into a formatted date using the specified format pattern.
12971          * @param {Mixed} value The value to format
12972          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
12973          * @return {String} The formatted date string
12974          */
12975         date : function(v, format){
12976             if(!v){
12977                 return "";
12978             }
12979             if(!(v instanceof Date)){
12980                 v = new Date(Date.parse(v));
12981             }
12982             return v.dateFormat(format || "m/d/Y");
12983         },
12984
12985         /**
12986          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
12987          * @param {String} format Any valid date format string
12988          * @return {Function} The date formatting function
12989          */
12990         dateRenderer : function(format){
12991             return function(v){
12992                 return Roo.util.Format.date(v, format);  
12993             };
12994         },
12995
12996         // private
12997         stripTagsRE : /<\/?[^>]+>/gi,
12998         
12999         /**
13000          * Strips all HTML tags
13001          * @param {Mixed} value The text from which to strip tags
13002          * @return {String} The stripped text
13003          */
13004         stripTags : function(v){
13005             return !v ? v : String(v).replace(this.stripTagsRE, "");
13006         }
13007     };
13008 }();/*
13009  * Based on:
13010  * Ext JS Library 1.1.1
13011  * Copyright(c) 2006-2007, Ext JS, LLC.
13012  *
13013  * Originally Released Under LGPL - original licence link has changed is not relivant.
13014  *
13015  * Fork - LGPL
13016  * <script type="text/javascript">
13017  */
13018
13019
13020  
13021
13022 /**
13023  * @class Roo.MasterTemplate
13024  * @extends Roo.Template
13025  * Provides a template that can have child templates. The syntax is:
13026 <pre><code>
13027 var t = new Roo.MasterTemplate(
13028         '&lt;select name="{name}"&gt;',
13029                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13030         '&lt;/select&gt;'
13031 );
13032 t.add('options', {value: 'foo', text: 'bar'});
13033 // or you can add multiple child elements in one shot
13034 t.addAll('options', [
13035     {value: 'foo', text: 'bar'},
13036     {value: 'foo2', text: 'bar2'},
13037     {value: 'foo3', text: 'bar3'}
13038 ]);
13039 // then append, applying the master template values
13040 t.append('my-form', {name: 'my-select'});
13041 </code></pre>
13042 * A name attribute for the child template is not required if you have only one child
13043 * template or you want to refer to them by index.
13044  */
13045 Roo.MasterTemplate = function(){
13046     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13047     this.originalHtml = this.html;
13048     var st = {};
13049     var m, re = this.subTemplateRe;
13050     re.lastIndex = 0;
13051     var subIndex = 0;
13052     while(m = re.exec(this.html)){
13053         var name = m[1], content = m[2];
13054         st[subIndex] = {
13055             name: name,
13056             index: subIndex,
13057             buffer: [],
13058             tpl : new Roo.Template(content)
13059         };
13060         if(name){
13061             st[name] = st[subIndex];
13062         }
13063         st[subIndex].tpl.compile();
13064         st[subIndex].tpl.call = this.call.createDelegate(this);
13065         subIndex++;
13066     }
13067     this.subCount = subIndex;
13068     this.subs = st;
13069 };
13070 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13071     /**
13072     * The regular expression used to match sub templates
13073     * @type RegExp
13074     * @property
13075     */
13076     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13077
13078     /**
13079      * Applies the passed values to a child template.
13080      * @param {String/Number} name (optional) The name or index of the child template
13081      * @param {Array/Object} values The values to be applied to the template
13082      * @return {MasterTemplate} this
13083      */
13084      add : function(name, values){
13085         if(arguments.length == 1){
13086             values = arguments[0];
13087             name = 0;
13088         }
13089         var s = this.subs[name];
13090         s.buffer[s.buffer.length] = s.tpl.apply(values);
13091         return this;
13092     },
13093
13094     /**
13095      * Applies all the passed values to a child template.
13096      * @param {String/Number} name (optional) The name or index of the child template
13097      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13098      * @param {Boolean} reset (optional) True to reset the template first
13099      * @return {MasterTemplate} this
13100      */
13101     fill : function(name, values, reset){
13102         var a = arguments;
13103         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13104             values = a[0];
13105             name = 0;
13106             reset = a[1];
13107         }
13108         if(reset){
13109             this.reset();
13110         }
13111         for(var i = 0, len = values.length; i < len; i++){
13112             this.add(name, values[i]);
13113         }
13114         return this;
13115     },
13116
13117     /**
13118      * Resets the template for reuse
13119      * @return {MasterTemplate} this
13120      */
13121      reset : function(){
13122         var s = this.subs;
13123         for(var i = 0; i < this.subCount; i++){
13124             s[i].buffer = [];
13125         }
13126         return this;
13127     },
13128
13129     applyTemplate : function(values){
13130         var s = this.subs;
13131         var replaceIndex = -1;
13132         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13133             return s[++replaceIndex].buffer.join("");
13134         });
13135         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13136     },
13137
13138     apply : function(){
13139         return this.applyTemplate.apply(this, arguments);
13140     },
13141
13142     compile : function(){return this;}
13143 });
13144
13145 /**
13146  * Alias for fill().
13147  * @method
13148  */
13149 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13150  /**
13151  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13152  * var tpl = Roo.MasterTemplate.from('element-id');
13153  * @param {String/HTMLElement} el
13154  * @param {Object} config
13155  * @static
13156  */
13157 Roo.MasterTemplate.from = function(el, config){
13158     el = Roo.getDom(el);
13159     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13160 };/*
13161  * Based on:
13162  * Ext JS Library 1.1.1
13163  * Copyright(c) 2006-2007, Ext JS, LLC.
13164  *
13165  * Originally Released Under LGPL - original licence link has changed is not relivant.
13166  *
13167  * Fork - LGPL
13168  * <script type="text/javascript">
13169  */
13170
13171  
13172 /**
13173  * @class Roo.util.CSS
13174  * Utility class for manipulating CSS rules
13175  * @singleton
13176  */
13177 Roo.util.CSS = function(){
13178         var rules = null;
13179         var doc = document;
13180
13181     var camelRe = /(-[a-z])/gi;
13182     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13183
13184    return {
13185    /**
13186     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13187     * tag and appended to the HEAD of the document.
13188     * @param {String|Object} cssText The text containing the css rules
13189     * @param {String} id An id to add to the stylesheet for later removal
13190     * @return {StyleSheet}
13191     */
13192     createStyleSheet : function(cssText, id){
13193         var ss;
13194         var head = doc.getElementsByTagName("head")[0];
13195         var nrules = doc.createElement("style");
13196         nrules.setAttribute("type", "text/css");
13197         if(id){
13198             nrules.setAttribute("id", id);
13199         }
13200         if (typeof(cssText) != 'string') {
13201             // support object maps..
13202             // not sure if this a good idea.. 
13203             // perhaps it should be merged with the general css handling
13204             // and handle js style props.
13205             var cssTextNew = [];
13206             for(var n in cssText) {
13207                 var citems = [];
13208                 for(var k in cssText[n]) {
13209                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13210                 }
13211                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13212                 
13213             }
13214             cssText = cssTextNew.join("\n");
13215             
13216         }
13217        
13218        
13219        if(Roo.isIE){
13220            head.appendChild(nrules);
13221            ss = nrules.styleSheet;
13222            ss.cssText = cssText;
13223        }else{
13224            try{
13225                 nrules.appendChild(doc.createTextNode(cssText));
13226            }catch(e){
13227                nrules.cssText = cssText; 
13228            }
13229            head.appendChild(nrules);
13230            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13231        }
13232        this.cacheStyleSheet(ss);
13233        return ss;
13234    },
13235
13236    /**
13237     * Removes a style or link tag by id
13238     * @param {String} id The id of the tag
13239     */
13240    removeStyleSheet : function(id){
13241        var existing = doc.getElementById(id);
13242        if(existing){
13243            existing.parentNode.removeChild(existing);
13244        }
13245    },
13246
13247    /**
13248     * Dynamically swaps an existing stylesheet reference for a new one
13249     * @param {String} id The id of an existing link tag to remove
13250     * @param {String} url The href of the new stylesheet to include
13251     */
13252    swapStyleSheet : function(id, url){
13253        this.removeStyleSheet(id);
13254        var ss = doc.createElement("link");
13255        ss.setAttribute("rel", "stylesheet");
13256        ss.setAttribute("type", "text/css");
13257        ss.setAttribute("id", id);
13258        ss.setAttribute("href", url);
13259        doc.getElementsByTagName("head")[0].appendChild(ss);
13260    },
13261    
13262    /**
13263     * Refresh the rule cache if you have dynamically added stylesheets
13264     * @return {Object} An object (hash) of rules indexed by selector
13265     */
13266    refreshCache : function(){
13267        return this.getRules(true);
13268    },
13269
13270    // private
13271    cacheStyleSheet : function(stylesheet){
13272        if(!rules){
13273            rules = {};
13274        }
13275        try{// try catch for cross domain access issue
13276            var ssRules = stylesheet.cssRules || stylesheet.rules;
13277            for(var j = ssRules.length-1; j >= 0; --j){
13278                rules[ssRules[j].selectorText] = ssRules[j];
13279            }
13280        }catch(e){}
13281    },
13282    
13283    /**
13284     * Gets all css rules for the document
13285     * @param {Boolean} refreshCache true to refresh the internal cache
13286     * @return {Object} An object (hash) of rules indexed by selector
13287     */
13288    getRules : function(refreshCache){
13289                 if(rules == null || refreshCache){
13290                         rules = {};
13291                         var ds = doc.styleSheets;
13292                         for(var i =0, len = ds.length; i < len; i++){
13293                             try{
13294                         this.cacheStyleSheet(ds[i]);
13295                     }catch(e){} 
13296                 }
13297                 }
13298                 return rules;
13299         },
13300         
13301         /**
13302     * Gets an an individual CSS rule by selector(s)
13303     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13304     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13305     * @return {CSSRule} The CSS rule or null if one is not found
13306     */
13307    getRule : function(selector, refreshCache){
13308                 var rs = this.getRules(refreshCache);
13309                 if(!(selector instanceof Array)){
13310                     return rs[selector];
13311                 }
13312                 for(var i = 0; i < selector.length; i++){
13313                         if(rs[selector[i]]){
13314                                 return rs[selector[i]];
13315                         }
13316                 }
13317                 return null;
13318         },
13319         
13320         
13321         /**
13322     * Updates a rule property
13323     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13324     * @param {String} property The css property
13325     * @param {String} value The new value for the property
13326     * @return {Boolean} true If a rule was found and updated
13327     */
13328    updateRule : function(selector, property, value){
13329                 if(!(selector instanceof Array)){
13330                         var rule = this.getRule(selector);
13331                         if(rule){
13332                                 rule.style[property.replace(camelRe, camelFn)] = value;
13333                                 return true;
13334                         }
13335                 }else{
13336                         for(var i = 0; i < selector.length; i++){
13337                                 if(this.updateRule(selector[i], property, value)){
13338                                         return true;
13339                                 }
13340                         }
13341                 }
13342                 return false;
13343         }
13344    };   
13345 }();/*
13346  * Based on:
13347  * Ext JS Library 1.1.1
13348  * Copyright(c) 2006-2007, Ext JS, LLC.
13349  *
13350  * Originally Released Under LGPL - original licence link has changed is not relivant.
13351  *
13352  * Fork - LGPL
13353  * <script type="text/javascript">
13354  */
13355
13356  
13357
13358 /**
13359  * @class Roo.util.ClickRepeater
13360  * @extends Roo.util.Observable
13361  * 
13362  * A wrapper class which can be applied to any element. Fires a "click" event while the
13363  * mouse is pressed. The interval between firings may be specified in the config but
13364  * defaults to 10 milliseconds.
13365  * 
13366  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13367  * 
13368  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13369  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13370  * Similar to an autorepeat key delay.
13371  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13372  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13373  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13374  *           "interval" and "delay" are ignored. "immediate" is honored.
13375  * @cfg {Boolean} preventDefault True to prevent the default click event
13376  * @cfg {Boolean} stopDefault True to stop the default click event
13377  * 
13378  * @history
13379  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13380  *     2007-02-02 jvs Renamed to ClickRepeater
13381  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13382  *
13383  *  @constructor
13384  * @param {String/HTMLElement/Element} el The element to listen on
13385  * @param {Object} config
13386  **/
13387 Roo.util.ClickRepeater = function(el, config)
13388 {
13389     this.el = Roo.get(el);
13390     this.el.unselectable();
13391
13392     Roo.apply(this, config);
13393
13394     this.addEvents({
13395     /**
13396      * @event mousedown
13397      * Fires when the mouse button is depressed.
13398      * @param {Roo.util.ClickRepeater} this
13399      */
13400         "mousedown" : true,
13401     /**
13402      * @event click
13403      * Fires on a specified interval during the time the element is pressed.
13404      * @param {Roo.util.ClickRepeater} this
13405      */
13406         "click" : true,
13407     /**
13408      * @event mouseup
13409      * Fires when the mouse key is released.
13410      * @param {Roo.util.ClickRepeater} this
13411      */
13412         "mouseup" : true
13413     });
13414
13415     this.el.on("mousedown", this.handleMouseDown, this);
13416     if(this.preventDefault || this.stopDefault){
13417         this.el.on("click", function(e){
13418             if(this.preventDefault){
13419                 e.preventDefault();
13420             }
13421             if(this.stopDefault){
13422                 e.stopEvent();
13423             }
13424         }, this);
13425     }
13426
13427     // allow inline handler
13428     if(this.handler){
13429         this.on("click", this.handler,  this.scope || this);
13430     }
13431
13432     Roo.util.ClickRepeater.superclass.constructor.call(this);
13433 };
13434
13435 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13436     interval : 20,
13437     delay: 250,
13438     preventDefault : true,
13439     stopDefault : false,
13440     timer : 0,
13441
13442     // private
13443     handleMouseDown : function(){
13444         clearTimeout(this.timer);
13445         this.el.blur();
13446         if(this.pressClass){
13447             this.el.addClass(this.pressClass);
13448         }
13449         this.mousedownTime = new Date();
13450
13451         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13452         this.el.on("mouseout", this.handleMouseOut, this);
13453
13454         this.fireEvent("mousedown", this);
13455         this.fireEvent("click", this);
13456         
13457         this.timer = this.click.defer(this.delay || this.interval, this);
13458     },
13459
13460     // private
13461     click : function(){
13462         this.fireEvent("click", this);
13463         this.timer = this.click.defer(this.getInterval(), this);
13464     },
13465
13466     // private
13467     getInterval: function(){
13468         if(!this.accelerate){
13469             return this.interval;
13470         }
13471         var pressTime = this.mousedownTime.getElapsed();
13472         if(pressTime < 500){
13473             return 400;
13474         }else if(pressTime < 1700){
13475             return 320;
13476         }else if(pressTime < 2600){
13477             return 250;
13478         }else if(pressTime < 3500){
13479             return 180;
13480         }else if(pressTime < 4400){
13481             return 140;
13482         }else if(pressTime < 5300){
13483             return 80;
13484         }else if(pressTime < 6200){
13485             return 50;
13486         }else{
13487             return 10;
13488         }
13489     },
13490
13491     // private
13492     handleMouseOut : function(){
13493         clearTimeout(this.timer);
13494         if(this.pressClass){
13495             this.el.removeClass(this.pressClass);
13496         }
13497         this.el.on("mouseover", this.handleMouseReturn, this);
13498     },
13499
13500     // private
13501     handleMouseReturn : function(){
13502         this.el.un("mouseover", this.handleMouseReturn);
13503         if(this.pressClass){
13504             this.el.addClass(this.pressClass);
13505         }
13506         this.click();
13507     },
13508
13509     // private
13510     handleMouseUp : function(){
13511         clearTimeout(this.timer);
13512         this.el.un("mouseover", this.handleMouseReturn);
13513         this.el.un("mouseout", this.handleMouseOut);
13514         Roo.get(document).un("mouseup", this.handleMouseUp);
13515         this.el.removeClass(this.pressClass);
13516         this.fireEvent("mouseup", this);
13517     }
13518 });/*
13519  * Based on:
13520  * Ext JS Library 1.1.1
13521  * Copyright(c) 2006-2007, Ext JS, LLC.
13522  *
13523  * Originally Released Under LGPL - original licence link has changed is not relivant.
13524  *
13525  * Fork - LGPL
13526  * <script type="text/javascript">
13527  */
13528
13529  
13530 /**
13531  * @class Roo.KeyNav
13532  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13533  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13534  * way to implement custom navigation schemes for any UI component.</p>
13535  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13536  * pageUp, pageDown, del, home, end.  Usage:</p>
13537  <pre><code>
13538 var nav = new Roo.KeyNav("my-element", {
13539     "left" : function(e){
13540         this.moveLeft(e.ctrlKey);
13541     },
13542     "right" : function(e){
13543         this.moveRight(e.ctrlKey);
13544     },
13545     "enter" : function(e){
13546         this.save();
13547     },
13548     scope : this
13549 });
13550 </code></pre>
13551  * @constructor
13552  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13553  * @param {Object} config The config
13554  */
13555 Roo.KeyNav = function(el, config){
13556     this.el = Roo.get(el);
13557     Roo.apply(this, config);
13558     if(!this.disabled){
13559         this.disabled = true;
13560         this.enable();
13561     }
13562 };
13563
13564 Roo.KeyNav.prototype = {
13565     /**
13566      * @cfg {Boolean} disabled
13567      * True to disable this KeyNav instance (defaults to false)
13568      */
13569     disabled : false,
13570     /**
13571      * @cfg {String} defaultEventAction
13572      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13573      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13574      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13575      */
13576     defaultEventAction: "stopEvent",
13577     /**
13578      * @cfg {Boolean} forceKeyDown
13579      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13580      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13581      * handle keydown instead of keypress.
13582      */
13583     forceKeyDown : false,
13584
13585     // private
13586     prepareEvent : function(e){
13587         var k = e.getKey();
13588         var h = this.keyToHandler[k];
13589         //if(h && this[h]){
13590         //    e.stopPropagation();
13591         //}
13592         if(Roo.isSafari && h && k >= 37 && k <= 40){
13593             e.stopEvent();
13594         }
13595     },
13596
13597     // private
13598     relay : function(e){
13599         var k = e.getKey();
13600         var h = this.keyToHandler[k];
13601         if(h && this[h]){
13602             if(this.doRelay(e, this[h], h) !== true){
13603                 e[this.defaultEventAction]();
13604             }
13605         }
13606     },
13607
13608     // private
13609     doRelay : function(e, h, hname){
13610         return h.call(this.scope || this, e);
13611     },
13612
13613     // possible handlers
13614     enter : false,
13615     left : false,
13616     right : false,
13617     up : false,
13618     down : false,
13619     tab : false,
13620     esc : false,
13621     pageUp : false,
13622     pageDown : false,
13623     del : false,
13624     home : false,
13625     end : false,
13626
13627     // quick lookup hash
13628     keyToHandler : {
13629         37 : "left",
13630         39 : "right",
13631         38 : "up",
13632         40 : "down",
13633         33 : "pageUp",
13634         34 : "pageDown",
13635         46 : "del",
13636         36 : "home",
13637         35 : "end",
13638         13 : "enter",
13639         27 : "esc",
13640         9  : "tab"
13641     },
13642
13643         /**
13644          * Enable this KeyNav
13645          */
13646         enable: function(){
13647                 if(this.disabled){
13648             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13649             // the EventObject will normalize Safari automatically
13650             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13651                 this.el.on("keydown", this.relay,  this);
13652             }else{
13653                 this.el.on("keydown", this.prepareEvent,  this);
13654                 this.el.on("keypress", this.relay,  this);
13655             }
13656                     this.disabled = false;
13657                 }
13658         },
13659
13660         /**
13661          * Disable this KeyNav
13662          */
13663         disable: function(){
13664                 if(!this.disabled){
13665                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13666                 this.el.un("keydown", this.relay);
13667             }else{
13668                 this.el.un("keydown", this.prepareEvent);
13669                 this.el.un("keypress", this.relay);
13670             }
13671                     this.disabled = true;
13672                 }
13673         }
13674 };/*
13675  * Based on:
13676  * Ext JS Library 1.1.1
13677  * Copyright(c) 2006-2007, Ext JS, LLC.
13678  *
13679  * Originally Released Under LGPL - original licence link has changed is not relivant.
13680  *
13681  * Fork - LGPL
13682  * <script type="text/javascript">
13683  */
13684
13685  
13686 /**
13687  * @class Roo.KeyMap
13688  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13689  * The constructor accepts the same config object as defined by {@link #addBinding}.
13690  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13691  * combination it will call the function with this signature (if the match is a multi-key
13692  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13693  * A KeyMap can also handle a string representation of keys.<br />
13694  * Usage:
13695  <pre><code>
13696 // map one key by key code
13697 var map = new Roo.KeyMap("my-element", {
13698     key: 13, // or Roo.EventObject.ENTER
13699     fn: myHandler,
13700     scope: myObject
13701 });
13702
13703 // map multiple keys to one action by string
13704 var map = new Roo.KeyMap("my-element", {
13705     key: "a\r\n\t",
13706     fn: myHandler,
13707     scope: myObject
13708 });
13709
13710 // map multiple keys to multiple actions by strings and array of codes
13711 var map = new Roo.KeyMap("my-element", [
13712     {
13713         key: [10,13],
13714         fn: function(){ alert("Return was pressed"); }
13715     }, {
13716         key: "abc",
13717         fn: function(){ alert('a, b or c was pressed'); }
13718     }, {
13719         key: "\t",
13720         ctrl:true,
13721         shift:true,
13722         fn: function(){ alert('Control + shift + tab was pressed.'); }
13723     }
13724 ]);
13725 </code></pre>
13726  * <b>Note: A KeyMap starts enabled</b>
13727  * @constructor
13728  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13729  * @param {Object} config The config (see {@link #addBinding})
13730  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13731  */
13732 Roo.KeyMap = function(el, config, eventName){
13733     this.el  = Roo.get(el);
13734     this.eventName = eventName || "keydown";
13735     this.bindings = [];
13736     if(config){
13737         this.addBinding(config);
13738     }
13739     this.enable();
13740 };
13741
13742 Roo.KeyMap.prototype = {
13743     /**
13744      * True to stop the event from bubbling and prevent the default browser action if the
13745      * key was handled by the KeyMap (defaults to false)
13746      * @type Boolean
13747      */
13748     stopEvent : false,
13749
13750     /**
13751      * Add a new binding to this KeyMap. The following config object properties are supported:
13752      * <pre>
13753 Property    Type             Description
13754 ----------  ---------------  ----------------------------------------------------------------------
13755 key         String/Array     A single keycode or an array of keycodes to handle
13756 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13757 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13758 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13759 fn          Function         The function to call when KeyMap finds the expected key combination
13760 scope       Object           The scope of the callback function
13761 </pre>
13762      *
13763      * Usage:
13764      * <pre><code>
13765 // Create a KeyMap
13766 var map = new Roo.KeyMap(document, {
13767     key: Roo.EventObject.ENTER,
13768     fn: handleKey,
13769     scope: this
13770 });
13771
13772 //Add a new binding to the existing KeyMap later
13773 map.addBinding({
13774     key: 'abc',
13775     shift: true,
13776     fn: handleKey,
13777     scope: this
13778 });
13779 </code></pre>
13780      * @param {Object/Array} config A single KeyMap config or an array of configs
13781      */
13782         addBinding : function(config){
13783         if(config instanceof Array){
13784             for(var i = 0, len = config.length; i < len; i++){
13785                 this.addBinding(config[i]);
13786             }
13787             return;
13788         }
13789         var keyCode = config.key,
13790             shift = config.shift, 
13791             ctrl = config.ctrl, 
13792             alt = config.alt,
13793             fn = config.fn,
13794             scope = config.scope;
13795         if(typeof keyCode == "string"){
13796             var ks = [];
13797             var keyString = keyCode.toUpperCase();
13798             for(var j = 0, len = keyString.length; j < len; j++){
13799                 ks.push(keyString.charCodeAt(j));
13800             }
13801             keyCode = ks;
13802         }
13803         var keyArray = keyCode instanceof Array;
13804         var handler = function(e){
13805             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13806                 var k = e.getKey();
13807                 if(keyArray){
13808                     for(var i = 0, len = keyCode.length; i < len; i++){
13809                         if(keyCode[i] == k){
13810                           if(this.stopEvent){
13811                               e.stopEvent();
13812                           }
13813                           fn.call(scope || window, k, e);
13814                           return;
13815                         }
13816                     }
13817                 }else{
13818                     if(k == keyCode){
13819                         if(this.stopEvent){
13820                            e.stopEvent();
13821                         }
13822                         fn.call(scope || window, k, e);
13823                     }
13824                 }
13825             }
13826         };
13827         this.bindings.push(handler);  
13828         },
13829
13830     /**
13831      * Shorthand for adding a single key listener
13832      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13833      * following options:
13834      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13835      * @param {Function} fn The function to call
13836      * @param {Object} scope (optional) The scope of the function
13837      */
13838     on : function(key, fn, scope){
13839         var keyCode, shift, ctrl, alt;
13840         if(typeof key == "object" && !(key instanceof Array)){
13841             keyCode = key.key;
13842             shift = key.shift;
13843             ctrl = key.ctrl;
13844             alt = key.alt;
13845         }else{
13846             keyCode = key;
13847         }
13848         this.addBinding({
13849             key: keyCode,
13850             shift: shift,
13851             ctrl: ctrl,
13852             alt: alt,
13853             fn: fn,
13854             scope: scope
13855         })
13856     },
13857
13858     // private
13859     handleKeyDown : function(e){
13860             if(this.enabled){ //just in case
13861             var b = this.bindings;
13862             for(var i = 0, len = b.length; i < len; i++){
13863                 b[i].call(this, e);
13864             }
13865             }
13866         },
13867         
13868         /**
13869          * Returns true if this KeyMap is enabled
13870          * @return {Boolean} 
13871          */
13872         isEnabled : function(){
13873             return this.enabled;  
13874         },
13875         
13876         /**
13877          * Enables this KeyMap
13878          */
13879         enable: function(){
13880                 if(!this.enabled){
13881                     this.el.on(this.eventName, this.handleKeyDown, this);
13882                     this.enabled = true;
13883                 }
13884         },
13885
13886         /**
13887          * Disable this KeyMap
13888          */
13889         disable: function(){
13890                 if(this.enabled){
13891                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13892                     this.enabled = false;
13893                 }
13894         }
13895 };/*
13896  * Based on:
13897  * Ext JS Library 1.1.1
13898  * Copyright(c) 2006-2007, Ext JS, LLC.
13899  *
13900  * Originally Released Under LGPL - original licence link has changed is not relivant.
13901  *
13902  * Fork - LGPL
13903  * <script type="text/javascript">
13904  */
13905
13906  
13907 /**
13908  * @class Roo.util.TextMetrics
13909  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13910  * wide, in pixels, a given block of text will be.
13911  * @singleton
13912  */
13913 Roo.util.TextMetrics = function(){
13914     var shared;
13915     return {
13916         /**
13917          * Measures the size of the specified text
13918          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13919          * that can affect the size of the rendered text
13920          * @param {String} text The text to measure
13921          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13922          * in order to accurately measure the text height
13923          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13924          */
13925         measure : function(el, text, fixedWidth){
13926             if(!shared){
13927                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13928             }
13929             shared.bind(el);
13930             shared.setFixedWidth(fixedWidth || 'auto');
13931             return shared.getSize(text);
13932         },
13933
13934         /**
13935          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13936          * the overhead of multiple calls to initialize the style properties on each measurement.
13937          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13938          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13939          * in order to accurately measure the text height
13940          * @return {Roo.util.TextMetrics.Instance} instance The new instance
13941          */
13942         createInstance : function(el, fixedWidth){
13943             return Roo.util.TextMetrics.Instance(el, fixedWidth);
13944         }
13945     };
13946 }();
13947
13948  
13949
13950 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
13951     var ml = new Roo.Element(document.createElement('div'));
13952     document.body.appendChild(ml.dom);
13953     ml.position('absolute');
13954     ml.setLeftTop(-1000, -1000);
13955     ml.hide();
13956
13957     if(fixedWidth){
13958         ml.setWidth(fixedWidth);
13959     }
13960      
13961     var instance = {
13962         /**
13963          * Returns the size of the specified text based on the internal element's style and width properties
13964          * @memberOf Roo.util.TextMetrics.Instance#
13965          * @param {String} text The text to measure
13966          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13967          */
13968         getSize : function(text){
13969             ml.update(text);
13970             var s = ml.getSize();
13971             ml.update('');
13972             return s;
13973         },
13974
13975         /**
13976          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
13977          * that can affect the size of the rendered text
13978          * @memberOf Roo.util.TextMetrics.Instance#
13979          * @param {String/HTMLElement} el The element, dom node or id
13980          */
13981         bind : function(el){
13982             ml.setStyle(
13983                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
13984             );
13985         },
13986
13987         /**
13988          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
13989          * to set a fixed width in order to accurately measure the text height.
13990          * @memberOf Roo.util.TextMetrics.Instance#
13991          * @param {Number} width The width to set on the element
13992          */
13993         setFixedWidth : function(width){
13994             ml.setWidth(width);
13995         },
13996
13997         /**
13998          * Returns the measured width of the specified text
13999          * @memberOf Roo.util.TextMetrics.Instance#
14000          * @param {String} text The text to measure
14001          * @return {Number} width The width in pixels
14002          */
14003         getWidth : function(text){
14004             ml.dom.style.width = 'auto';
14005             return this.getSize(text).width;
14006         },
14007
14008         /**
14009          * Returns the measured height of the specified text.  For multiline text, be sure to call
14010          * {@link #setFixedWidth} if necessary.
14011          * @memberOf Roo.util.TextMetrics.Instance#
14012          * @param {String} text The text to measure
14013          * @return {Number} height The height in pixels
14014          */
14015         getHeight : function(text){
14016             return this.getSize(text).height;
14017         }
14018     };
14019
14020     instance.bind(bindTo);
14021
14022     return instance;
14023 };
14024
14025 // backwards compat
14026 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14027  * Based on:
14028  * Ext JS Library 1.1.1
14029  * Copyright(c) 2006-2007, Ext JS, LLC.
14030  *
14031  * Originally Released Under LGPL - original licence link has changed is not relivant.
14032  *
14033  * Fork - LGPL
14034  * <script type="text/javascript">
14035  */
14036
14037 /**
14038  * @class Roo.state.Provider
14039  * Abstract base class for state provider implementations. This class provides methods
14040  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14041  * Provider interface.
14042  */
14043 Roo.state.Provider = function(){
14044     /**
14045      * @event statechange
14046      * Fires when a state change occurs.
14047      * @param {Provider} this This state provider
14048      * @param {String} key The state key which was changed
14049      * @param {String} value The encoded value for the state
14050      */
14051     this.addEvents({
14052         "statechange": true
14053     });
14054     this.state = {};
14055     Roo.state.Provider.superclass.constructor.call(this);
14056 };
14057 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14058     /**
14059      * Returns the current value for a key
14060      * @param {String} name The key name
14061      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14062      * @return {Mixed} The state data
14063      */
14064     get : function(name, defaultValue){
14065         return typeof this.state[name] == "undefined" ?
14066             defaultValue : this.state[name];
14067     },
14068     
14069     /**
14070      * Clears a value from the state
14071      * @param {String} name The key name
14072      */
14073     clear : function(name){
14074         delete this.state[name];
14075         this.fireEvent("statechange", this, name, null);
14076     },
14077     
14078     /**
14079      * Sets the value for a key
14080      * @param {String} name The key name
14081      * @param {Mixed} value The value to set
14082      */
14083     set : function(name, value){
14084         this.state[name] = value;
14085         this.fireEvent("statechange", this, name, value);
14086     },
14087     
14088     /**
14089      * Decodes a string previously encoded with {@link #encodeValue}.
14090      * @param {String} value The value to decode
14091      * @return {Mixed} The decoded value
14092      */
14093     decodeValue : function(cookie){
14094         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14095         var matches = re.exec(unescape(cookie));
14096         if(!matches || !matches[1]) return; // non state cookie
14097         var type = matches[1];
14098         var v = matches[2];
14099         switch(type){
14100             case "n":
14101                 return parseFloat(v);
14102             case "d":
14103                 return new Date(Date.parse(v));
14104             case "b":
14105                 return (v == "1");
14106             case "a":
14107                 var all = [];
14108                 var values = v.split("^");
14109                 for(var i = 0, len = values.length; i < len; i++){
14110                     all.push(this.decodeValue(values[i]));
14111                 }
14112                 return all;
14113            case "o":
14114                 var all = {};
14115                 var values = v.split("^");
14116                 for(var i = 0, len = values.length; i < len; i++){
14117                     var kv = values[i].split("=");
14118                     all[kv[0]] = this.decodeValue(kv[1]);
14119                 }
14120                 return all;
14121            default:
14122                 return v;
14123         }
14124     },
14125     
14126     /**
14127      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14128      * @param {Mixed} value The value to encode
14129      * @return {String} The encoded value
14130      */
14131     encodeValue : function(v){
14132         var enc;
14133         if(typeof v == "number"){
14134             enc = "n:" + v;
14135         }else if(typeof v == "boolean"){
14136             enc = "b:" + (v ? "1" : "0");
14137         }else if(v instanceof Date){
14138             enc = "d:" + v.toGMTString();
14139         }else if(v instanceof Array){
14140             var flat = "";
14141             for(var i = 0, len = v.length; i < len; i++){
14142                 flat += this.encodeValue(v[i]);
14143                 if(i != len-1) flat += "^";
14144             }
14145             enc = "a:" + flat;
14146         }else if(typeof v == "object"){
14147             var flat = "";
14148             for(var key in v){
14149                 if(typeof v[key] != "function"){
14150                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14151                 }
14152             }
14153             enc = "o:" + flat.substring(0, flat.length-1);
14154         }else{
14155             enc = "s:" + v;
14156         }
14157         return escape(enc);        
14158     }
14159 });
14160
14161 /*
14162  * Based on:
14163  * Ext JS Library 1.1.1
14164  * Copyright(c) 2006-2007, Ext JS, LLC.
14165  *
14166  * Originally Released Under LGPL - original licence link has changed is not relivant.
14167  *
14168  * Fork - LGPL
14169  * <script type="text/javascript">
14170  */
14171 /**
14172  * @class Roo.state.Manager
14173  * This is the global state manager. By default all components that are "state aware" check this class
14174  * for state information if you don't pass them a custom state provider. In order for this class
14175  * to be useful, it must be initialized with a provider when your application initializes.
14176  <pre><code>
14177 // in your initialization function
14178 init : function(){
14179    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14180    ...
14181    // supposed you have a {@link Roo.BorderLayout}
14182    var layout = new Roo.BorderLayout(...);
14183    layout.restoreState();
14184    // or a {Roo.BasicDialog}
14185    var dialog = new Roo.BasicDialog(...);
14186    dialog.restoreState();
14187  </code></pre>
14188  * @singleton
14189  */
14190 Roo.state.Manager = function(){
14191     var provider = new Roo.state.Provider();
14192     
14193     return {
14194         /**
14195          * Configures the default state provider for your application
14196          * @param {Provider} stateProvider The state provider to set
14197          */
14198         setProvider : function(stateProvider){
14199             provider = stateProvider;
14200         },
14201         
14202         /**
14203          * Returns the current value for a key
14204          * @param {String} name The key name
14205          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14206          * @return {Mixed} The state data
14207          */
14208         get : function(key, defaultValue){
14209             return provider.get(key, defaultValue);
14210         },
14211         
14212         /**
14213          * Sets the value for a key
14214          * @param {String} name The key name
14215          * @param {Mixed} value The state data
14216          */
14217          set : function(key, value){
14218             provider.set(key, value);
14219         },
14220         
14221         /**
14222          * Clears a value from the state
14223          * @param {String} name The key name
14224          */
14225         clear : function(key){
14226             provider.clear(key);
14227         },
14228         
14229         /**
14230          * Gets the currently configured state provider
14231          * @return {Provider} The state provider
14232          */
14233         getProvider : function(){
14234             return provider;
14235         }
14236     };
14237 }();
14238 /*
14239  * Based on:
14240  * Ext JS Library 1.1.1
14241  * Copyright(c) 2006-2007, Ext JS, LLC.
14242  *
14243  * Originally Released Under LGPL - original licence link has changed is not relivant.
14244  *
14245  * Fork - LGPL
14246  * <script type="text/javascript">
14247  */
14248 /**
14249  * @class Roo.state.CookieProvider
14250  * @extends Roo.state.Provider
14251  * The default Provider implementation which saves state via cookies.
14252  * <br />Usage:
14253  <pre><code>
14254    var cp = new Roo.state.CookieProvider({
14255        path: "/cgi-bin/",
14256        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14257        domain: "roojs.com"
14258    })
14259    Roo.state.Manager.setProvider(cp);
14260  </code></pre>
14261  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14262  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14263  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14264  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14265  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14266  * domain the page is running on including the 'www' like 'www.roojs.com')
14267  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14268  * @constructor
14269  * Create a new CookieProvider
14270  * @param {Object} config The configuration object
14271  */
14272 Roo.state.CookieProvider = function(config){
14273     Roo.state.CookieProvider.superclass.constructor.call(this);
14274     this.path = "/";
14275     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14276     this.domain = null;
14277     this.secure = false;
14278     Roo.apply(this, config);
14279     this.state = this.readCookies();
14280 };
14281
14282 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14283     // private
14284     set : function(name, value){
14285         if(typeof value == "undefined" || value === null){
14286             this.clear(name);
14287             return;
14288         }
14289         this.setCookie(name, value);
14290         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14291     },
14292
14293     // private
14294     clear : function(name){
14295         this.clearCookie(name);
14296         Roo.state.CookieProvider.superclass.clear.call(this, name);
14297     },
14298
14299     // private
14300     readCookies : function(){
14301         var cookies = {};
14302         var c = document.cookie + ";";
14303         var re = /\s?(.*?)=(.*?);/g;
14304         var matches;
14305         while((matches = re.exec(c)) != null){
14306             var name = matches[1];
14307             var value = matches[2];
14308             if(name && name.substring(0,3) == "ys-"){
14309                 cookies[name.substr(3)] = this.decodeValue(value);
14310             }
14311         }
14312         return cookies;
14313     },
14314
14315     // private
14316     setCookie : function(name, value){
14317         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14318            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14319            ((this.path == null) ? "" : ("; path=" + this.path)) +
14320            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14321            ((this.secure == true) ? "; secure" : "");
14322     },
14323
14324     // private
14325     clearCookie : function(name){
14326         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14327            ((this.path == null) ? "" : ("; path=" + this.path)) +
14328            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14329            ((this.secure == true) ? "; secure" : "");
14330     }
14331 });/*
14332  * Based on:
14333  * Ext JS Library 1.1.1
14334  * Copyright(c) 2006-2007, Ext JS, LLC.
14335  *
14336  * Originally Released Under LGPL - original licence link has changed is not relivant.
14337  *
14338  * Fork - LGPL
14339  * <script type="text/javascript">
14340  */
14341
14342
14343
14344 /*
14345  * These classes are derivatives of the similarly named classes in the YUI Library.
14346  * The original license:
14347  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14348  * Code licensed under the BSD License:
14349  * http://developer.yahoo.net/yui/license.txt
14350  */
14351
14352 (function() {
14353
14354 var Event=Roo.EventManager;
14355 var Dom=Roo.lib.Dom;
14356
14357 /**
14358  * @class Roo.dd.DragDrop
14359  * Defines the interface and base operation of items that that can be
14360  * dragged or can be drop targets.  It was designed to be extended, overriding
14361  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14362  * Up to three html elements can be associated with a DragDrop instance:
14363  * <ul>
14364  * <li>linked element: the element that is passed into the constructor.
14365  * This is the element which defines the boundaries for interaction with
14366  * other DragDrop objects.</li>
14367  * <li>handle element(s): The drag operation only occurs if the element that
14368  * was clicked matches a handle element.  By default this is the linked
14369  * element, but there are times that you will want only a portion of the
14370  * linked element to initiate the drag operation, and the setHandleElId()
14371  * method provides a way to define this.</li>
14372  * <li>drag element: this represents the element that would be moved along
14373  * with the cursor during a drag operation.  By default, this is the linked
14374  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14375  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14376  * </li>
14377  * </ul>
14378  * This class should not be instantiated until the onload event to ensure that
14379  * the associated elements are available.
14380  * The following would define a DragDrop obj that would interact with any
14381  * other DragDrop obj in the "group1" group:
14382  * <pre>
14383  *  dd = new Roo.dd.DragDrop("div1", "group1");
14384  * </pre>
14385  * Since none of the event handlers have been implemented, nothing would
14386  * actually happen if you were to run the code above.  Normally you would
14387  * override this class or one of the default implementations, but you can
14388  * also override the methods you want on an instance of the class...
14389  * <pre>
14390  *  dd.onDragDrop = function(e, id) {
14391  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14392  *  }
14393  * </pre>
14394  * @constructor
14395  * @param {String} id of the element that is linked to this instance
14396  * @param {String} sGroup the group of related DragDrop objects
14397  * @param {object} config an object containing configurable attributes
14398  *                Valid properties for DragDrop:
14399  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14400  */
14401 Roo.dd.DragDrop = function(id, sGroup, config) {
14402     if (id) {
14403         this.init(id, sGroup, config);
14404     }
14405 };
14406
14407 Roo.dd.DragDrop.prototype = {
14408
14409     /**
14410      * The id of the element associated with this object.  This is what we
14411      * refer to as the "linked element" because the size and position of
14412      * this element is used to determine when the drag and drop objects have
14413      * interacted.
14414      * @property id
14415      * @type String
14416      */
14417     id: null,
14418
14419     /**
14420      * Configuration attributes passed into the constructor
14421      * @property config
14422      * @type object
14423      */
14424     config: null,
14425
14426     /**
14427      * The id of the element that will be dragged.  By default this is same
14428      * as the linked element , but could be changed to another element. Ex:
14429      * Roo.dd.DDProxy
14430      * @property dragElId
14431      * @type String
14432      * @private
14433      */
14434     dragElId: null,
14435
14436     /**
14437      * the id of the element that initiates the drag operation.  By default
14438      * this is the linked element, but could be changed to be a child of this
14439      * element.  This lets us do things like only starting the drag when the
14440      * header element within the linked html element is clicked.
14441      * @property handleElId
14442      * @type String
14443      * @private
14444      */
14445     handleElId: null,
14446
14447     /**
14448      * An associative array of HTML tags that will be ignored if clicked.
14449      * @property invalidHandleTypes
14450      * @type {string: string}
14451      */
14452     invalidHandleTypes: null,
14453
14454     /**
14455      * An associative array of ids for elements that will be ignored if clicked
14456      * @property invalidHandleIds
14457      * @type {string: string}
14458      */
14459     invalidHandleIds: null,
14460
14461     /**
14462      * An indexted array of css class names for elements that will be ignored
14463      * if clicked.
14464      * @property invalidHandleClasses
14465      * @type string[]
14466      */
14467     invalidHandleClasses: null,
14468
14469     /**
14470      * The linked element's absolute X position at the time the drag was
14471      * started
14472      * @property startPageX
14473      * @type int
14474      * @private
14475      */
14476     startPageX: 0,
14477
14478     /**
14479      * The linked element's absolute X position at the time the drag was
14480      * started
14481      * @property startPageY
14482      * @type int
14483      * @private
14484      */
14485     startPageY: 0,
14486
14487     /**
14488      * The group defines a logical collection of DragDrop objects that are
14489      * related.  Instances only get events when interacting with other
14490      * DragDrop object in the same group.  This lets us define multiple
14491      * groups using a single DragDrop subclass if we want.
14492      * @property groups
14493      * @type {string: string}
14494      */
14495     groups: null,
14496
14497     /**
14498      * Individual drag/drop instances can be locked.  This will prevent
14499      * onmousedown start drag.
14500      * @property locked
14501      * @type boolean
14502      * @private
14503      */
14504     locked: false,
14505
14506     /**
14507      * Lock this instance
14508      * @method lock
14509      */
14510     lock: function() { this.locked = true; },
14511
14512     /**
14513      * Unlock this instace
14514      * @method unlock
14515      */
14516     unlock: function() { this.locked = false; },
14517
14518     /**
14519      * By default, all insances can be a drop target.  This can be disabled by
14520      * setting isTarget to false.
14521      * @method isTarget
14522      * @type boolean
14523      */
14524     isTarget: true,
14525
14526     /**
14527      * The padding configured for this drag and drop object for calculating
14528      * the drop zone intersection with this object.
14529      * @method padding
14530      * @type int[]
14531      */
14532     padding: null,
14533
14534     /**
14535      * Cached reference to the linked element
14536      * @property _domRef
14537      * @private
14538      */
14539     _domRef: null,
14540
14541     /**
14542      * Internal typeof flag
14543      * @property __ygDragDrop
14544      * @private
14545      */
14546     __ygDragDrop: true,
14547
14548     /**
14549      * Set to true when horizontal contraints are applied
14550      * @property constrainX
14551      * @type boolean
14552      * @private
14553      */
14554     constrainX: false,
14555
14556     /**
14557      * Set to true when vertical contraints are applied
14558      * @property constrainY
14559      * @type boolean
14560      * @private
14561      */
14562     constrainY: false,
14563
14564     /**
14565      * The left constraint
14566      * @property minX
14567      * @type int
14568      * @private
14569      */
14570     minX: 0,
14571
14572     /**
14573      * The right constraint
14574      * @property maxX
14575      * @type int
14576      * @private
14577      */
14578     maxX: 0,
14579
14580     /**
14581      * The up constraint
14582      * @property minY
14583      * @type int
14584      * @type int
14585      * @private
14586      */
14587     minY: 0,
14588
14589     /**
14590      * The down constraint
14591      * @property maxY
14592      * @type int
14593      * @private
14594      */
14595     maxY: 0,
14596
14597     /**
14598      * Maintain offsets when we resetconstraints.  Set to true when you want
14599      * the position of the element relative to its parent to stay the same
14600      * when the page changes
14601      *
14602      * @property maintainOffset
14603      * @type boolean
14604      */
14605     maintainOffset: false,
14606
14607     /**
14608      * Array of pixel locations the element will snap to if we specified a
14609      * horizontal graduation/interval.  This array is generated automatically
14610      * when you define a tick interval.
14611      * @property xTicks
14612      * @type int[]
14613      */
14614     xTicks: null,
14615
14616     /**
14617      * Array of pixel locations the element will snap to if we specified a
14618      * vertical graduation/interval.  This array is generated automatically
14619      * when you define a tick interval.
14620      * @property yTicks
14621      * @type int[]
14622      */
14623     yTicks: null,
14624
14625     /**
14626      * By default the drag and drop instance will only respond to the primary
14627      * button click (left button for a right-handed mouse).  Set to true to
14628      * allow drag and drop to start with any mouse click that is propogated
14629      * by the browser
14630      * @property primaryButtonOnly
14631      * @type boolean
14632      */
14633     primaryButtonOnly: true,
14634
14635     /**
14636      * The availabe property is false until the linked dom element is accessible.
14637      * @property available
14638      * @type boolean
14639      */
14640     available: false,
14641
14642     /**
14643      * By default, drags can only be initiated if the mousedown occurs in the
14644      * region the linked element is.  This is done in part to work around a
14645      * bug in some browsers that mis-report the mousedown if the previous
14646      * mouseup happened outside of the window.  This property is set to true
14647      * if outer handles are defined.
14648      *
14649      * @property hasOuterHandles
14650      * @type boolean
14651      * @default false
14652      */
14653     hasOuterHandles: false,
14654
14655     /**
14656      * Code that executes immediately before the startDrag event
14657      * @method b4StartDrag
14658      * @private
14659      */
14660     b4StartDrag: function(x, y) { },
14661
14662     /**
14663      * Abstract method called after a drag/drop object is clicked
14664      * and the drag or mousedown time thresholds have beeen met.
14665      * @method startDrag
14666      * @param {int} X click location
14667      * @param {int} Y click location
14668      */
14669     startDrag: function(x, y) { /* override this */ },
14670
14671     /**
14672      * Code that executes immediately before the onDrag event
14673      * @method b4Drag
14674      * @private
14675      */
14676     b4Drag: function(e) { },
14677
14678     /**
14679      * Abstract method called during the onMouseMove event while dragging an
14680      * object.
14681      * @method onDrag
14682      * @param {Event} e the mousemove event
14683      */
14684     onDrag: function(e) { /* override this */ },
14685
14686     /**
14687      * Abstract method called when this element fist begins hovering over
14688      * another DragDrop obj
14689      * @method onDragEnter
14690      * @param {Event} e the mousemove event
14691      * @param {String|DragDrop[]} id In POINT mode, the element
14692      * id this is hovering over.  In INTERSECT mode, an array of one or more
14693      * dragdrop items being hovered over.
14694      */
14695     onDragEnter: function(e, id) { /* override this */ },
14696
14697     /**
14698      * Code that executes immediately before the onDragOver event
14699      * @method b4DragOver
14700      * @private
14701      */
14702     b4DragOver: function(e) { },
14703
14704     /**
14705      * Abstract method called when this element is hovering over another
14706      * DragDrop obj
14707      * @method onDragOver
14708      * @param {Event} e the mousemove event
14709      * @param {String|DragDrop[]} id In POINT mode, the element
14710      * id this is hovering over.  In INTERSECT mode, an array of dd items
14711      * being hovered over.
14712      */
14713     onDragOver: function(e, id) { /* override this */ },
14714
14715     /**
14716      * Code that executes immediately before the onDragOut event
14717      * @method b4DragOut
14718      * @private
14719      */
14720     b4DragOut: function(e) { },
14721
14722     /**
14723      * Abstract method called when we are no longer hovering over an element
14724      * @method onDragOut
14725      * @param {Event} e the mousemove event
14726      * @param {String|DragDrop[]} id In POINT mode, the element
14727      * id this was hovering over.  In INTERSECT mode, an array of dd items
14728      * that the mouse is no longer over.
14729      */
14730     onDragOut: function(e, id) { /* override this */ },
14731
14732     /**
14733      * Code that executes immediately before the onDragDrop event
14734      * @method b4DragDrop
14735      * @private
14736      */
14737     b4DragDrop: function(e) { },
14738
14739     /**
14740      * Abstract method called when this item is dropped on another DragDrop
14741      * obj
14742      * @method onDragDrop
14743      * @param {Event} e the mouseup event
14744      * @param {String|DragDrop[]} id In POINT mode, the element
14745      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14746      * was dropped on.
14747      */
14748     onDragDrop: function(e, id) { /* override this */ },
14749
14750     /**
14751      * Abstract method called when this item is dropped on an area with no
14752      * drop target
14753      * @method onInvalidDrop
14754      * @param {Event} e the mouseup event
14755      */
14756     onInvalidDrop: function(e) { /* override this */ },
14757
14758     /**
14759      * Code that executes immediately before the endDrag event
14760      * @method b4EndDrag
14761      * @private
14762      */
14763     b4EndDrag: function(e) { },
14764
14765     /**
14766      * Fired when we are done dragging the object
14767      * @method endDrag
14768      * @param {Event} e the mouseup event
14769      */
14770     endDrag: function(e) { /* override this */ },
14771
14772     /**
14773      * Code executed immediately before the onMouseDown event
14774      * @method b4MouseDown
14775      * @param {Event} e the mousedown event
14776      * @private
14777      */
14778     b4MouseDown: function(e) {  },
14779
14780     /**
14781      * Event handler that fires when a drag/drop obj gets a mousedown
14782      * @method onMouseDown
14783      * @param {Event} e the mousedown event
14784      */
14785     onMouseDown: function(e) { /* override this */ },
14786
14787     /**
14788      * Event handler that fires when a drag/drop obj gets a mouseup
14789      * @method onMouseUp
14790      * @param {Event} e the mouseup event
14791      */
14792     onMouseUp: function(e) { /* override this */ },
14793
14794     /**
14795      * Override the onAvailable method to do what is needed after the initial
14796      * position was determined.
14797      * @method onAvailable
14798      */
14799     onAvailable: function () {
14800     },
14801
14802     /*
14803      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14804      * @type Object
14805      */
14806     defaultPadding : {left:0, right:0, top:0, bottom:0},
14807
14808     /*
14809      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14810  *
14811  * Usage:
14812  <pre><code>
14813  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14814                 { dragElId: "existingProxyDiv" });
14815  dd.startDrag = function(){
14816      this.constrainTo("parent-id");
14817  };
14818  </code></pre>
14819  * Or you can initalize it using the {@link Roo.Element} object:
14820  <pre><code>
14821  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14822      startDrag : function(){
14823          this.constrainTo("parent-id");
14824      }
14825  });
14826  </code></pre>
14827      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14828      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14829      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14830      * an object containing the sides to pad. For example: {right:10, bottom:10}
14831      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14832      */
14833     constrainTo : function(constrainTo, pad, inContent){
14834         if(typeof pad == "number"){
14835             pad = {left: pad, right:pad, top:pad, bottom:pad};
14836         }
14837         pad = pad || this.defaultPadding;
14838         var b = Roo.get(this.getEl()).getBox();
14839         var ce = Roo.get(constrainTo);
14840         var s = ce.getScroll();
14841         var c, cd = ce.dom;
14842         if(cd == document.body){
14843             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14844         }else{
14845             xy = ce.getXY();
14846             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14847         }
14848
14849
14850         var topSpace = b.y - c.y;
14851         var leftSpace = b.x - c.x;
14852
14853         this.resetConstraints();
14854         this.setXConstraint(leftSpace - (pad.left||0), // left
14855                 c.width - leftSpace - b.width - (pad.right||0) //right
14856         );
14857         this.setYConstraint(topSpace - (pad.top||0), //top
14858                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14859         );
14860     },
14861
14862     /**
14863      * Returns a reference to the linked element
14864      * @method getEl
14865      * @return {HTMLElement} the html element
14866      */
14867     getEl: function() {
14868         if (!this._domRef) {
14869             this._domRef = Roo.getDom(this.id);
14870         }
14871
14872         return this._domRef;
14873     },
14874
14875     /**
14876      * Returns a reference to the actual element to drag.  By default this is
14877      * the same as the html element, but it can be assigned to another
14878      * element. An example of this can be found in Roo.dd.DDProxy
14879      * @method getDragEl
14880      * @return {HTMLElement} the html element
14881      */
14882     getDragEl: function() {
14883         return Roo.getDom(this.dragElId);
14884     },
14885
14886     /**
14887      * Sets up the DragDrop object.  Must be called in the constructor of any
14888      * Roo.dd.DragDrop subclass
14889      * @method init
14890      * @param id the id of the linked element
14891      * @param {String} sGroup the group of related items
14892      * @param {object} config configuration attributes
14893      */
14894     init: function(id, sGroup, config) {
14895         this.initTarget(id, sGroup, config);
14896         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14897         // Event.on(this.id, "selectstart", Event.preventDefault);
14898     },
14899
14900     /**
14901      * Initializes Targeting functionality only... the object does not
14902      * get a mousedown handler.
14903      * @method initTarget
14904      * @param id the id of the linked element
14905      * @param {String} sGroup the group of related items
14906      * @param {object} config configuration attributes
14907      */
14908     initTarget: function(id, sGroup, config) {
14909
14910         // configuration attributes
14911         this.config = config || {};
14912
14913         // create a local reference to the drag and drop manager
14914         this.DDM = Roo.dd.DDM;
14915         // initialize the groups array
14916         this.groups = {};
14917
14918         // assume that we have an element reference instead of an id if the
14919         // parameter is not a string
14920         if (typeof id !== "string") {
14921             id = Roo.id(id);
14922         }
14923
14924         // set the id
14925         this.id = id;
14926
14927         // add to an interaction group
14928         this.addToGroup((sGroup) ? sGroup : "default");
14929
14930         // We don't want to register this as the handle with the manager
14931         // so we just set the id rather than calling the setter.
14932         this.handleElId = id;
14933
14934         // the linked element is the element that gets dragged by default
14935         this.setDragElId(id);
14936
14937         // by default, clicked anchors will not start drag operations.
14938         this.invalidHandleTypes = { A: "A" };
14939         this.invalidHandleIds = {};
14940         this.invalidHandleClasses = [];
14941
14942         this.applyConfig();
14943
14944         this.handleOnAvailable();
14945     },
14946
14947     /**
14948      * Applies the configuration parameters that were passed into the constructor.
14949      * This is supposed to happen at each level through the inheritance chain.  So
14950      * a DDProxy implentation will execute apply config on DDProxy, DD, and
14951      * DragDrop in order to get all of the parameters that are available in
14952      * each object.
14953      * @method applyConfig
14954      */
14955     applyConfig: function() {
14956
14957         // configurable properties:
14958         //    padding, isTarget, maintainOffset, primaryButtonOnly
14959         this.padding           = this.config.padding || [0, 0, 0, 0];
14960         this.isTarget          = (this.config.isTarget !== false);
14961         this.maintainOffset    = (this.config.maintainOffset);
14962         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
14963
14964     },
14965
14966     /**
14967      * Executed when the linked element is available
14968      * @method handleOnAvailable
14969      * @private
14970      */
14971     handleOnAvailable: function() {
14972         this.available = true;
14973         this.resetConstraints();
14974         this.onAvailable();
14975     },
14976
14977      /**
14978      * Configures the padding for the target zone in px.  Effectively expands
14979      * (or reduces) the virtual object size for targeting calculations.
14980      * Supports css-style shorthand; if only one parameter is passed, all sides
14981      * will have that padding, and if only two are passed, the top and bottom
14982      * will have the first param, the left and right the second.
14983      * @method setPadding
14984      * @param {int} iTop    Top pad
14985      * @param {int} iRight  Right pad
14986      * @param {int} iBot    Bot pad
14987      * @param {int} iLeft   Left pad
14988      */
14989     setPadding: function(iTop, iRight, iBot, iLeft) {
14990         // this.padding = [iLeft, iRight, iTop, iBot];
14991         if (!iRight && 0 !== iRight) {
14992             this.padding = [iTop, iTop, iTop, iTop];
14993         } else if (!iBot && 0 !== iBot) {
14994             this.padding = [iTop, iRight, iTop, iRight];
14995         } else {
14996             this.padding = [iTop, iRight, iBot, iLeft];
14997         }
14998     },
14999
15000     /**
15001      * Stores the initial placement of the linked element.
15002      * @method setInitialPosition
15003      * @param {int} diffX   the X offset, default 0
15004      * @param {int} diffY   the Y offset, default 0
15005      */
15006     setInitPosition: function(diffX, diffY) {
15007         var el = this.getEl();
15008
15009         if (!this.DDM.verifyEl(el)) {
15010             return;
15011         }
15012
15013         var dx = diffX || 0;
15014         var dy = diffY || 0;
15015
15016         var p = Dom.getXY( el );
15017
15018         this.initPageX = p[0] - dx;
15019         this.initPageY = p[1] - dy;
15020
15021         this.lastPageX = p[0];
15022         this.lastPageY = p[1];
15023
15024
15025         this.setStartPosition(p);
15026     },
15027
15028     /**
15029      * Sets the start position of the element.  This is set when the obj
15030      * is initialized, the reset when a drag is started.
15031      * @method setStartPosition
15032      * @param pos current position (from previous lookup)
15033      * @private
15034      */
15035     setStartPosition: function(pos) {
15036         var p = pos || Dom.getXY( this.getEl() );
15037         this.deltaSetXY = null;
15038
15039         this.startPageX = p[0];
15040         this.startPageY = p[1];
15041     },
15042
15043     /**
15044      * Add this instance to a group of related drag/drop objects.  All
15045      * instances belong to at least one group, and can belong to as many
15046      * groups as needed.
15047      * @method addToGroup
15048      * @param sGroup {string} the name of the group
15049      */
15050     addToGroup: function(sGroup) {
15051         this.groups[sGroup] = true;
15052         this.DDM.regDragDrop(this, sGroup);
15053     },
15054
15055     /**
15056      * Remove's this instance from the supplied interaction group
15057      * @method removeFromGroup
15058      * @param {string}  sGroup  The group to drop
15059      */
15060     removeFromGroup: function(sGroup) {
15061         if (this.groups[sGroup]) {
15062             delete this.groups[sGroup];
15063         }
15064
15065         this.DDM.removeDDFromGroup(this, sGroup);
15066     },
15067
15068     /**
15069      * Allows you to specify that an element other than the linked element
15070      * will be moved with the cursor during a drag
15071      * @method setDragElId
15072      * @param id {string} the id of the element that will be used to initiate the drag
15073      */
15074     setDragElId: function(id) {
15075         this.dragElId = id;
15076     },
15077
15078     /**
15079      * Allows you to specify a child of the linked element that should be
15080      * used to initiate the drag operation.  An example of this would be if
15081      * you have a content div with text and links.  Clicking anywhere in the
15082      * content area would normally start the drag operation.  Use this method
15083      * to specify that an element inside of the content div is the element
15084      * that starts the drag operation.
15085      * @method setHandleElId
15086      * @param id {string} the id of the element that will be used to
15087      * initiate the drag.
15088      */
15089     setHandleElId: function(id) {
15090         if (typeof id !== "string") {
15091             id = Roo.id(id);
15092         }
15093         this.handleElId = id;
15094         this.DDM.regHandle(this.id, id);
15095     },
15096
15097     /**
15098      * Allows you to set an element outside of the linked element as a drag
15099      * handle
15100      * @method setOuterHandleElId
15101      * @param id the id of the element that will be used to initiate the drag
15102      */
15103     setOuterHandleElId: function(id) {
15104         if (typeof id !== "string") {
15105             id = Roo.id(id);
15106         }
15107         Event.on(id, "mousedown",
15108                 this.handleMouseDown, this);
15109         this.setHandleElId(id);
15110
15111         this.hasOuterHandles = true;
15112     },
15113
15114     /**
15115      * Remove all drag and drop hooks for this element
15116      * @method unreg
15117      */
15118     unreg: function() {
15119         Event.un(this.id, "mousedown",
15120                 this.handleMouseDown);
15121         this._domRef = null;
15122         this.DDM._remove(this);
15123     },
15124
15125     destroy : function(){
15126         this.unreg();
15127     },
15128
15129     /**
15130      * Returns true if this instance is locked, or the drag drop mgr is locked
15131      * (meaning that all drag/drop is disabled on the page.)
15132      * @method isLocked
15133      * @return {boolean} true if this obj or all drag/drop is locked, else
15134      * false
15135      */
15136     isLocked: function() {
15137         return (this.DDM.isLocked() || this.locked);
15138     },
15139
15140     /**
15141      * Fired when this object is clicked
15142      * @method handleMouseDown
15143      * @param {Event} e
15144      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15145      * @private
15146      */
15147     handleMouseDown: function(e, oDD){
15148         if (this.primaryButtonOnly && e.button != 0) {
15149             return;
15150         }
15151
15152         if (this.isLocked()) {
15153             return;
15154         }
15155
15156         this.DDM.refreshCache(this.groups);
15157
15158         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15159         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15160         } else {
15161             if (this.clickValidator(e)) {
15162
15163                 // set the initial element position
15164                 this.setStartPosition();
15165
15166
15167                 this.b4MouseDown(e);
15168                 this.onMouseDown(e);
15169
15170                 this.DDM.handleMouseDown(e, this);
15171
15172                 this.DDM.stopEvent(e);
15173             } else {
15174
15175
15176             }
15177         }
15178     },
15179
15180     clickValidator: function(e) {
15181         var target = e.getTarget();
15182         return ( this.isValidHandleChild(target) &&
15183                     (this.id == this.handleElId ||
15184                         this.DDM.handleWasClicked(target, this.id)) );
15185     },
15186
15187     /**
15188      * Allows you to specify a tag name that should not start a drag operation
15189      * when clicked.  This is designed to facilitate embedding links within a
15190      * drag handle that do something other than start the drag.
15191      * @method addInvalidHandleType
15192      * @param {string} tagName the type of element to exclude
15193      */
15194     addInvalidHandleType: function(tagName) {
15195         var type = tagName.toUpperCase();
15196         this.invalidHandleTypes[type] = type;
15197     },
15198
15199     /**
15200      * Lets you to specify an element id for a child of a drag handle
15201      * that should not initiate a drag
15202      * @method addInvalidHandleId
15203      * @param {string} id the element id of the element you wish to ignore
15204      */
15205     addInvalidHandleId: function(id) {
15206         if (typeof id !== "string") {
15207             id = Roo.id(id);
15208         }
15209         this.invalidHandleIds[id] = id;
15210     },
15211
15212     /**
15213      * Lets you specify a css class of elements that will not initiate a drag
15214      * @method addInvalidHandleClass
15215      * @param {string} cssClass the class of the elements you wish to ignore
15216      */
15217     addInvalidHandleClass: function(cssClass) {
15218         this.invalidHandleClasses.push(cssClass);
15219     },
15220
15221     /**
15222      * Unsets an excluded tag name set by addInvalidHandleType
15223      * @method removeInvalidHandleType
15224      * @param {string} tagName the type of element to unexclude
15225      */
15226     removeInvalidHandleType: function(tagName) {
15227         var type = tagName.toUpperCase();
15228         // this.invalidHandleTypes[type] = null;
15229         delete this.invalidHandleTypes[type];
15230     },
15231
15232     /**
15233      * Unsets an invalid handle id
15234      * @method removeInvalidHandleId
15235      * @param {string} id the id of the element to re-enable
15236      */
15237     removeInvalidHandleId: function(id) {
15238         if (typeof id !== "string") {
15239             id = Roo.id(id);
15240         }
15241         delete this.invalidHandleIds[id];
15242     },
15243
15244     /**
15245      * Unsets an invalid css class
15246      * @method removeInvalidHandleClass
15247      * @param {string} cssClass the class of the element(s) you wish to
15248      * re-enable
15249      */
15250     removeInvalidHandleClass: function(cssClass) {
15251         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15252             if (this.invalidHandleClasses[i] == cssClass) {
15253                 delete this.invalidHandleClasses[i];
15254             }
15255         }
15256     },
15257
15258     /**
15259      * Checks the tag exclusion list to see if this click should be ignored
15260      * @method isValidHandleChild
15261      * @param {HTMLElement} node the HTMLElement to evaluate
15262      * @return {boolean} true if this is a valid tag type, false if not
15263      */
15264     isValidHandleChild: function(node) {
15265
15266         var valid = true;
15267         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15268         var nodeName;
15269         try {
15270             nodeName = node.nodeName.toUpperCase();
15271         } catch(e) {
15272             nodeName = node.nodeName;
15273         }
15274         valid = valid && !this.invalidHandleTypes[nodeName];
15275         valid = valid && !this.invalidHandleIds[node.id];
15276
15277         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15278             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15279         }
15280
15281
15282         return valid;
15283
15284     },
15285
15286     /**
15287      * Create the array of horizontal tick marks if an interval was specified
15288      * in setXConstraint().
15289      * @method setXTicks
15290      * @private
15291      */
15292     setXTicks: function(iStartX, iTickSize) {
15293         this.xTicks = [];
15294         this.xTickSize = iTickSize;
15295
15296         var tickMap = {};
15297
15298         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15299             if (!tickMap[i]) {
15300                 this.xTicks[this.xTicks.length] = i;
15301                 tickMap[i] = true;
15302             }
15303         }
15304
15305         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15306             if (!tickMap[i]) {
15307                 this.xTicks[this.xTicks.length] = i;
15308                 tickMap[i] = true;
15309             }
15310         }
15311
15312         this.xTicks.sort(this.DDM.numericSort) ;
15313     },
15314
15315     /**
15316      * Create the array of vertical tick marks if an interval was specified in
15317      * setYConstraint().
15318      * @method setYTicks
15319      * @private
15320      */
15321     setYTicks: function(iStartY, iTickSize) {
15322         this.yTicks = [];
15323         this.yTickSize = iTickSize;
15324
15325         var tickMap = {};
15326
15327         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15328             if (!tickMap[i]) {
15329                 this.yTicks[this.yTicks.length] = i;
15330                 tickMap[i] = true;
15331             }
15332         }
15333
15334         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15335             if (!tickMap[i]) {
15336                 this.yTicks[this.yTicks.length] = i;
15337                 tickMap[i] = true;
15338             }
15339         }
15340
15341         this.yTicks.sort(this.DDM.numericSort) ;
15342     },
15343
15344     /**
15345      * By default, the element can be dragged any place on the screen.  Use
15346      * this method to limit the horizontal travel of the element.  Pass in
15347      * 0,0 for the parameters if you want to lock the drag to the y axis.
15348      * @method setXConstraint
15349      * @param {int} iLeft the number of pixels the element can move to the left
15350      * @param {int} iRight the number of pixels the element can move to the
15351      * right
15352      * @param {int} iTickSize optional parameter for specifying that the
15353      * element
15354      * should move iTickSize pixels at a time.
15355      */
15356     setXConstraint: function(iLeft, iRight, iTickSize) {
15357         this.leftConstraint = iLeft;
15358         this.rightConstraint = iRight;
15359
15360         this.minX = this.initPageX - iLeft;
15361         this.maxX = this.initPageX + iRight;
15362         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15363
15364         this.constrainX = true;
15365     },
15366
15367     /**
15368      * Clears any constraints applied to this instance.  Also clears ticks
15369      * since they can't exist independent of a constraint at this time.
15370      * @method clearConstraints
15371      */
15372     clearConstraints: function() {
15373         this.constrainX = false;
15374         this.constrainY = false;
15375         this.clearTicks();
15376     },
15377
15378     /**
15379      * Clears any tick interval defined for this instance
15380      * @method clearTicks
15381      */
15382     clearTicks: function() {
15383         this.xTicks = null;
15384         this.yTicks = null;
15385         this.xTickSize = 0;
15386         this.yTickSize = 0;
15387     },
15388
15389     /**
15390      * By default, the element can be dragged any place on the screen.  Set
15391      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15392      * parameters if you want to lock the drag to the x axis.
15393      * @method setYConstraint
15394      * @param {int} iUp the number of pixels the element can move up
15395      * @param {int} iDown the number of pixels the element can move down
15396      * @param {int} iTickSize optional parameter for specifying that the
15397      * element should move iTickSize pixels at a time.
15398      */
15399     setYConstraint: function(iUp, iDown, iTickSize) {
15400         this.topConstraint = iUp;
15401         this.bottomConstraint = iDown;
15402
15403         this.minY = this.initPageY - iUp;
15404         this.maxY = this.initPageY + iDown;
15405         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15406
15407         this.constrainY = true;
15408
15409     },
15410
15411     /**
15412      * resetConstraints must be called if you manually reposition a dd element.
15413      * @method resetConstraints
15414      * @param {boolean} maintainOffset
15415      */
15416     resetConstraints: function() {
15417
15418
15419         // Maintain offsets if necessary
15420         if (this.initPageX || this.initPageX === 0) {
15421             // figure out how much this thing has moved
15422             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15423             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15424
15425             this.setInitPosition(dx, dy);
15426
15427         // This is the first time we have detected the element's position
15428         } else {
15429             this.setInitPosition();
15430         }
15431
15432         if (this.constrainX) {
15433             this.setXConstraint( this.leftConstraint,
15434                                  this.rightConstraint,
15435                                  this.xTickSize        );
15436         }
15437
15438         if (this.constrainY) {
15439             this.setYConstraint( this.topConstraint,
15440                                  this.bottomConstraint,
15441                                  this.yTickSize         );
15442         }
15443     },
15444
15445     /**
15446      * Normally the drag element is moved pixel by pixel, but we can specify
15447      * that it move a number of pixels at a time.  This method resolves the
15448      * location when we have it set up like this.
15449      * @method getTick
15450      * @param {int} val where we want to place the object
15451      * @param {int[]} tickArray sorted array of valid points
15452      * @return {int} the closest tick
15453      * @private
15454      */
15455     getTick: function(val, tickArray) {
15456
15457         if (!tickArray) {
15458             // If tick interval is not defined, it is effectively 1 pixel,
15459             // so we return the value passed to us.
15460             return val;
15461         } else if (tickArray[0] >= val) {
15462             // The value is lower than the first tick, so we return the first
15463             // tick.
15464             return tickArray[0];
15465         } else {
15466             for (var i=0, len=tickArray.length; i<len; ++i) {
15467                 var next = i + 1;
15468                 if (tickArray[next] && tickArray[next] >= val) {
15469                     var diff1 = val - tickArray[i];
15470                     var diff2 = tickArray[next] - val;
15471                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15472                 }
15473             }
15474
15475             // The value is larger than the last tick, so we return the last
15476             // tick.
15477             return tickArray[tickArray.length - 1];
15478         }
15479     },
15480
15481     /**
15482      * toString method
15483      * @method toString
15484      * @return {string} string representation of the dd obj
15485      */
15486     toString: function() {
15487         return ("DragDrop " + this.id);
15488     }
15489
15490 };
15491
15492 })();
15493 /*
15494  * Based on:
15495  * Ext JS Library 1.1.1
15496  * Copyright(c) 2006-2007, Ext JS, LLC.
15497  *
15498  * Originally Released Under LGPL - original licence link has changed is not relivant.
15499  *
15500  * Fork - LGPL
15501  * <script type="text/javascript">
15502  */
15503
15504
15505 /**
15506  * The drag and drop utility provides a framework for building drag and drop
15507  * applications.  In addition to enabling drag and drop for specific elements,
15508  * the drag and drop elements are tracked by the manager class, and the
15509  * interactions between the various elements are tracked during the drag and
15510  * the implementing code is notified about these important moments.
15511  */
15512
15513 // Only load the library once.  Rewriting the manager class would orphan
15514 // existing drag and drop instances.
15515 if (!Roo.dd.DragDropMgr) {
15516
15517 /**
15518  * @class Roo.dd.DragDropMgr
15519  * DragDropMgr is a singleton that tracks the element interaction for
15520  * all DragDrop items in the window.  Generally, you will not call
15521  * this class directly, but it does have helper methods that could
15522  * be useful in your DragDrop implementations.
15523  * @singleton
15524  */
15525 Roo.dd.DragDropMgr = function() {
15526
15527     var Event = Roo.EventManager;
15528
15529     return {
15530
15531         /**
15532          * Two dimensional Array of registered DragDrop objects.  The first
15533          * dimension is the DragDrop item group, the second the DragDrop
15534          * object.
15535          * @property ids
15536          * @type {string: string}
15537          * @private
15538          * @static
15539          */
15540         ids: {},
15541
15542         /**
15543          * Array of element ids defined as drag handles.  Used to determine
15544          * if the element that generated the mousedown event is actually the
15545          * handle and not the html element itself.
15546          * @property handleIds
15547          * @type {string: string}
15548          * @private
15549          * @static
15550          */
15551         handleIds: {},
15552
15553         /**
15554          * the DragDrop object that is currently being dragged
15555          * @property dragCurrent
15556          * @type DragDrop
15557          * @private
15558          * @static
15559          **/
15560         dragCurrent: null,
15561
15562         /**
15563          * the DragDrop object(s) that are being hovered over
15564          * @property dragOvers
15565          * @type Array
15566          * @private
15567          * @static
15568          */
15569         dragOvers: {},
15570
15571         /**
15572          * the X distance between the cursor and the object being dragged
15573          * @property deltaX
15574          * @type int
15575          * @private
15576          * @static
15577          */
15578         deltaX: 0,
15579
15580         /**
15581          * the Y distance between the cursor and the object being dragged
15582          * @property deltaY
15583          * @type int
15584          * @private
15585          * @static
15586          */
15587         deltaY: 0,
15588
15589         /**
15590          * Flag to determine if we should prevent the default behavior of the
15591          * events we define. By default this is true, but this can be set to
15592          * false if you need the default behavior (not recommended)
15593          * @property preventDefault
15594          * @type boolean
15595          * @static
15596          */
15597         preventDefault: true,
15598
15599         /**
15600          * Flag to determine if we should stop the propagation of the events
15601          * we generate. This is true by default but you may want to set it to
15602          * false if the html element contains other features that require the
15603          * mouse click.
15604          * @property stopPropagation
15605          * @type boolean
15606          * @static
15607          */
15608         stopPropagation: true,
15609
15610         /**
15611          * Internal flag that is set to true when drag and drop has been
15612          * intialized
15613          * @property initialized
15614          * @private
15615          * @static
15616          */
15617         initalized: false,
15618
15619         /**
15620          * All drag and drop can be disabled.
15621          * @property locked
15622          * @private
15623          * @static
15624          */
15625         locked: false,
15626
15627         /**
15628          * Called the first time an element is registered.
15629          * @method init
15630          * @private
15631          * @static
15632          */
15633         init: function() {
15634             this.initialized = true;
15635         },
15636
15637         /**
15638          * In point mode, drag and drop interaction is defined by the
15639          * location of the cursor during the drag/drop
15640          * @property POINT
15641          * @type int
15642          * @static
15643          */
15644         POINT: 0,
15645
15646         /**
15647          * In intersect mode, drag and drop interactio nis defined by the
15648          * overlap of two or more drag and drop objects.
15649          * @property INTERSECT
15650          * @type int
15651          * @static
15652          */
15653         INTERSECT: 1,
15654
15655         /**
15656          * The current drag and drop mode.  Default: POINT
15657          * @property mode
15658          * @type int
15659          * @static
15660          */
15661         mode: 0,
15662
15663         /**
15664          * Runs method on all drag and drop objects
15665          * @method _execOnAll
15666          * @private
15667          * @static
15668          */
15669         _execOnAll: function(sMethod, args) {
15670             for (var i in this.ids) {
15671                 for (var j in this.ids[i]) {
15672                     var oDD = this.ids[i][j];
15673                     if (! this.isTypeOfDD(oDD)) {
15674                         continue;
15675                     }
15676                     oDD[sMethod].apply(oDD, args);
15677                 }
15678             }
15679         },
15680
15681         /**
15682          * Drag and drop initialization.  Sets up the global event handlers
15683          * @method _onLoad
15684          * @private
15685          * @static
15686          */
15687         _onLoad: function() {
15688
15689             this.init();
15690
15691
15692             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15693             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15694             Event.on(window,   "unload",    this._onUnload, this, true);
15695             Event.on(window,   "resize",    this._onResize, this, true);
15696             // Event.on(window,   "mouseout",    this._test);
15697
15698         },
15699
15700         /**
15701          * Reset constraints on all drag and drop objs
15702          * @method _onResize
15703          * @private
15704          * @static
15705          */
15706         _onResize: function(e) {
15707             this._execOnAll("resetConstraints", []);
15708         },
15709
15710         /**
15711          * Lock all drag and drop functionality
15712          * @method lock
15713          * @static
15714          */
15715         lock: function() { this.locked = true; },
15716
15717         /**
15718          * Unlock all drag and drop functionality
15719          * @method unlock
15720          * @static
15721          */
15722         unlock: function() { this.locked = false; },
15723
15724         /**
15725          * Is drag and drop locked?
15726          * @method isLocked
15727          * @return {boolean} True if drag and drop is locked, false otherwise.
15728          * @static
15729          */
15730         isLocked: function() { return this.locked; },
15731
15732         /**
15733          * Location cache that is set for all drag drop objects when a drag is
15734          * initiated, cleared when the drag is finished.
15735          * @property locationCache
15736          * @private
15737          * @static
15738          */
15739         locationCache: {},
15740
15741         /**
15742          * Set useCache to false if you want to force object the lookup of each
15743          * drag and drop linked element constantly during a drag.
15744          * @property useCache
15745          * @type boolean
15746          * @static
15747          */
15748         useCache: true,
15749
15750         /**
15751          * The number of pixels that the mouse needs to move after the
15752          * mousedown before the drag is initiated.  Default=3;
15753          * @property clickPixelThresh
15754          * @type int
15755          * @static
15756          */
15757         clickPixelThresh: 3,
15758
15759         /**
15760          * The number of milliseconds after the mousedown event to initiate the
15761          * drag if we don't get a mouseup event. Default=1000
15762          * @property clickTimeThresh
15763          * @type int
15764          * @static
15765          */
15766         clickTimeThresh: 350,
15767
15768         /**
15769          * Flag that indicates that either the drag pixel threshold or the
15770          * mousdown time threshold has been met
15771          * @property dragThreshMet
15772          * @type boolean
15773          * @private
15774          * @static
15775          */
15776         dragThreshMet: false,
15777
15778         /**
15779          * Timeout used for the click time threshold
15780          * @property clickTimeout
15781          * @type Object
15782          * @private
15783          * @static
15784          */
15785         clickTimeout: null,
15786
15787         /**
15788          * The X position of the mousedown event stored for later use when a
15789          * drag threshold is met.
15790          * @property startX
15791          * @type int
15792          * @private
15793          * @static
15794          */
15795         startX: 0,
15796
15797         /**
15798          * The Y position of the mousedown event stored for later use when a
15799          * drag threshold is met.
15800          * @property startY
15801          * @type int
15802          * @private
15803          * @static
15804          */
15805         startY: 0,
15806
15807         /**
15808          * Each DragDrop instance must be registered with the DragDropMgr.
15809          * This is executed in DragDrop.init()
15810          * @method regDragDrop
15811          * @param {DragDrop} oDD the DragDrop object to register
15812          * @param {String} sGroup the name of the group this element belongs to
15813          * @static
15814          */
15815         regDragDrop: function(oDD, sGroup) {
15816             if (!this.initialized) { this.init(); }
15817
15818             if (!this.ids[sGroup]) {
15819                 this.ids[sGroup] = {};
15820             }
15821             this.ids[sGroup][oDD.id] = oDD;
15822         },
15823
15824         /**
15825          * Removes the supplied dd instance from the supplied group. Executed
15826          * by DragDrop.removeFromGroup, so don't call this function directly.
15827          * @method removeDDFromGroup
15828          * @private
15829          * @static
15830          */
15831         removeDDFromGroup: function(oDD, sGroup) {
15832             if (!this.ids[sGroup]) {
15833                 this.ids[sGroup] = {};
15834             }
15835
15836             var obj = this.ids[sGroup];
15837             if (obj && obj[oDD.id]) {
15838                 delete obj[oDD.id];
15839             }
15840         },
15841
15842         /**
15843          * Unregisters a drag and drop item.  This is executed in
15844          * DragDrop.unreg, use that method instead of calling this directly.
15845          * @method _remove
15846          * @private
15847          * @static
15848          */
15849         _remove: function(oDD) {
15850             for (var g in oDD.groups) {
15851                 if (g && this.ids[g][oDD.id]) {
15852                     delete this.ids[g][oDD.id];
15853                 }
15854             }
15855             delete this.handleIds[oDD.id];
15856         },
15857
15858         /**
15859          * Each DragDrop handle element must be registered.  This is done
15860          * automatically when executing DragDrop.setHandleElId()
15861          * @method regHandle
15862          * @param {String} sDDId the DragDrop id this element is a handle for
15863          * @param {String} sHandleId the id of the element that is the drag
15864          * handle
15865          * @static
15866          */
15867         regHandle: function(sDDId, sHandleId) {
15868             if (!this.handleIds[sDDId]) {
15869                 this.handleIds[sDDId] = {};
15870             }
15871             this.handleIds[sDDId][sHandleId] = sHandleId;
15872         },
15873
15874         /**
15875          * Utility function to determine if a given element has been
15876          * registered as a drag drop item.
15877          * @method isDragDrop
15878          * @param {String} id the element id to check
15879          * @return {boolean} true if this element is a DragDrop item,
15880          * false otherwise
15881          * @static
15882          */
15883         isDragDrop: function(id) {
15884             return ( this.getDDById(id) ) ? true : false;
15885         },
15886
15887         /**
15888          * Returns the drag and drop instances that are in all groups the
15889          * passed in instance belongs to.
15890          * @method getRelated
15891          * @param {DragDrop} p_oDD the obj to get related data for
15892          * @param {boolean} bTargetsOnly if true, only return targetable objs
15893          * @return {DragDrop[]} the related instances
15894          * @static
15895          */
15896         getRelated: function(p_oDD, bTargetsOnly) {
15897             var oDDs = [];
15898             for (var i in p_oDD.groups) {
15899                 for (j in this.ids[i]) {
15900                     var dd = this.ids[i][j];
15901                     if (! this.isTypeOfDD(dd)) {
15902                         continue;
15903                     }
15904                     if (!bTargetsOnly || dd.isTarget) {
15905                         oDDs[oDDs.length] = dd;
15906                     }
15907                 }
15908             }
15909
15910             return oDDs;
15911         },
15912
15913         /**
15914          * Returns true if the specified dd target is a legal target for
15915          * the specifice drag obj
15916          * @method isLegalTarget
15917          * @param {DragDrop} the drag obj
15918          * @param {DragDrop} the target
15919          * @return {boolean} true if the target is a legal target for the
15920          * dd obj
15921          * @static
15922          */
15923         isLegalTarget: function (oDD, oTargetDD) {
15924             var targets = this.getRelated(oDD, true);
15925             for (var i=0, len=targets.length;i<len;++i) {
15926                 if (targets[i].id == oTargetDD.id) {
15927                     return true;
15928                 }
15929             }
15930
15931             return false;
15932         },
15933
15934         /**
15935          * My goal is to be able to transparently determine if an object is
15936          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
15937          * returns "object", oDD.constructor.toString() always returns
15938          * "DragDrop" and not the name of the subclass.  So for now it just
15939          * evaluates a well-known variable in DragDrop.
15940          * @method isTypeOfDD
15941          * @param {Object} the object to evaluate
15942          * @return {boolean} true if typeof oDD = DragDrop
15943          * @static
15944          */
15945         isTypeOfDD: function (oDD) {
15946             return (oDD && oDD.__ygDragDrop);
15947         },
15948
15949         /**
15950          * Utility function to determine if a given element has been
15951          * registered as a drag drop handle for the given Drag Drop object.
15952          * @method isHandle
15953          * @param {String} id the element id to check
15954          * @return {boolean} true if this element is a DragDrop handle, false
15955          * otherwise
15956          * @static
15957          */
15958         isHandle: function(sDDId, sHandleId) {
15959             return ( this.handleIds[sDDId] &&
15960                             this.handleIds[sDDId][sHandleId] );
15961         },
15962
15963         /**
15964          * Returns the DragDrop instance for a given id
15965          * @method getDDById
15966          * @param {String} id the id of the DragDrop object
15967          * @return {DragDrop} the drag drop object, null if it is not found
15968          * @static
15969          */
15970         getDDById: function(id) {
15971             for (var i in this.ids) {
15972                 if (this.ids[i][id]) {
15973                     return this.ids[i][id];
15974                 }
15975             }
15976             return null;
15977         },
15978
15979         /**
15980          * Fired after a registered DragDrop object gets the mousedown event.
15981          * Sets up the events required to track the object being dragged
15982          * @method handleMouseDown
15983          * @param {Event} e the event
15984          * @param oDD the DragDrop object being dragged
15985          * @private
15986          * @static
15987          */
15988         handleMouseDown: function(e, oDD) {
15989             if(Roo.QuickTips){
15990                 Roo.QuickTips.disable();
15991             }
15992             this.currentTarget = e.getTarget();
15993
15994             this.dragCurrent = oDD;
15995
15996             var el = oDD.getEl();
15997
15998             // track start position
15999             this.startX = e.getPageX();
16000             this.startY = e.getPageY();
16001
16002             this.deltaX = this.startX - el.offsetLeft;
16003             this.deltaY = this.startY - el.offsetTop;
16004
16005             this.dragThreshMet = false;
16006
16007             this.clickTimeout = setTimeout(
16008                     function() {
16009                         var DDM = Roo.dd.DDM;
16010                         DDM.startDrag(DDM.startX, DDM.startY);
16011                     },
16012                     this.clickTimeThresh );
16013         },
16014
16015         /**
16016          * Fired when either the drag pixel threshol or the mousedown hold
16017          * time threshold has been met.
16018          * @method startDrag
16019          * @param x {int} the X position of the original mousedown
16020          * @param y {int} the Y position of the original mousedown
16021          * @static
16022          */
16023         startDrag: function(x, y) {
16024             clearTimeout(this.clickTimeout);
16025             if (this.dragCurrent) {
16026                 this.dragCurrent.b4StartDrag(x, y);
16027                 this.dragCurrent.startDrag(x, y);
16028             }
16029             this.dragThreshMet = true;
16030         },
16031
16032         /**
16033          * Internal function to handle the mouseup event.  Will be invoked
16034          * from the context of the document.
16035          * @method handleMouseUp
16036          * @param {Event} e the event
16037          * @private
16038          * @static
16039          */
16040         handleMouseUp: function(e) {
16041
16042             if(Roo.QuickTips){
16043                 Roo.QuickTips.enable();
16044             }
16045             if (! this.dragCurrent) {
16046                 return;
16047             }
16048
16049             clearTimeout(this.clickTimeout);
16050
16051             if (this.dragThreshMet) {
16052                 this.fireEvents(e, true);
16053             } else {
16054             }
16055
16056             this.stopDrag(e);
16057
16058             this.stopEvent(e);
16059         },
16060
16061         /**
16062          * Utility to stop event propagation and event default, if these
16063          * features are turned on.
16064          * @method stopEvent
16065          * @param {Event} e the event as returned by this.getEvent()
16066          * @static
16067          */
16068         stopEvent: function(e){
16069             if(this.stopPropagation) {
16070                 e.stopPropagation();
16071             }
16072
16073             if (this.preventDefault) {
16074                 e.preventDefault();
16075             }
16076         },
16077
16078         /**
16079          * Internal function to clean up event handlers after the drag
16080          * operation is complete
16081          * @method stopDrag
16082          * @param {Event} e the event
16083          * @private
16084          * @static
16085          */
16086         stopDrag: function(e) {
16087             // Fire the drag end event for the item that was dragged
16088             if (this.dragCurrent) {
16089                 if (this.dragThreshMet) {
16090                     this.dragCurrent.b4EndDrag(e);
16091                     this.dragCurrent.endDrag(e);
16092                 }
16093
16094                 this.dragCurrent.onMouseUp(e);
16095             }
16096
16097             this.dragCurrent = null;
16098             this.dragOvers = {};
16099         },
16100
16101         /**
16102          * Internal function to handle the mousemove event.  Will be invoked
16103          * from the context of the html element.
16104          *
16105          * @TODO figure out what we can do about mouse events lost when the
16106          * user drags objects beyond the window boundary.  Currently we can
16107          * detect this in internet explorer by verifying that the mouse is
16108          * down during the mousemove event.  Firefox doesn't give us the
16109          * button state on the mousemove event.
16110          * @method handleMouseMove
16111          * @param {Event} e the event
16112          * @private
16113          * @static
16114          */
16115         handleMouseMove: function(e) {
16116             if (! this.dragCurrent) {
16117                 return true;
16118             }
16119
16120             // var button = e.which || e.button;
16121
16122             // check for IE mouseup outside of page boundary
16123             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16124                 this.stopEvent(e);
16125                 return this.handleMouseUp(e);
16126             }
16127
16128             if (!this.dragThreshMet) {
16129                 var diffX = Math.abs(this.startX - e.getPageX());
16130                 var diffY = Math.abs(this.startY - e.getPageY());
16131                 if (diffX > this.clickPixelThresh ||
16132                             diffY > this.clickPixelThresh) {
16133                     this.startDrag(this.startX, this.startY);
16134                 }
16135             }
16136
16137             if (this.dragThreshMet) {
16138                 this.dragCurrent.b4Drag(e);
16139                 this.dragCurrent.onDrag(e);
16140                 if(!this.dragCurrent.moveOnly){
16141                     this.fireEvents(e, false);
16142                 }
16143             }
16144
16145             this.stopEvent(e);
16146
16147             return true;
16148         },
16149
16150         /**
16151          * Iterates over all of the DragDrop elements to find ones we are
16152          * hovering over or dropping on
16153          * @method fireEvents
16154          * @param {Event} e the event
16155          * @param {boolean} isDrop is this a drop op or a mouseover op?
16156          * @private
16157          * @static
16158          */
16159         fireEvents: function(e, isDrop) {
16160             var dc = this.dragCurrent;
16161
16162             // If the user did the mouse up outside of the window, we could
16163             // get here even though we have ended the drag.
16164             if (!dc || dc.isLocked()) {
16165                 return;
16166             }
16167
16168             var pt = e.getPoint();
16169
16170             // cache the previous dragOver array
16171             var oldOvers = [];
16172
16173             var outEvts   = [];
16174             var overEvts  = [];
16175             var dropEvts  = [];
16176             var enterEvts = [];
16177
16178             // Check to see if the object(s) we were hovering over is no longer
16179             // being hovered over so we can fire the onDragOut event
16180             for (var i in this.dragOvers) {
16181
16182                 var ddo = this.dragOvers[i];
16183
16184                 if (! this.isTypeOfDD(ddo)) {
16185                     continue;
16186                 }
16187
16188                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16189                     outEvts.push( ddo );
16190                 }
16191
16192                 oldOvers[i] = true;
16193                 delete this.dragOvers[i];
16194             }
16195
16196             for (var sGroup in dc.groups) {
16197
16198                 if ("string" != typeof sGroup) {
16199                     continue;
16200                 }
16201
16202                 for (i in this.ids[sGroup]) {
16203                     var oDD = this.ids[sGroup][i];
16204                     if (! this.isTypeOfDD(oDD)) {
16205                         continue;
16206                     }
16207
16208                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16209                         if (this.isOverTarget(pt, oDD, this.mode)) {
16210                             // look for drop interactions
16211                             if (isDrop) {
16212                                 dropEvts.push( oDD );
16213                             // look for drag enter and drag over interactions
16214                             } else {
16215
16216                                 // initial drag over: dragEnter fires
16217                                 if (!oldOvers[oDD.id]) {
16218                                     enterEvts.push( oDD );
16219                                 // subsequent drag overs: dragOver fires
16220                                 } else {
16221                                     overEvts.push( oDD );
16222                                 }
16223
16224                                 this.dragOvers[oDD.id] = oDD;
16225                             }
16226                         }
16227                     }
16228                 }
16229             }
16230
16231             if (this.mode) {
16232                 if (outEvts.length) {
16233                     dc.b4DragOut(e, outEvts);
16234                     dc.onDragOut(e, outEvts);
16235                 }
16236
16237                 if (enterEvts.length) {
16238                     dc.onDragEnter(e, enterEvts);
16239                 }
16240
16241                 if (overEvts.length) {
16242                     dc.b4DragOver(e, overEvts);
16243                     dc.onDragOver(e, overEvts);
16244                 }
16245
16246                 if (dropEvts.length) {
16247                     dc.b4DragDrop(e, dropEvts);
16248                     dc.onDragDrop(e, dropEvts);
16249                 }
16250
16251             } else {
16252                 // fire dragout events
16253                 var len = 0;
16254                 for (i=0, len=outEvts.length; i<len; ++i) {
16255                     dc.b4DragOut(e, outEvts[i].id);
16256                     dc.onDragOut(e, outEvts[i].id);
16257                 }
16258
16259                 // fire enter events
16260                 for (i=0,len=enterEvts.length; i<len; ++i) {
16261                     // dc.b4DragEnter(e, oDD.id);
16262                     dc.onDragEnter(e, enterEvts[i].id);
16263                 }
16264
16265                 // fire over events
16266                 for (i=0,len=overEvts.length; i<len; ++i) {
16267                     dc.b4DragOver(e, overEvts[i].id);
16268                     dc.onDragOver(e, overEvts[i].id);
16269                 }
16270
16271                 // fire drop events
16272                 for (i=0, len=dropEvts.length; i<len; ++i) {
16273                     dc.b4DragDrop(e, dropEvts[i].id);
16274                     dc.onDragDrop(e, dropEvts[i].id);
16275                 }
16276
16277             }
16278
16279             // notify about a drop that did not find a target
16280             if (isDrop && !dropEvts.length) {
16281                 dc.onInvalidDrop(e);
16282             }
16283
16284         },
16285
16286         /**
16287          * Helper function for getting the best match from the list of drag
16288          * and drop objects returned by the drag and drop events when we are
16289          * in INTERSECT mode.  It returns either the first object that the
16290          * cursor is over, or the object that has the greatest overlap with
16291          * the dragged element.
16292          * @method getBestMatch
16293          * @param  {DragDrop[]} dds The array of drag and drop objects
16294          * targeted
16295          * @return {DragDrop}       The best single match
16296          * @static
16297          */
16298         getBestMatch: function(dds) {
16299             var winner = null;
16300             // Return null if the input is not what we expect
16301             //if (!dds || !dds.length || dds.length == 0) {
16302                // winner = null;
16303             // If there is only one item, it wins
16304             //} else if (dds.length == 1) {
16305
16306             var len = dds.length;
16307
16308             if (len == 1) {
16309                 winner = dds[0];
16310             } else {
16311                 // Loop through the targeted items
16312                 for (var i=0; i<len; ++i) {
16313                     var dd = dds[i];
16314                     // If the cursor is over the object, it wins.  If the
16315                     // cursor is over multiple matches, the first one we come
16316                     // to wins.
16317                     if (dd.cursorIsOver) {
16318                         winner = dd;
16319                         break;
16320                     // Otherwise the object with the most overlap wins
16321                     } else {
16322                         if (!winner ||
16323                             winner.overlap.getArea() < dd.overlap.getArea()) {
16324                             winner = dd;
16325                         }
16326                     }
16327                 }
16328             }
16329
16330             return winner;
16331         },
16332
16333         /**
16334          * Refreshes the cache of the top-left and bottom-right points of the
16335          * drag and drop objects in the specified group(s).  This is in the
16336          * format that is stored in the drag and drop instance, so typical
16337          * usage is:
16338          * <code>
16339          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16340          * </code>
16341          * Alternatively:
16342          * <code>
16343          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16344          * </code>
16345          * @TODO this really should be an indexed array.  Alternatively this
16346          * method could accept both.
16347          * @method refreshCache
16348          * @param {Object} groups an associative array of groups to refresh
16349          * @static
16350          */
16351         refreshCache: function(groups) {
16352             for (var sGroup in groups) {
16353                 if ("string" != typeof sGroup) {
16354                     continue;
16355                 }
16356                 for (var i in this.ids[sGroup]) {
16357                     var oDD = this.ids[sGroup][i];
16358
16359                     if (this.isTypeOfDD(oDD)) {
16360                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16361                         var loc = this.getLocation(oDD);
16362                         if (loc) {
16363                             this.locationCache[oDD.id] = loc;
16364                         } else {
16365                             delete this.locationCache[oDD.id];
16366                             // this will unregister the drag and drop object if
16367                             // the element is not in a usable state
16368                             // oDD.unreg();
16369                         }
16370                     }
16371                 }
16372             }
16373         },
16374
16375         /**
16376          * This checks to make sure an element exists and is in the DOM.  The
16377          * main purpose is to handle cases where innerHTML is used to remove
16378          * drag and drop objects from the DOM.  IE provides an 'unspecified
16379          * error' when trying to access the offsetParent of such an element
16380          * @method verifyEl
16381          * @param {HTMLElement} el the element to check
16382          * @return {boolean} true if the element looks usable
16383          * @static
16384          */
16385         verifyEl: function(el) {
16386             if (el) {
16387                 var parent;
16388                 if(Roo.isIE){
16389                     try{
16390                         parent = el.offsetParent;
16391                     }catch(e){}
16392                 }else{
16393                     parent = el.offsetParent;
16394                 }
16395                 if (parent) {
16396                     return true;
16397                 }
16398             }
16399
16400             return false;
16401         },
16402
16403         /**
16404          * Returns a Region object containing the drag and drop element's position
16405          * and size, including the padding configured for it
16406          * @method getLocation
16407          * @param {DragDrop} oDD the drag and drop object to get the
16408          *                       location for
16409          * @return {Roo.lib.Region} a Region object representing the total area
16410          *                             the element occupies, including any padding
16411          *                             the instance is configured for.
16412          * @static
16413          */
16414         getLocation: function(oDD) {
16415             if (! this.isTypeOfDD(oDD)) {
16416                 return null;
16417             }
16418
16419             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16420
16421             try {
16422                 pos= Roo.lib.Dom.getXY(el);
16423             } catch (e) { }
16424
16425             if (!pos) {
16426                 return null;
16427             }
16428
16429             x1 = pos[0];
16430             x2 = x1 + el.offsetWidth;
16431             y1 = pos[1];
16432             y2 = y1 + el.offsetHeight;
16433
16434             t = y1 - oDD.padding[0];
16435             r = x2 + oDD.padding[1];
16436             b = y2 + oDD.padding[2];
16437             l = x1 - oDD.padding[3];
16438
16439             return new Roo.lib.Region( t, r, b, l );
16440         },
16441
16442         /**
16443          * Checks the cursor location to see if it over the target
16444          * @method isOverTarget
16445          * @param {Roo.lib.Point} pt The point to evaluate
16446          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16447          * @return {boolean} true if the mouse is over the target
16448          * @private
16449          * @static
16450          */
16451         isOverTarget: function(pt, oTarget, intersect) {
16452             // use cache if available
16453             var loc = this.locationCache[oTarget.id];
16454             if (!loc || !this.useCache) {
16455                 loc = this.getLocation(oTarget);
16456                 this.locationCache[oTarget.id] = loc;
16457
16458             }
16459
16460             if (!loc) {
16461                 return false;
16462             }
16463
16464             oTarget.cursorIsOver = loc.contains( pt );
16465
16466             // DragDrop is using this as a sanity check for the initial mousedown
16467             // in this case we are done.  In POINT mode, if the drag obj has no
16468             // contraints, we are also done. Otherwise we need to evaluate the
16469             // location of the target as related to the actual location of the
16470             // dragged element.
16471             var dc = this.dragCurrent;
16472             if (!dc || !dc.getTargetCoord ||
16473                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16474                 return oTarget.cursorIsOver;
16475             }
16476
16477             oTarget.overlap = null;
16478
16479             // Get the current location of the drag element, this is the
16480             // location of the mouse event less the delta that represents
16481             // where the original mousedown happened on the element.  We
16482             // need to consider constraints and ticks as well.
16483             var pos = dc.getTargetCoord(pt.x, pt.y);
16484
16485             var el = dc.getDragEl();
16486             var curRegion = new Roo.lib.Region( pos.y,
16487                                                    pos.x + el.offsetWidth,
16488                                                    pos.y + el.offsetHeight,
16489                                                    pos.x );
16490
16491             var overlap = curRegion.intersect(loc);
16492
16493             if (overlap) {
16494                 oTarget.overlap = overlap;
16495                 return (intersect) ? true : oTarget.cursorIsOver;
16496             } else {
16497                 return false;
16498             }
16499         },
16500
16501         /**
16502          * unload event handler
16503          * @method _onUnload
16504          * @private
16505          * @static
16506          */
16507         _onUnload: function(e, me) {
16508             Roo.dd.DragDropMgr.unregAll();
16509         },
16510
16511         /**
16512          * Cleans up the drag and drop events and objects.
16513          * @method unregAll
16514          * @private
16515          * @static
16516          */
16517         unregAll: function() {
16518
16519             if (this.dragCurrent) {
16520                 this.stopDrag();
16521                 this.dragCurrent = null;
16522             }
16523
16524             this._execOnAll("unreg", []);
16525
16526             for (i in this.elementCache) {
16527                 delete this.elementCache[i];
16528             }
16529
16530             this.elementCache = {};
16531             this.ids = {};
16532         },
16533
16534         /**
16535          * A cache of DOM elements
16536          * @property elementCache
16537          * @private
16538          * @static
16539          */
16540         elementCache: {},
16541
16542         /**
16543          * Get the wrapper for the DOM element specified
16544          * @method getElWrapper
16545          * @param {String} id the id of the element to get
16546          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16547          * @private
16548          * @deprecated This wrapper isn't that useful
16549          * @static
16550          */
16551         getElWrapper: function(id) {
16552             var oWrapper = this.elementCache[id];
16553             if (!oWrapper || !oWrapper.el) {
16554                 oWrapper = this.elementCache[id] =
16555                     new this.ElementWrapper(Roo.getDom(id));
16556             }
16557             return oWrapper;
16558         },
16559
16560         /**
16561          * Returns the actual DOM element
16562          * @method getElement
16563          * @param {String} id the id of the elment to get
16564          * @return {Object} The element
16565          * @deprecated use Roo.getDom instead
16566          * @static
16567          */
16568         getElement: function(id) {
16569             return Roo.getDom(id);
16570         },
16571
16572         /**
16573          * Returns the style property for the DOM element (i.e.,
16574          * document.getElById(id).style)
16575          * @method getCss
16576          * @param {String} id the id of the elment to get
16577          * @return {Object} The style property of the element
16578          * @deprecated use Roo.getDom instead
16579          * @static
16580          */
16581         getCss: function(id) {
16582             var el = Roo.getDom(id);
16583             return (el) ? el.style : null;
16584         },
16585
16586         /**
16587          * Inner class for cached elements
16588          * @class DragDropMgr.ElementWrapper
16589          * @for DragDropMgr
16590          * @private
16591          * @deprecated
16592          */
16593         ElementWrapper: function(el) {
16594                 /**
16595                  * The element
16596                  * @property el
16597                  */
16598                 this.el = el || null;
16599                 /**
16600                  * The element id
16601                  * @property id
16602                  */
16603                 this.id = this.el && el.id;
16604                 /**
16605                  * A reference to the style property
16606                  * @property css
16607                  */
16608                 this.css = this.el && el.style;
16609             },
16610
16611         /**
16612          * Returns the X position of an html element
16613          * @method getPosX
16614          * @param el the element for which to get the position
16615          * @return {int} the X coordinate
16616          * @for DragDropMgr
16617          * @deprecated use Roo.lib.Dom.getX instead
16618          * @static
16619          */
16620         getPosX: function(el) {
16621             return Roo.lib.Dom.getX(el);
16622         },
16623
16624         /**
16625          * Returns the Y position of an html element
16626          * @method getPosY
16627          * @param el the element for which to get the position
16628          * @return {int} the Y coordinate
16629          * @deprecated use Roo.lib.Dom.getY instead
16630          * @static
16631          */
16632         getPosY: function(el) {
16633             return Roo.lib.Dom.getY(el);
16634         },
16635
16636         /**
16637          * Swap two nodes.  In IE, we use the native method, for others we
16638          * emulate the IE behavior
16639          * @method swapNode
16640          * @param n1 the first node to swap
16641          * @param n2 the other node to swap
16642          * @static
16643          */
16644         swapNode: function(n1, n2) {
16645             if (n1.swapNode) {
16646                 n1.swapNode(n2);
16647             } else {
16648                 var p = n2.parentNode;
16649                 var s = n2.nextSibling;
16650
16651                 if (s == n1) {
16652                     p.insertBefore(n1, n2);
16653                 } else if (n2 == n1.nextSibling) {
16654                     p.insertBefore(n2, n1);
16655                 } else {
16656                     n1.parentNode.replaceChild(n2, n1);
16657                     p.insertBefore(n1, s);
16658                 }
16659             }
16660         },
16661
16662         /**
16663          * Returns the current scroll position
16664          * @method getScroll
16665          * @private
16666          * @static
16667          */
16668         getScroll: function () {
16669             var t, l, dde=document.documentElement, db=document.body;
16670             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16671                 t = dde.scrollTop;
16672                 l = dde.scrollLeft;
16673             } else if (db) {
16674                 t = db.scrollTop;
16675                 l = db.scrollLeft;
16676             } else {
16677
16678             }
16679             return { top: t, left: l };
16680         },
16681
16682         /**
16683          * Returns the specified element style property
16684          * @method getStyle
16685          * @param {HTMLElement} el          the element
16686          * @param {string}      styleProp   the style property
16687          * @return {string} The value of the style property
16688          * @deprecated use Roo.lib.Dom.getStyle
16689          * @static
16690          */
16691         getStyle: function(el, styleProp) {
16692             return Roo.fly(el).getStyle(styleProp);
16693         },
16694
16695         /**
16696          * Gets the scrollTop
16697          * @method getScrollTop
16698          * @return {int} the document's scrollTop
16699          * @static
16700          */
16701         getScrollTop: function () { return this.getScroll().top; },
16702
16703         /**
16704          * Gets the scrollLeft
16705          * @method getScrollLeft
16706          * @return {int} the document's scrollTop
16707          * @static
16708          */
16709         getScrollLeft: function () { return this.getScroll().left; },
16710
16711         /**
16712          * Sets the x/y position of an element to the location of the
16713          * target element.
16714          * @method moveToEl
16715          * @param {HTMLElement} moveEl      The element to move
16716          * @param {HTMLElement} targetEl    The position reference element
16717          * @static
16718          */
16719         moveToEl: function (moveEl, targetEl) {
16720             var aCoord = Roo.lib.Dom.getXY(targetEl);
16721             Roo.lib.Dom.setXY(moveEl, aCoord);
16722         },
16723
16724         /**
16725          * Numeric array sort function
16726          * @method numericSort
16727          * @static
16728          */
16729         numericSort: function(a, b) { return (a - b); },
16730
16731         /**
16732          * Internal counter
16733          * @property _timeoutCount
16734          * @private
16735          * @static
16736          */
16737         _timeoutCount: 0,
16738
16739         /**
16740          * Trying to make the load order less important.  Without this we get
16741          * an error if this file is loaded before the Event Utility.
16742          * @method _addListeners
16743          * @private
16744          * @static
16745          */
16746         _addListeners: function() {
16747             var DDM = Roo.dd.DDM;
16748             if ( Roo.lib.Event && document ) {
16749                 DDM._onLoad();
16750             } else {
16751                 if (DDM._timeoutCount > 2000) {
16752                 } else {
16753                     setTimeout(DDM._addListeners, 10);
16754                     if (document && document.body) {
16755                         DDM._timeoutCount += 1;
16756                     }
16757                 }
16758             }
16759         },
16760
16761         /**
16762          * Recursively searches the immediate parent and all child nodes for
16763          * the handle element in order to determine wheter or not it was
16764          * clicked.
16765          * @method handleWasClicked
16766          * @param node the html element to inspect
16767          * @static
16768          */
16769         handleWasClicked: function(node, id) {
16770             if (this.isHandle(id, node.id)) {
16771                 return true;
16772             } else {
16773                 // check to see if this is a text node child of the one we want
16774                 var p = node.parentNode;
16775
16776                 while (p) {
16777                     if (this.isHandle(id, p.id)) {
16778                         return true;
16779                     } else {
16780                         p = p.parentNode;
16781                     }
16782                 }
16783             }
16784
16785             return false;
16786         }
16787
16788     };
16789
16790 }();
16791
16792 // shorter alias, save a few bytes
16793 Roo.dd.DDM = Roo.dd.DragDropMgr;
16794 Roo.dd.DDM._addListeners();
16795
16796 }/*
16797  * Based on:
16798  * Ext JS Library 1.1.1
16799  * Copyright(c) 2006-2007, Ext JS, LLC.
16800  *
16801  * Originally Released Under LGPL - original licence link has changed is not relivant.
16802  *
16803  * Fork - LGPL
16804  * <script type="text/javascript">
16805  */
16806
16807 /**
16808  * @class Roo.dd.DD
16809  * A DragDrop implementation where the linked element follows the
16810  * mouse cursor during a drag.
16811  * @extends Roo.dd.DragDrop
16812  * @constructor
16813  * @param {String} id the id of the linked element
16814  * @param {String} sGroup the group of related DragDrop items
16815  * @param {object} config an object containing configurable attributes
16816  *                Valid properties for DD:
16817  *                    scroll
16818  */
16819 Roo.dd.DD = function(id, sGroup, config) {
16820     if (id) {
16821         this.init(id, sGroup, config);
16822     }
16823 };
16824
16825 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16826
16827     /**
16828      * When set to true, the utility automatically tries to scroll the browser
16829      * window wehn a drag and drop element is dragged near the viewport boundary.
16830      * Defaults to true.
16831      * @property scroll
16832      * @type boolean
16833      */
16834     scroll: true,
16835
16836     /**
16837      * Sets the pointer offset to the distance between the linked element's top
16838      * left corner and the location the element was clicked
16839      * @method autoOffset
16840      * @param {int} iPageX the X coordinate of the click
16841      * @param {int} iPageY the Y coordinate of the click
16842      */
16843     autoOffset: function(iPageX, iPageY) {
16844         var x = iPageX - this.startPageX;
16845         var y = iPageY - this.startPageY;
16846         this.setDelta(x, y);
16847     },
16848
16849     /**
16850      * Sets the pointer offset.  You can call this directly to force the
16851      * offset to be in a particular location (e.g., pass in 0,0 to set it
16852      * to the center of the object)
16853      * @method setDelta
16854      * @param {int} iDeltaX the distance from the left
16855      * @param {int} iDeltaY the distance from the top
16856      */
16857     setDelta: function(iDeltaX, iDeltaY) {
16858         this.deltaX = iDeltaX;
16859         this.deltaY = iDeltaY;
16860     },
16861
16862     /**
16863      * Sets the drag element to the location of the mousedown or click event,
16864      * maintaining the cursor location relative to the location on the element
16865      * that was clicked.  Override this if you want to place the element in a
16866      * location other than where the cursor is.
16867      * @method setDragElPos
16868      * @param {int} iPageX the X coordinate of the mousedown or drag event
16869      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16870      */
16871     setDragElPos: function(iPageX, iPageY) {
16872         // the first time we do this, we are going to check to make sure
16873         // the element has css positioning
16874
16875         var el = this.getDragEl();
16876         this.alignElWithMouse(el, iPageX, iPageY);
16877     },
16878
16879     /**
16880      * Sets the element to the location of the mousedown or click event,
16881      * maintaining the cursor location relative to the location on the element
16882      * that was clicked.  Override this if you want to place the element in a
16883      * location other than where the cursor is.
16884      * @method alignElWithMouse
16885      * @param {HTMLElement} el the element to move
16886      * @param {int} iPageX the X coordinate of the mousedown or drag event
16887      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16888      */
16889     alignElWithMouse: function(el, iPageX, iPageY) {
16890         var oCoord = this.getTargetCoord(iPageX, iPageY);
16891         var fly = el.dom ? el : Roo.fly(el);
16892         if (!this.deltaSetXY) {
16893             var aCoord = [oCoord.x, oCoord.y];
16894             fly.setXY(aCoord);
16895             var newLeft = fly.getLeft(true);
16896             var newTop  = fly.getTop(true);
16897             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16898         } else {
16899             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16900         }
16901
16902         this.cachePosition(oCoord.x, oCoord.y);
16903         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16904         return oCoord;
16905     },
16906
16907     /**
16908      * Saves the most recent position so that we can reset the constraints and
16909      * tick marks on-demand.  We need to know this so that we can calculate the
16910      * number of pixels the element is offset from its original position.
16911      * @method cachePosition
16912      * @param iPageX the current x position (optional, this just makes it so we
16913      * don't have to look it up again)
16914      * @param iPageY the current y position (optional, this just makes it so we
16915      * don't have to look it up again)
16916      */
16917     cachePosition: function(iPageX, iPageY) {
16918         if (iPageX) {
16919             this.lastPageX = iPageX;
16920             this.lastPageY = iPageY;
16921         } else {
16922             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16923             this.lastPageX = aCoord[0];
16924             this.lastPageY = aCoord[1];
16925         }
16926     },
16927
16928     /**
16929      * Auto-scroll the window if the dragged object has been moved beyond the
16930      * visible window boundary.
16931      * @method autoScroll
16932      * @param {int} x the drag element's x position
16933      * @param {int} y the drag element's y position
16934      * @param {int} h the height of the drag element
16935      * @param {int} w the width of the drag element
16936      * @private
16937      */
16938     autoScroll: function(x, y, h, w) {
16939
16940         if (this.scroll) {
16941             // The client height
16942             var clientH = Roo.lib.Dom.getViewWidth();
16943
16944             // The client width
16945             var clientW = Roo.lib.Dom.getViewHeight();
16946
16947             // The amt scrolled down
16948             var st = this.DDM.getScrollTop();
16949
16950             // The amt scrolled right
16951             var sl = this.DDM.getScrollLeft();
16952
16953             // Location of the bottom of the element
16954             var bot = h + y;
16955
16956             // Location of the right of the element
16957             var right = w + x;
16958
16959             // The distance from the cursor to the bottom of the visible area,
16960             // adjusted so that we don't scroll if the cursor is beyond the
16961             // element drag constraints
16962             var toBot = (clientH + st - y - this.deltaY);
16963
16964             // The distance from the cursor to the right of the visible area
16965             var toRight = (clientW + sl - x - this.deltaX);
16966
16967
16968             // How close to the edge the cursor must be before we scroll
16969             // var thresh = (document.all) ? 100 : 40;
16970             var thresh = 40;
16971
16972             // How many pixels to scroll per autoscroll op.  This helps to reduce
16973             // clunky scrolling. IE is more sensitive about this ... it needs this
16974             // value to be higher.
16975             var scrAmt = (document.all) ? 80 : 30;
16976
16977             // Scroll down if we are near the bottom of the visible page and the
16978             // obj extends below the crease
16979             if ( bot > clientH && toBot < thresh ) {
16980                 window.scrollTo(sl, st + scrAmt);
16981             }
16982
16983             // Scroll up if the window is scrolled down and the top of the object
16984             // goes above the top border
16985             if ( y < st && st > 0 && y - st < thresh ) {
16986                 window.scrollTo(sl, st - scrAmt);
16987             }
16988
16989             // Scroll right if the obj is beyond the right border and the cursor is
16990             // near the border.
16991             if ( right > clientW && toRight < thresh ) {
16992                 window.scrollTo(sl + scrAmt, st);
16993             }
16994
16995             // Scroll left if the window has been scrolled to the right and the obj
16996             // extends past the left border
16997             if ( x < sl && sl > 0 && x - sl < thresh ) {
16998                 window.scrollTo(sl - scrAmt, st);
16999             }
17000         }
17001     },
17002
17003     /**
17004      * Finds the location the element should be placed if we want to move
17005      * it to where the mouse location less the click offset would place us.
17006      * @method getTargetCoord
17007      * @param {int} iPageX the X coordinate of the click
17008      * @param {int} iPageY the Y coordinate of the click
17009      * @return an object that contains the coordinates (Object.x and Object.y)
17010      * @private
17011      */
17012     getTargetCoord: function(iPageX, iPageY) {
17013
17014
17015         var x = iPageX - this.deltaX;
17016         var y = iPageY - this.deltaY;
17017
17018         if (this.constrainX) {
17019             if (x < this.minX) { x = this.minX; }
17020             if (x > this.maxX) { x = this.maxX; }
17021         }
17022
17023         if (this.constrainY) {
17024             if (y < this.minY) { y = this.minY; }
17025             if (y > this.maxY) { y = this.maxY; }
17026         }
17027
17028         x = this.getTick(x, this.xTicks);
17029         y = this.getTick(y, this.yTicks);
17030
17031
17032         return {x:x, y:y};
17033     },
17034
17035     /*
17036      * Sets up config options specific to this class. Overrides
17037      * Roo.dd.DragDrop, but all versions of this method through the
17038      * inheritance chain are called
17039      */
17040     applyConfig: function() {
17041         Roo.dd.DD.superclass.applyConfig.call(this);
17042         this.scroll = (this.config.scroll !== false);
17043     },
17044
17045     /*
17046      * Event that fires prior to the onMouseDown event.  Overrides
17047      * Roo.dd.DragDrop.
17048      */
17049     b4MouseDown: function(e) {
17050         // this.resetConstraints();
17051         this.autoOffset(e.getPageX(),
17052                             e.getPageY());
17053     },
17054
17055     /*
17056      * Event that fires prior to the onDrag event.  Overrides
17057      * Roo.dd.DragDrop.
17058      */
17059     b4Drag: function(e) {
17060         this.setDragElPos(e.getPageX(),
17061                             e.getPageY());
17062     },
17063
17064     toString: function() {
17065         return ("DD " + this.id);
17066     }
17067
17068     //////////////////////////////////////////////////////////////////////////
17069     // Debugging ygDragDrop events that can be overridden
17070     //////////////////////////////////////////////////////////////////////////
17071     /*
17072     startDrag: function(x, y) {
17073     },
17074
17075     onDrag: function(e) {
17076     },
17077
17078     onDragEnter: function(e, id) {
17079     },
17080
17081     onDragOver: function(e, id) {
17082     },
17083
17084     onDragOut: function(e, id) {
17085     },
17086
17087     onDragDrop: function(e, id) {
17088     },
17089
17090     endDrag: function(e) {
17091     }
17092
17093     */
17094
17095 });/*
17096  * Based on:
17097  * Ext JS Library 1.1.1
17098  * Copyright(c) 2006-2007, Ext JS, LLC.
17099  *
17100  * Originally Released Under LGPL - original licence link has changed is not relivant.
17101  *
17102  * Fork - LGPL
17103  * <script type="text/javascript">
17104  */
17105
17106 /**
17107  * @class Roo.dd.DDProxy
17108  * A DragDrop implementation that inserts an empty, bordered div into
17109  * the document that follows the cursor during drag operations.  At the time of
17110  * the click, the frame div is resized to the dimensions of the linked html
17111  * element, and moved to the exact location of the linked element.
17112  *
17113  * References to the "frame" element refer to the single proxy element that
17114  * was created to be dragged in place of all DDProxy elements on the
17115  * page.
17116  *
17117  * @extends Roo.dd.DD
17118  * @constructor
17119  * @param {String} id the id of the linked html element
17120  * @param {String} sGroup the group of related DragDrop objects
17121  * @param {object} config an object containing configurable attributes
17122  *                Valid properties for DDProxy in addition to those in DragDrop:
17123  *                   resizeFrame, centerFrame, dragElId
17124  */
17125 Roo.dd.DDProxy = function(id, sGroup, config) {
17126     if (id) {
17127         this.init(id, sGroup, config);
17128         this.initFrame();
17129     }
17130 };
17131
17132 /**
17133  * The default drag frame div id
17134  * @property Roo.dd.DDProxy.dragElId
17135  * @type String
17136  * @static
17137  */
17138 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17139
17140 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17141
17142     /**
17143      * By default we resize the drag frame to be the same size as the element
17144      * we want to drag (this is to get the frame effect).  We can turn it off
17145      * if we want a different behavior.
17146      * @property resizeFrame
17147      * @type boolean
17148      */
17149     resizeFrame: true,
17150
17151     /**
17152      * By default the frame is positioned exactly where the drag element is, so
17153      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17154      * you do not have constraints on the obj is to have the drag frame centered
17155      * around the cursor.  Set centerFrame to true for this effect.
17156      * @property centerFrame
17157      * @type boolean
17158      */
17159     centerFrame: false,
17160
17161     /**
17162      * Creates the proxy element if it does not yet exist
17163      * @method createFrame
17164      */
17165     createFrame: function() {
17166         var self = this;
17167         var body = document.body;
17168
17169         if (!body || !body.firstChild) {
17170             setTimeout( function() { self.createFrame(); }, 50 );
17171             return;
17172         }
17173
17174         var div = this.getDragEl();
17175
17176         if (!div) {
17177             div    = document.createElement("div");
17178             div.id = this.dragElId;
17179             var s  = div.style;
17180
17181             s.position   = "absolute";
17182             s.visibility = "hidden";
17183             s.cursor     = "move";
17184             s.border     = "2px solid #aaa";
17185             s.zIndex     = 999;
17186
17187             // appendChild can blow up IE if invoked prior to the window load event
17188             // while rendering a table.  It is possible there are other scenarios
17189             // that would cause this to happen as well.
17190             body.insertBefore(div, body.firstChild);
17191         }
17192     },
17193
17194     /**
17195      * Initialization for the drag frame element.  Must be called in the
17196      * constructor of all subclasses
17197      * @method initFrame
17198      */
17199     initFrame: function() {
17200         this.createFrame();
17201     },
17202
17203     applyConfig: function() {
17204         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17205
17206         this.resizeFrame = (this.config.resizeFrame !== false);
17207         this.centerFrame = (this.config.centerFrame);
17208         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17209     },
17210
17211     /**
17212      * Resizes the drag frame to the dimensions of the clicked object, positions
17213      * it over the object, and finally displays it
17214      * @method showFrame
17215      * @param {int} iPageX X click position
17216      * @param {int} iPageY Y click position
17217      * @private
17218      */
17219     showFrame: function(iPageX, iPageY) {
17220         var el = this.getEl();
17221         var dragEl = this.getDragEl();
17222         var s = dragEl.style;
17223
17224         this._resizeProxy();
17225
17226         if (this.centerFrame) {
17227             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17228                            Math.round(parseInt(s.height, 10)/2) );
17229         }
17230
17231         this.setDragElPos(iPageX, iPageY);
17232
17233         Roo.fly(dragEl).show();
17234     },
17235
17236     /**
17237      * The proxy is automatically resized to the dimensions of the linked
17238      * element when a drag is initiated, unless resizeFrame is set to false
17239      * @method _resizeProxy
17240      * @private
17241      */
17242     _resizeProxy: function() {
17243         if (this.resizeFrame) {
17244             var el = this.getEl();
17245             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17246         }
17247     },
17248
17249     // overrides Roo.dd.DragDrop
17250     b4MouseDown: function(e) {
17251         var x = e.getPageX();
17252         var y = e.getPageY();
17253         this.autoOffset(x, y);
17254         this.setDragElPos(x, y);
17255     },
17256
17257     // overrides Roo.dd.DragDrop
17258     b4StartDrag: function(x, y) {
17259         // show the drag frame
17260         this.showFrame(x, y);
17261     },
17262
17263     // overrides Roo.dd.DragDrop
17264     b4EndDrag: function(e) {
17265         Roo.fly(this.getDragEl()).hide();
17266     },
17267
17268     // overrides Roo.dd.DragDrop
17269     // By default we try to move the element to the last location of the frame.
17270     // This is so that the default behavior mirrors that of Roo.dd.DD.
17271     endDrag: function(e) {
17272
17273         var lel = this.getEl();
17274         var del = this.getDragEl();
17275
17276         // Show the drag frame briefly so we can get its position
17277         del.style.visibility = "";
17278
17279         this.beforeMove();
17280         // Hide the linked element before the move to get around a Safari
17281         // rendering bug.
17282         lel.style.visibility = "hidden";
17283         Roo.dd.DDM.moveToEl(lel, del);
17284         del.style.visibility = "hidden";
17285         lel.style.visibility = "";
17286
17287         this.afterDrag();
17288     },
17289
17290     beforeMove : function(){
17291
17292     },
17293
17294     afterDrag : function(){
17295
17296     },
17297
17298     toString: function() {
17299         return ("DDProxy " + this.id);
17300     }
17301
17302 });
17303 /*
17304  * Based on:
17305  * Ext JS Library 1.1.1
17306  * Copyright(c) 2006-2007, Ext JS, LLC.
17307  *
17308  * Originally Released Under LGPL - original licence link has changed is not relivant.
17309  *
17310  * Fork - LGPL
17311  * <script type="text/javascript">
17312  */
17313
17314  /**
17315  * @class Roo.dd.DDTarget
17316  * A DragDrop implementation that does not move, but can be a drop
17317  * target.  You would get the same result by simply omitting implementation
17318  * for the event callbacks, but this way we reduce the processing cost of the
17319  * event listener and the callbacks.
17320  * @extends Roo.dd.DragDrop
17321  * @constructor
17322  * @param {String} id the id of the element that is a drop target
17323  * @param {String} sGroup the group of related DragDrop objects
17324  * @param {object} config an object containing configurable attributes
17325  *                 Valid properties for DDTarget in addition to those in
17326  *                 DragDrop:
17327  *                    none
17328  */
17329 Roo.dd.DDTarget = function(id, sGroup, config) {
17330     if (id) {
17331         this.initTarget(id, sGroup, config);
17332     }
17333 };
17334
17335 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17336 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17337     toString: function() {
17338         return ("DDTarget " + this.id);
17339     }
17340 });
17341 /*
17342  * Based on:
17343  * Ext JS Library 1.1.1
17344  * Copyright(c) 2006-2007, Ext JS, LLC.
17345  *
17346  * Originally Released Under LGPL - original licence link has changed is not relivant.
17347  *
17348  * Fork - LGPL
17349  * <script type="text/javascript">
17350  */
17351  
17352
17353 /**
17354  * @class Roo.dd.ScrollManager
17355  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17356  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17357  * @singleton
17358  */
17359 Roo.dd.ScrollManager = function(){
17360     var ddm = Roo.dd.DragDropMgr;
17361     var els = {};
17362     var dragEl = null;
17363     var proc = {};
17364     
17365     var onStop = function(e){
17366         dragEl = null;
17367         clearProc();
17368     };
17369     
17370     var triggerRefresh = function(){
17371         if(ddm.dragCurrent){
17372              ddm.refreshCache(ddm.dragCurrent.groups);
17373         }
17374     };
17375     
17376     var doScroll = function(){
17377         if(ddm.dragCurrent){
17378             var dds = Roo.dd.ScrollManager;
17379             if(!dds.animate){
17380                 if(proc.el.scroll(proc.dir, dds.increment)){
17381                     triggerRefresh();
17382                 }
17383             }else{
17384                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17385             }
17386         }
17387     };
17388     
17389     var clearProc = function(){
17390         if(proc.id){
17391             clearInterval(proc.id);
17392         }
17393         proc.id = 0;
17394         proc.el = null;
17395         proc.dir = "";
17396     };
17397     
17398     var startProc = function(el, dir){
17399         clearProc();
17400         proc.el = el;
17401         proc.dir = dir;
17402         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17403     };
17404     
17405     var onFire = function(e, isDrop){
17406         if(isDrop || !ddm.dragCurrent){ return; }
17407         var dds = Roo.dd.ScrollManager;
17408         if(!dragEl || dragEl != ddm.dragCurrent){
17409             dragEl = ddm.dragCurrent;
17410             // refresh regions on drag start
17411             dds.refreshCache();
17412         }
17413         
17414         var xy = Roo.lib.Event.getXY(e);
17415         var pt = new Roo.lib.Point(xy[0], xy[1]);
17416         for(var id in els){
17417             var el = els[id], r = el._region;
17418             if(r && r.contains(pt) && el.isScrollable()){
17419                 if(r.bottom - pt.y <= dds.thresh){
17420                     if(proc.el != el){
17421                         startProc(el, "down");
17422                     }
17423                     return;
17424                 }else if(r.right - pt.x <= dds.thresh){
17425                     if(proc.el != el){
17426                         startProc(el, "left");
17427                     }
17428                     return;
17429                 }else if(pt.y - r.top <= dds.thresh){
17430                     if(proc.el != el){
17431                         startProc(el, "up");
17432                     }
17433                     return;
17434                 }else if(pt.x - r.left <= dds.thresh){
17435                     if(proc.el != el){
17436                         startProc(el, "right");
17437                     }
17438                     return;
17439                 }
17440             }
17441         }
17442         clearProc();
17443     };
17444     
17445     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17446     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17447     
17448     return {
17449         /**
17450          * Registers new overflow element(s) to auto scroll
17451          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17452          */
17453         register : function(el){
17454             if(el instanceof Array){
17455                 for(var i = 0, len = el.length; i < len; i++) {
17456                         this.register(el[i]);
17457                 }
17458             }else{
17459                 el = Roo.get(el);
17460                 els[el.id] = el;
17461             }
17462         },
17463         
17464         /**
17465          * Unregisters overflow element(s) so they are no longer scrolled
17466          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17467          */
17468         unregister : function(el){
17469             if(el instanceof Array){
17470                 for(var i = 0, len = el.length; i < len; i++) {
17471                         this.unregister(el[i]);
17472                 }
17473             }else{
17474                 el = Roo.get(el);
17475                 delete els[el.id];
17476             }
17477         },
17478         
17479         /**
17480          * The number of pixels from the edge of a container the pointer needs to be to 
17481          * trigger scrolling (defaults to 25)
17482          * @type Number
17483          */
17484         thresh : 25,
17485         
17486         /**
17487          * The number of pixels to scroll in each scroll increment (defaults to 50)
17488          * @type Number
17489          */
17490         increment : 100,
17491         
17492         /**
17493          * The frequency of scrolls in milliseconds (defaults to 500)
17494          * @type Number
17495          */
17496         frequency : 500,
17497         
17498         /**
17499          * True to animate the scroll (defaults to true)
17500          * @type Boolean
17501          */
17502         animate: true,
17503         
17504         /**
17505          * The animation duration in seconds - 
17506          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17507          * @type Number
17508          */
17509         animDuration: .4,
17510         
17511         /**
17512          * Manually trigger a cache refresh.
17513          */
17514         refreshCache : function(){
17515             for(var id in els){
17516                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17517                     els[id]._region = els[id].getRegion();
17518                 }
17519             }
17520         }
17521     };
17522 }();/*
17523  * Based on:
17524  * Ext JS Library 1.1.1
17525  * Copyright(c) 2006-2007, Ext JS, LLC.
17526  *
17527  * Originally Released Under LGPL - original licence link has changed is not relivant.
17528  *
17529  * Fork - LGPL
17530  * <script type="text/javascript">
17531  */
17532  
17533
17534 /**
17535  * @class Roo.dd.Registry
17536  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17537  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17538  * @singleton
17539  */
17540 Roo.dd.Registry = function(){
17541     var elements = {}; 
17542     var handles = {}; 
17543     var autoIdSeed = 0;
17544
17545     var getId = function(el, autogen){
17546         if(typeof el == "string"){
17547             return el;
17548         }
17549         var id = el.id;
17550         if(!id && autogen !== false){
17551             id = "roodd-" + (++autoIdSeed);
17552             el.id = id;
17553         }
17554         return id;
17555     };
17556     
17557     return {
17558     /**
17559      * Register a drag drop element
17560      * @param {String|HTMLElement} element The id or DOM node to register
17561      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17562      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17563      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17564      * populated in the data object (if applicable):
17565      * <pre>
17566 Value      Description<br />
17567 ---------  ------------------------------------------<br />
17568 handles    Array of DOM nodes that trigger dragging<br />
17569            for the element being registered<br />
17570 isHandle   True if the element passed in triggers<br />
17571            dragging itself, else false
17572 </pre>
17573      */
17574         register : function(el, data){
17575             data = data || {};
17576             if(typeof el == "string"){
17577                 el = document.getElementById(el);
17578             }
17579             data.ddel = el;
17580             elements[getId(el)] = data;
17581             if(data.isHandle !== false){
17582                 handles[data.ddel.id] = data;
17583             }
17584             if(data.handles){
17585                 var hs = data.handles;
17586                 for(var i = 0, len = hs.length; i < len; i++){
17587                         handles[getId(hs[i])] = data;
17588                 }
17589             }
17590         },
17591
17592     /**
17593      * Unregister a drag drop element
17594      * @param {String|HTMLElement}  element The id or DOM node to unregister
17595      */
17596         unregister : function(el){
17597             var id = getId(el, false);
17598             var data = elements[id];
17599             if(data){
17600                 delete elements[id];
17601                 if(data.handles){
17602                     var hs = data.handles;
17603                     for(var i = 0, len = hs.length; i < len; i++){
17604                         delete handles[getId(hs[i], false)];
17605                     }
17606                 }
17607             }
17608         },
17609
17610     /**
17611      * Returns the handle registered for a DOM Node by id
17612      * @param {String|HTMLElement} id The DOM node or id to look up
17613      * @return {Object} handle The custom handle data
17614      */
17615         getHandle : function(id){
17616             if(typeof id != "string"){ // must be element?
17617                 id = id.id;
17618             }
17619             return handles[id];
17620         },
17621
17622     /**
17623      * Returns the handle that is registered for the DOM node that is the target of the event
17624      * @param {Event} e The event
17625      * @return {Object} handle The custom handle data
17626      */
17627         getHandleFromEvent : function(e){
17628             var t = Roo.lib.Event.getTarget(e);
17629             return t ? handles[t.id] : null;
17630         },
17631
17632     /**
17633      * Returns a custom data object that is registered for a DOM node by id
17634      * @param {String|HTMLElement} id The DOM node or id to look up
17635      * @return {Object} data The custom data
17636      */
17637         getTarget : function(id){
17638             if(typeof id != "string"){ // must be element?
17639                 id = id.id;
17640             }
17641             return elements[id];
17642         },
17643
17644     /**
17645      * Returns a custom data object that is registered for the DOM node that is the target of the event
17646      * @param {Event} e The event
17647      * @return {Object} data The custom data
17648      */
17649         getTargetFromEvent : function(e){
17650             var t = Roo.lib.Event.getTarget(e);
17651             return t ? elements[t.id] || handles[t.id] : null;
17652         }
17653     };
17654 }();/*
17655  * Based on:
17656  * Ext JS Library 1.1.1
17657  * Copyright(c) 2006-2007, Ext JS, LLC.
17658  *
17659  * Originally Released Under LGPL - original licence link has changed is not relivant.
17660  *
17661  * Fork - LGPL
17662  * <script type="text/javascript">
17663  */
17664  
17665
17666 /**
17667  * @class Roo.dd.StatusProxy
17668  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17669  * default drag proxy used by all Roo.dd components.
17670  * @constructor
17671  * @param {Object} config
17672  */
17673 Roo.dd.StatusProxy = function(config){
17674     Roo.apply(this, config);
17675     this.id = this.id || Roo.id();
17676     this.el = new Roo.Layer({
17677         dh: {
17678             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17679                 {tag: "div", cls: "x-dd-drop-icon"},
17680                 {tag: "div", cls: "x-dd-drag-ghost"}
17681             ]
17682         }, 
17683         shadow: !config || config.shadow !== false
17684     });
17685     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17686     this.dropStatus = this.dropNotAllowed;
17687 };
17688
17689 Roo.dd.StatusProxy.prototype = {
17690     /**
17691      * @cfg {String} dropAllowed
17692      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17693      */
17694     dropAllowed : "x-dd-drop-ok",
17695     /**
17696      * @cfg {String} dropNotAllowed
17697      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17698      */
17699     dropNotAllowed : "x-dd-drop-nodrop",
17700
17701     /**
17702      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17703      * over the current target element.
17704      * @param {String} cssClass The css class for the new drop status indicator image
17705      */
17706     setStatus : function(cssClass){
17707         cssClass = cssClass || this.dropNotAllowed;
17708         if(this.dropStatus != cssClass){
17709             this.el.replaceClass(this.dropStatus, cssClass);
17710             this.dropStatus = cssClass;
17711         }
17712     },
17713
17714     /**
17715      * Resets the status indicator to the default dropNotAllowed value
17716      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17717      */
17718     reset : function(clearGhost){
17719         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17720         this.dropStatus = this.dropNotAllowed;
17721         if(clearGhost){
17722             this.ghost.update("");
17723         }
17724     },
17725
17726     /**
17727      * Updates the contents of the ghost element
17728      * @param {String} html The html that will replace the current innerHTML of the ghost element
17729      */
17730     update : function(html){
17731         if(typeof html == "string"){
17732             this.ghost.update(html);
17733         }else{
17734             this.ghost.update("");
17735             html.style.margin = "0";
17736             this.ghost.dom.appendChild(html);
17737         }
17738         // ensure float = none set?? cant remember why though.
17739         var el = this.ghost.dom.firstChild;
17740                 if(el){
17741                         Roo.fly(el).setStyle('float', 'none');
17742                 }
17743     },
17744     
17745     /**
17746      * Returns the underlying proxy {@link Roo.Layer}
17747      * @return {Roo.Layer} el
17748     */
17749     getEl : function(){
17750         return this.el;
17751     },
17752
17753     /**
17754      * Returns the ghost element
17755      * @return {Roo.Element} el
17756      */
17757     getGhost : function(){
17758         return this.ghost;
17759     },
17760
17761     /**
17762      * Hides the proxy
17763      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17764      */
17765     hide : function(clear){
17766         this.el.hide();
17767         if(clear){
17768             this.reset(true);
17769         }
17770     },
17771
17772     /**
17773      * Stops the repair animation if it's currently running
17774      */
17775     stop : function(){
17776         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17777             this.anim.stop();
17778         }
17779     },
17780
17781     /**
17782      * Displays this proxy
17783      */
17784     show : function(){
17785         this.el.show();
17786     },
17787
17788     /**
17789      * Force the Layer to sync its shadow and shim positions to the element
17790      */
17791     sync : function(){
17792         this.el.sync();
17793     },
17794
17795     /**
17796      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17797      * invalid drop operation by the item being dragged.
17798      * @param {Array} xy The XY position of the element ([x, y])
17799      * @param {Function} callback The function to call after the repair is complete
17800      * @param {Object} scope The scope in which to execute the callback
17801      */
17802     repair : function(xy, callback, scope){
17803         this.callback = callback;
17804         this.scope = scope;
17805         if(xy && this.animRepair !== false){
17806             this.el.addClass("x-dd-drag-repair");
17807             this.el.hideUnders(true);
17808             this.anim = this.el.shift({
17809                 duration: this.repairDuration || .5,
17810                 easing: 'easeOut',
17811                 xy: xy,
17812                 stopFx: true,
17813                 callback: this.afterRepair,
17814                 scope: this
17815             });
17816         }else{
17817             this.afterRepair();
17818         }
17819     },
17820
17821     // private
17822     afterRepair : function(){
17823         this.hide(true);
17824         if(typeof this.callback == "function"){
17825             this.callback.call(this.scope || this);
17826         }
17827         this.callback = null;
17828         this.scope = null;
17829     }
17830 };/*
17831  * Based on:
17832  * Ext JS Library 1.1.1
17833  * Copyright(c) 2006-2007, Ext JS, LLC.
17834  *
17835  * Originally Released Under LGPL - original licence link has changed is not relivant.
17836  *
17837  * Fork - LGPL
17838  * <script type="text/javascript">
17839  */
17840
17841 /**
17842  * @class Roo.dd.DragSource
17843  * @extends Roo.dd.DDProxy
17844  * A simple class that provides the basic implementation needed to make any element draggable.
17845  * @constructor
17846  * @param {String/HTMLElement/Element} el The container element
17847  * @param {Object} config
17848  */
17849 Roo.dd.DragSource = function(el, config){
17850     this.el = Roo.get(el);
17851     this.dragData = {};
17852     
17853     Roo.apply(this, config);
17854     
17855     if(!this.proxy){
17856         this.proxy = new Roo.dd.StatusProxy();
17857     }
17858
17859     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17860           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17861     
17862     this.dragging = false;
17863 };
17864
17865 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17866     /**
17867      * @cfg {String} dropAllowed
17868      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17869      */
17870     dropAllowed : "x-dd-drop-ok",
17871     /**
17872      * @cfg {String} dropNotAllowed
17873      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17874      */
17875     dropNotAllowed : "x-dd-drop-nodrop",
17876
17877     /**
17878      * Returns the data object associated with this drag source
17879      * @return {Object} data An object containing arbitrary data
17880      */
17881     getDragData : function(e){
17882         return this.dragData;
17883     },
17884
17885     // private
17886     onDragEnter : function(e, id){
17887         var target = Roo.dd.DragDropMgr.getDDById(id);
17888         this.cachedTarget = target;
17889         if(this.beforeDragEnter(target, e, id) !== false){
17890             if(target.isNotifyTarget){
17891                 var status = target.notifyEnter(this, e, this.dragData);
17892                 this.proxy.setStatus(status);
17893             }else{
17894                 this.proxy.setStatus(this.dropAllowed);
17895             }
17896             
17897             if(this.afterDragEnter){
17898                 /**
17899                  * An empty function by default, but provided so that you can perform a custom action
17900                  * when the dragged item enters the drop target by providing an implementation.
17901                  * @param {Roo.dd.DragDrop} target The drop target
17902                  * @param {Event} e The event object
17903                  * @param {String} id The id of the dragged element
17904                  * @method afterDragEnter
17905                  */
17906                 this.afterDragEnter(target, e, id);
17907             }
17908         }
17909     },
17910
17911     /**
17912      * An empty function by default, but provided so that you can perform a custom action
17913      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17914      * @param {Roo.dd.DragDrop} target The drop target
17915      * @param {Event} e The event object
17916      * @param {String} id The id of the dragged element
17917      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17918      */
17919     beforeDragEnter : function(target, e, id){
17920         return true;
17921     },
17922
17923     // private
17924     alignElWithMouse: function() {
17925         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17926         this.proxy.sync();
17927     },
17928
17929     // private
17930     onDragOver : function(e, id){
17931         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17932         if(this.beforeDragOver(target, e, id) !== false){
17933             if(target.isNotifyTarget){
17934                 var status = target.notifyOver(this, e, this.dragData);
17935                 this.proxy.setStatus(status);
17936             }
17937
17938             if(this.afterDragOver){
17939                 /**
17940                  * An empty function by default, but provided so that you can perform a custom action
17941                  * while the dragged item is over the drop target by providing an implementation.
17942                  * @param {Roo.dd.DragDrop} target The drop target
17943                  * @param {Event} e The event object
17944                  * @param {String} id The id of the dragged element
17945                  * @method afterDragOver
17946                  */
17947                 this.afterDragOver(target, e, id);
17948             }
17949         }
17950     },
17951
17952     /**
17953      * An empty function by default, but provided so that you can perform a custom action
17954      * while the dragged item is over the drop target and optionally cancel the onDragOver.
17955      * @param {Roo.dd.DragDrop} target The drop target
17956      * @param {Event} e The event object
17957      * @param {String} id The id of the dragged element
17958      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17959      */
17960     beforeDragOver : function(target, e, id){
17961         return true;
17962     },
17963
17964     // private
17965     onDragOut : function(e, id){
17966         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
17967         if(this.beforeDragOut(target, e, id) !== false){
17968             if(target.isNotifyTarget){
17969                 target.notifyOut(this, e, this.dragData);
17970             }
17971             this.proxy.reset();
17972             if(this.afterDragOut){
17973                 /**
17974                  * An empty function by default, but provided so that you can perform a custom action
17975                  * after the dragged item is dragged out of the target without dropping.
17976                  * @param {Roo.dd.DragDrop} target The drop target
17977                  * @param {Event} e The event object
17978                  * @param {String} id The id of the dragged element
17979                  * @method afterDragOut
17980                  */
17981                 this.afterDragOut(target, e, id);
17982             }
17983         }
17984         this.cachedTarget = null;
17985     },
17986
17987     /**
17988      * An empty function by default, but provided so that you can perform a custom action before the dragged
17989      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
17990      * @param {Roo.dd.DragDrop} target The drop target
17991      * @param {Event} e The event object
17992      * @param {String} id The id of the dragged element
17993      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17994      */
17995     beforeDragOut : function(target, e, id){
17996         return true;
17997     },
17998     
17999     // private
18000     onDragDrop : function(e, id){
18001         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18002         if(this.beforeDragDrop(target, e, id) !== false){
18003             if(target.isNotifyTarget){
18004                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18005                     this.onValidDrop(target, e, id);
18006                 }else{
18007                     this.onInvalidDrop(target, e, id);
18008                 }
18009             }else{
18010                 this.onValidDrop(target, e, id);
18011             }
18012             
18013             if(this.afterDragDrop){
18014                 /**
18015                  * An empty function by default, but provided so that you can perform a custom action
18016                  * after a valid drag drop has occurred by providing an implementation.
18017                  * @param {Roo.dd.DragDrop} target The drop target
18018                  * @param {Event} e The event object
18019                  * @param {String} id The id of the dropped element
18020                  * @method afterDragDrop
18021                  */
18022                 this.afterDragDrop(target, e, id);
18023             }
18024         }
18025         delete this.cachedTarget;
18026     },
18027
18028     /**
18029      * An empty function by default, but provided so that you can perform a custom action before the dragged
18030      * item is dropped onto the target and optionally cancel the onDragDrop.
18031      * @param {Roo.dd.DragDrop} target The drop target
18032      * @param {Event} e The event object
18033      * @param {String} id The id of the dragged element
18034      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18035      */
18036     beforeDragDrop : function(target, e, id){
18037         return true;
18038     },
18039
18040     // private
18041     onValidDrop : function(target, e, id){
18042         this.hideProxy();
18043         if(this.afterValidDrop){
18044             /**
18045              * An empty function by default, but provided so that you can perform a custom action
18046              * after a valid drop has occurred by providing an implementation.
18047              * @param {Object} target The target DD 
18048              * @param {Event} e The event object
18049              * @param {String} id The id of the dropped element
18050              * @method afterInvalidDrop
18051              */
18052             this.afterValidDrop(target, e, id);
18053         }
18054     },
18055
18056     // private
18057     getRepairXY : function(e, data){
18058         return this.el.getXY();  
18059     },
18060
18061     // private
18062     onInvalidDrop : function(target, e, id){
18063         this.beforeInvalidDrop(target, e, id);
18064         if(this.cachedTarget){
18065             if(this.cachedTarget.isNotifyTarget){
18066                 this.cachedTarget.notifyOut(this, e, this.dragData);
18067             }
18068             this.cacheTarget = null;
18069         }
18070         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18071
18072         if(this.afterInvalidDrop){
18073             /**
18074              * An empty function by default, but provided so that you can perform a custom action
18075              * after an invalid drop has occurred by providing an implementation.
18076              * @param {Event} e The event object
18077              * @param {String} id The id of the dropped element
18078              * @method afterInvalidDrop
18079              */
18080             this.afterInvalidDrop(e, id);
18081         }
18082     },
18083
18084     // private
18085     afterRepair : function(){
18086         if(Roo.enableFx){
18087             this.el.highlight(this.hlColor || "c3daf9");
18088         }
18089         this.dragging = false;
18090     },
18091
18092     /**
18093      * An empty function by default, but provided so that you can perform a custom action after an invalid
18094      * drop has occurred.
18095      * @param {Roo.dd.DragDrop} target The drop target
18096      * @param {Event} e The event object
18097      * @param {String} id The id of the dragged element
18098      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18099      */
18100     beforeInvalidDrop : function(target, e, id){
18101         return true;
18102     },
18103
18104     // private
18105     handleMouseDown : function(e){
18106         if(this.dragging) {
18107             return;
18108         }
18109         var data = this.getDragData(e);
18110         if(data && this.onBeforeDrag(data, e) !== false){
18111             this.dragData = data;
18112             this.proxy.stop();
18113             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18114         } 
18115     },
18116
18117     /**
18118      * An empty function by default, but provided so that you can perform a custom action before the initial
18119      * drag event begins and optionally cancel it.
18120      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18121      * @param {Event} e The event object
18122      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18123      */
18124     onBeforeDrag : function(data, e){
18125         return true;
18126     },
18127
18128     /**
18129      * An empty function by default, but provided so that you can perform a custom action once the initial
18130      * drag event has begun.  The drag cannot be canceled from this function.
18131      * @param {Number} x The x position of the click on the dragged object
18132      * @param {Number} y The y position of the click on the dragged object
18133      */
18134     onStartDrag : Roo.emptyFn,
18135
18136     // private - YUI override
18137     startDrag : function(x, y){
18138         this.proxy.reset();
18139         this.dragging = true;
18140         this.proxy.update("");
18141         this.onInitDrag(x, y);
18142         this.proxy.show();
18143     },
18144
18145     // private
18146     onInitDrag : function(x, y){
18147         var clone = this.el.dom.cloneNode(true);
18148         clone.id = Roo.id(); // prevent duplicate ids
18149         this.proxy.update(clone);
18150         this.onStartDrag(x, y);
18151         return true;
18152     },
18153
18154     /**
18155      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18156      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18157      */
18158     getProxy : function(){
18159         return this.proxy;  
18160     },
18161
18162     /**
18163      * Hides the drag source's {@link Roo.dd.StatusProxy}
18164      */
18165     hideProxy : function(){
18166         this.proxy.hide();  
18167         this.proxy.reset(true);
18168         this.dragging = false;
18169     },
18170
18171     // private
18172     triggerCacheRefresh : function(){
18173         Roo.dd.DDM.refreshCache(this.groups);
18174     },
18175
18176     // private - override to prevent hiding
18177     b4EndDrag: function(e) {
18178     },
18179
18180     // private - override to prevent moving
18181     endDrag : function(e){
18182         this.onEndDrag(this.dragData, e);
18183     },
18184
18185     // private
18186     onEndDrag : function(data, e){
18187     },
18188     
18189     // private - pin to cursor
18190     autoOffset : function(x, y) {
18191         this.setDelta(-12, -20);
18192     }    
18193 });/*
18194  * Based on:
18195  * Ext JS Library 1.1.1
18196  * Copyright(c) 2006-2007, Ext JS, LLC.
18197  *
18198  * Originally Released Under LGPL - original licence link has changed is not relivant.
18199  *
18200  * Fork - LGPL
18201  * <script type="text/javascript">
18202  */
18203
18204
18205 /**
18206  * @class Roo.dd.DropTarget
18207  * @extends Roo.dd.DDTarget
18208  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18209  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18210  * @constructor
18211  * @param {String/HTMLElement/Element} el The container element
18212  * @param {Object} config
18213  */
18214 Roo.dd.DropTarget = function(el, config){
18215     this.el = Roo.get(el);
18216     
18217     Roo.apply(this, config);
18218     
18219     if(this.containerScroll){
18220         Roo.dd.ScrollManager.register(this.el);
18221     }
18222     
18223     Roo.dd.DropTarget.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group, 
18224           {isTarget: true});
18225
18226 };
18227
18228 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18229     /**
18230      * @cfg {String} overClass
18231      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18232      */
18233     /**
18234      * @cfg {String} dropAllowed
18235      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18236      */
18237     dropAllowed : "x-dd-drop-ok",
18238     /**
18239      * @cfg {String} dropNotAllowed
18240      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18241      */
18242     dropNotAllowed : "x-dd-drop-nodrop",
18243
18244     // private
18245     isTarget : true,
18246
18247     // private
18248     isNotifyTarget : true,
18249
18250     /**
18251      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18252      * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18253      * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18254      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18255      * @param {Event} e The event
18256      * @param {Object} data An object containing arbitrary data supplied by the drag source
18257      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18258      * underlying {@link Roo.dd.StatusProxy} can be updated
18259      */
18260     notifyEnter : function(dd, e, data){
18261         if(this.overClass){
18262             this.el.addClass(this.overClass);
18263         }
18264         return this.dropAllowed;
18265     },
18266
18267     /**
18268      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18269      * This method will be called on every mouse movement while the drag source is over the drop target.
18270      * This default implementation simply returns the dropAllowed config value.
18271      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18272      * @param {Event} e The event
18273      * @param {Object} data An object containing arbitrary data supplied by the drag source
18274      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18275      * underlying {@link Roo.dd.StatusProxy} can be updated
18276      */
18277     notifyOver : function(dd, e, data){
18278         return this.dropAllowed;
18279     },
18280
18281     /**
18282      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18283      * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18284      * overClass (if any) from the drop element.
18285      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18286      * @param {Event} e The event
18287      * @param {Object} data An object containing arbitrary data supplied by the drag source
18288      */
18289     notifyOut : function(dd, e, data){
18290         if(this.overClass){
18291             this.el.removeClass(this.overClass);
18292         }
18293     },
18294
18295     /**
18296      * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18297      * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18298      * implementation that does something to process the drop event and returns true so that the drag source's
18299      * repair action does not run.
18300      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18301      * @param {Event} e The event
18302      * @param {Object} data An object containing arbitrary data supplied by the drag source
18303      * @return {Boolean} True if the drop was valid, else false
18304      */
18305     notifyDrop : function(dd, e, data){
18306         return false;
18307     }
18308 });/*
18309  * Based on:
18310  * Ext JS Library 1.1.1
18311  * Copyright(c) 2006-2007, Ext JS, LLC.
18312  *
18313  * Originally Released Under LGPL - original licence link has changed is not relivant.
18314  *
18315  * Fork - LGPL
18316  * <script type="text/javascript">
18317  */
18318
18319
18320 /**
18321  * @class Roo.dd.DragZone
18322  * @extends Roo.dd.DragSource
18323  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18324  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18325  * @constructor
18326  * @param {String/HTMLElement/Element} el The container element
18327  * @param {Object} config
18328  */
18329 Roo.dd.DragZone = function(el, config){
18330     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18331     if(this.containerScroll){
18332         Roo.dd.ScrollManager.register(this.el);
18333     }
18334 };
18335
18336 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18337     /**
18338      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18339      * for auto scrolling during drag operations.
18340      */
18341     /**
18342      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18343      * method after a failed drop (defaults to "c3daf9" - light blue)
18344      */
18345
18346     /**
18347      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18348      * for a valid target to drag based on the mouse down. Override this method
18349      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18350      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18351      * @param {EventObject} e The mouse down event
18352      * @return {Object} The dragData
18353      */
18354     getDragData : function(e){
18355         return Roo.dd.Registry.getHandleFromEvent(e);
18356     },
18357     
18358     /**
18359      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18360      * this.dragData.ddel
18361      * @param {Number} x The x position of the click on the dragged object
18362      * @param {Number} y The y position of the click on the dragged object
18363      * @return {Boolean} true to continue the drag, false to cancel
18364      */
18365     onInitDrag : function(x, y){
18366         this.proxy.update(this.dragData.ddel.cloneNode(true));
18367         this.onStartDrag(x, y);
18368         return true;
18369     },
18370     
18371     /**
18372      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18373      */
18374     afterRepair : function(){
18375         if(Roo.enableFx){
18376             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18377         }
18378         this.dragging = false;
18379     },
18380
18381     /**
18382      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18383      * the XY of this.dragData.ddel
18384      * @param {EventObject} e The mouse up event
18385      * @return {Array} The xy location (e.g. [100, 200])
18386      */
18387     getRepairXY : function(e){
18388         return Roo.Element.fly(this.dragData.ddel).getXY();  
18389     }
18390 });/*
18391  * Based on:
18392  * Ext JS Library 1.1.1
18393  * Copyright(c) 2006-2007, Ext JS, LLC.
18394  *
18395  * Originally Released Under LGPL - original licence link has changed is not relivant.
18396  *
18397  * Fork - LGPL
18398  * <script type="text/javascript">
18399  */
18400 /**
18401  * @class Roo.dd.DropZone
18402  * @extends Roo.dd.DropTarget
18403  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18404  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18405  * @constructor
18406  * @param {String/HTMLElement/Element} el The container element
18407  * @param {Object} config
18408  */
18409 Roo.dd.DropZone = function(el, config){
18410     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18411 };
18412
18413 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18414     /**
18415      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18416      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18417      * provide your own custom lookup.
18418      * @param {Event} e The event
18419      * @return {Object} data The custom data
18420      */
18421     getTargetFromEvent : function(e){
18422         return Roo.dd.Registry.getTargetFromEvent(e);
18423     },
18424
18425     /**
18426      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18427      * that it has registered.  This method has no default implementation and should be overridden to provide
18428      * node-specific processing if necessary.
18429      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18430      * {@link #getTargetFromEvent} for this node)
18431      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18432      * @param {Event} e The event
18433      * @param {Object} data An object containing arbitrary data supplied by the drag source
18434      */
18435     onNodeEnter : function(n, dd, e, data){
18436         
18437     },
18438
18439     /**
18440      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18441      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18442      * overridden to provide the proper feedback.
18443      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18444      * {@link #getTargetFromEvent} for this node)
18445      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18446      * @param {Event} e The event
18447      * @param {Object} data An object containing arbitrary data supplied by the drag source
18448      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18449      * underlying {@link Roo.dd.StatusProxy} can be updated
18450      */
18451     onNodeOver : function(n, dd, e, data){
18452         return this.dropAllowed;
18453     },
18454
18455     /**
18456      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18457      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18458      * node-specific processing if necessary.
18459      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18460      * {@link #getTargetFromEvent} for this node)
18461      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18462      * @param {Event} e The event
18463      * @param {Object} data An object containing arbitrary data supplied by the drag source
18464      */
18465     onNodeOut : function(n, dd, e, data){
18466         
18467     },
18468
18469     /**
18470      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18471      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18472      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18473      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18474      * {@link #getTargetFromEvent} for this node)
18475      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18476      * @param {Event} e The event
18477      * @param {Object} data An object containing arbitrary data supplied by the drag source
18478      * @return {Boolean} True if the drop was valid, else false
18479      */
18480     onNodeDrop : function(n, dd, e, data){
18481         return false;
18482     },
18483
18484     /**
18485      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18486      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18487      * it should be overridden to provide the proper feedback if necessary.
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     onContainerOver : function(dd, e, data){
18495         return this.dropNotAllowed;
18496     },
18497
18498     /**
18499      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18500      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18501      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18502      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18503      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18504      * @param {Event} e The event
18505      * @param {Object} data An object containing arbitrary data supplied by the drag source
18506      * @return {Boolean} True if the drop was valid, else false
18507      */
18508     onContainerDrop : function(dd, e, data){
18509         return false;
18510     },
18511
18512     /**
18513      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18514      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18515      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18516      * you should override this method and provide a custom implementation.
18517      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18518      * @param {Event} e The event
18519      * @param {Object} data An object containing arbitrary data supplied by the drag source
18520      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18521      * underlying {@link Roo.dd.StatusProxy} can be updated
18522      */
18523     notifyEnter : function(dd, e, data){
18524         return this.dropNotAllowed;
18525     },
18526
18527     /**
18528      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18529      * This method will be called on every mouse movement while the drag source is over the drop zone.
18530      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18531      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18532      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18533      * registered node, it will call {@link #onContainerOver}.
18534      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18535      * @param {Event} e The event
18536      * @param {Object} data An object containing arbitrary data supplied by the drag source
18537      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18538      * underlying {@link Roo.dd.StatusProxy} can be updated
18539      */
18540     notifyOver : function(dd, e, data){
18541         var n = this.getTargetFromEvent(e);
18542         if(!n){ // not over valid drop target
18543             if(this.lastOverNode){
18544                 this.onNodeOut(this.lastOverNode, dd, e, data);
18545                 this.lastOverNode = null;
18546             }
18547             return this.onContainerOver(dd, e, data);
18548         }
18549         if(this.lastOverNode != n){
18550             if(this.lastOverNode){
18551                 this.onNodeOut(this.lastOverNode, dd, e, data);
18552             }
18553             this.onNodeEnter(n, dd, e, data);
18554             this.lastOverNode = n;
18555         }
18556         return this.onNodeOver(n, dd, e, data);
18557     },
18558
18559     /**
18560      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18561      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18562      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18563      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18564      * @param {Event} e The event
18565      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18566      */
18567     notifyOut : function(dd, e, data){
18568         if(this.lastOverNode){
18569             this.onNodeOut(this.lastOverNode, dd, e, data);
18570             this.lastOverNode = null;
18571         }
18572     },
18573
18574     /**
18575      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18576      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18577      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18578      * otherwise it will call {@link #onContainerDrop}.
18579      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18580      * @param {Event} e The event
18581      * @param {Object} data An object containing arbitrary data supplied by the drag source
18582      * @return {Boolean} True if the drop was valid, else false
18583      */
18584     notifyDrop : function(dd, e, data){
18585         if(this.lastOverNode){
18586             this.onNodeOut(this.lastOverNode, dd, e, data);
18587             this.lastOverNode = null;
18588         }
18589         var n = this.getTargetFromEvent(e);
18590         return n ?
18591             this.onNodeDrop(n, dd, e, data) :
18592             this.onContainerDrop(dd, e, data);
18593     },
18594
18595     // private
18596     triggerCacheRefresh : function(){
18597         Roo.dd.DDM.refreshCache(this.groups);
18598     }  
18599 });/*
18600  * Based on:
18601  * Ext JS Library 1.1.1
18602  * Copyright(c) 2006-2007, Ext JS, LLC.
18603  *
18604  * Originally Released Under LGPL - original licence link has changed is not relivant.
18605  *
18606  * Fork - LGPL
18607  * <script type="text/javascript">
18608  */
18609
18610
18611 /**
18612  * @class Roo.data.SortTypes
18613  * @singleton
18614  * Defines the default sorting (casting?) comparison functions used when sorting data.
18615  */
18616 Roo.data.SortTypes = {
18617     /**
18618      * Default sort that does nothing
18619      * @param {Mixed} s The value being converted
18620      * @return {Mixed} The comparison value
18621      */
18622     none : function(s){
18623         return s;
18624     },
18625     
18626     /**
18627      * The regular expression used to strip tags
18628      * @type {RegExp}
18629      * @property
18630      */
18631     stripTagsRE : /<\/?[^>]+>/gi,
18632     
18633     /**
18634      * Strips all HTML tags to sort on text only
18635      * @param {Mixed} s The value being converted
18636      * @return {String} The comparison value
18637      */
18638     asText : function(s){
18639         return String(s).replace(this.stripTagsRE, "");
18640     },
18641     
18642     /**
18643      * Strips all HTML tags to sort on text only - Case insensitive
18644      * @param {Mixed} s The value being converted
18645      * @return {String} The comparison value
18646      */
18647     asUCText : function(s){
18648         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18649     },
18650     
18651     /**
18652      * Case insensitive string
18653      * @param {Mixed} s The value being converted
18654      * @return {String} The comparison value
18655      */
18656     asUCString : function(s) {
18657         return String(s).toUpperCase();
18658     },
18659     
18660     /**
18661      * Date sorting
18662      * @param {Mixed} s The value being converted
18663      * @return {Number} The comparison value
18664      */
18665     asDate : function(s) {
18666         if(!s){
18667             return 0;
18668         }
18669         if(s instanceof Date){
18670             return s.getTime();
18671         }
18672         return Date.parse(String(s));
18673     },
18674     
18675     /**
18676      * Float sorting
18677      * @param {Mixed} s The value being converted
18678      * @return {Float} The comparison value
18679      */
18680     asFloat : function(s) {
18681         var val = parseFloat(String(s).replace(/,/g, ""));
18682         if(isNaN(val)) val = 0;
18683         return val;
18684     },
18685     
18686     /**
18687      * Integer sorting
18688      * @param {Mixed} s The value being converted
18689      * @return {Number} The comparison value
18690      */
18691     asInt : function(s) {
18692         var val = parseInt(String(s).replace(/,/g, ""));
18693         if(isNaN(val)) val = 0;
18694         return val;
18695     }
18696 };/*
18697  * Based on:
18698  * Ext JS Library 1.1.1
18699  * Copyright(c) 2006-2007, Ext JS, LLC.
18700  *
18701  * Originally Released Under LGPL - original licence link has changed is not relivant.
18702  *
18703  * Fork - LGPL
18704  * <script type="text/javascript">
18705  */
18706
18707 /**
18708 * @class Roo.data.Record
18709  * Instances of this class encapsulate both record <em>definition</em> information, and record
18710  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18711  * to access Records cached in an {@link Roo.data.Store} object.<br>
18712  * <p>
18713  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18714  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18715  * objects.<br>
18716  * <p>
18717  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18718  * @constructor
18719  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18720  * {@link #create}. The parameters are the same.
18721  * @param {Array} data An associative Array of data values keyed by the field name.
18722  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18723  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18724  * not specified an integer id is generated.
18725  */
18726 Roo.data.Record = function(data, id){
18727     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18728     this.data = data;
18729 };
18730
18731 /**
18732  * Generate a constructor for a specific record layout.
18733  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18734  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18735  * Each field definition object may contain the following properties: <ul>
18736  * <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,
18737  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18738  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18739  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18740  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18741  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18742  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18743  * this may be omitted.</p></li>
18744  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18745  * <ul><li>auto (Default, implies no conversion)</li>
18746  * <li>string</li>
18747  * <li>int</li>
18748  * <li>float</li>
18749  * <li>boolean</li>
18750  * <li>date</li></ul></p></li>
18751  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18752  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18753  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18754  * by the Reader into an object that will be stored in the Record. It is passed the
18755  * following parameters:<ul>
18756  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18757  * </ul></p></li>
18758  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18759  * </ul>
18760  * <br>usage:<br><pre><code>
18761 var TopicRecord = Roo.data.Record.create(
18762     {name: 'title', mapping: 'topic_title'},
18763     {name: 'author', mapping: 'username'},
18764     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18765     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18766     {name: 'lastPoster', mapping: 'user2'},
18767     {name: 'excerpt', mapping: 'post_text'}
18768 );
18769
18770 var myNewRecord = new TopicRecord({
18771     title: 'Do my job please',
18772     author: 'noobie',
18773     totalPosts: 1,
18774     lastPost: new Date(),
18775     lastPoster: 'Animal',
18776     excerpt: 'No way dude!'
18777 });
18778 myStore.add(myNewRecord);
18779 </code></pre>
18780  * @method create
18781  * @static
18782  */
18783 Roo.data.Record.create = function(o){
18784     var f = function(){
18785         f.superclass.constructor.apply(this, arguments);
18786     };
18787     Roo.extend(f, Roo.data.Record);
18788     var p = f.prototype;
18789     p.fields = new Roo.util.MixedCollection(false, function(field){
18790         return field.name;
18791     });
18792     for(var i = 0, len = o.length; i < len; i++){
18793         p.fields.add(new Roo.data.Field(o[i]));
18794     }
18795     f.getField = function(name){
18796         return p.fields.get(name);  
18797     };
18798     return f;
18799 };
18800
18801 Roo.data.Record.AUTO_ID = 1000;
18802 Roo.data.Record.EDIT = 'edit';
18803 Roo.data.Record.REJECT = 'reject';
18804 Roo.data.Record.COMMIT = 'commit';
18805
18806 Roo.data.Record.prototype = {
18807     /**
18808      * Readonly flag - true if this record has been modified.
18809      * @type Boolean
18810      */
18811     dirty : false,
18812     editing : false,
18813     error: null,
18814     modified: null,
18815
18816     // private
18817     join : function(store){
18818         this.store = store;
18819     },
18820
18821     /**
18822      * Set the named field to the specified value.
18823      * @param {String} name The name of the field to set.
18824      * @param {Object} value The value to set the field to.
18825      */
18826     set : function(name, value){
18827         if(this.data[name] == value){
18828             return;
18829         }
18830         this.dirty = true;
18831         if(!this.modified){
18832             this.modified = {};
18833         }
18834         if(typeof this.modified[name] == 'undefined'){
18835             this.modified[name] = this.data[name];
18836         }
18837         this.data[name] = value;
18838         if(!this.editing){
18839             this.store.afterEdit(this);
18840         }       
18841     },
18842
18843     /**
18844      * Get the value of the named field.
18845      * @param {String} name The name of the field to get the value of.
18846      * @return {Object} The value of the field.
18847      */
18848     get : function(name){
18849         return this.data[name]; 
18850     },
18851
18852     // private
18853     beginEdit : function(){
18854         this.editing = true;
18855         this.modified = {}; 
18856     },
18857
18858     // private
18859     cancelEdit : function(){
18860         this.editing = false;
18861         delete this.modified;
18862     },
18863
18864     // private
18865     endEdit : function(){
18866         this.editing = false;
18867         if(this.dirty && this.store){
18868             this.store.afterEdit(this);
18869         }
18870     },
18871
18872     /**
18873      * Usually called by the {@link Roo.data.Store} which owns the Record.
18874      * Rejects all changes made to the Record since either creation, or the last commit operation.
18875      * Modified fields are reverted to their original values.
18876      * <p>
18877      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18878      * of reject operations.
18879      */
18880     reject : function(){
18881         var m = this.modified;
18882         for(var n in m){
18883             if(typeof m[n] != "function"){
18884                 this.data[n] = m[n];
18885             }
18886         }
18887         this.dirty = false;
18888         delete this.modified;
18889         this.editing = false;
18890         if(this.store){
18891             this.store.afterReject(this);
18892         }
18893     },
18894
18895     /**
18896      * Usually called by the {@link Roo.data.Store} which owns the Record.
18897      * Commits all changes made to the Record since either creation, or the last commit operation.
18898      * <p>
18899      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
18900      * of commit operations.
18901      */
18902     commit : function(){
18903         this.dirty = false;
18904         delete this.modified;
18905         this.editing = false;
18906         if(this.store){
18907             this.store.afterCommit(this);
18908         }
18909     },
18910
18911     // private
18912     hasError : function(){
18913         return this.error != null;
18914     },
18915
18916     // private
18917     clearError : function(){
18918         this.error = null;
18919     },
18920
18921     /**
18922      * Creates a copy of this record.
18923      * @param {String} id (optional) A new record id if you don't want to use this record's id
18924      * @return {Record}
18925      */
18926     copy : function(newId) {
18927         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
18928     }
18929 };/*
18930  * Based on:
18931  * Ext JS Library 1.1.1
18932  * Copyright(c) 2006-2007, Ext JS, LLC.
18933  *
18934  * Originally Released Under LGPL - original licence link has changed is not relivant.
18935  *
18936  * Fork - LGPL
18937  * <script type="text/javascript">
18938  */
18939
18940
18941
18942 /**
18943  * @class Roo.data.Store
18944  * @extends Roo.util.Observable
18945  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
18946  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
18947  * <p>
18948  * 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
18949  * has no knowledge of the format of the data returned by the Proxy.<br>
18950  * <p>
18951  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
18952  * instances from the data object. These records are cached and made available through accessor functions.
18953  * @constructor
18954  * Creates a new Store.
18955  * @param {Object} config A config object containing the objects needed for the Store to access data,
18956  * and read the data into Records.
18957  */
18958 Roo.data.Store = function(config){
18959     this.data = new Roo.util.MixedCollection(false);
18960     this.data.getKey = function(o){
18961         return o.id;
18962     };
18963     this.baseParams = {};
18964     // private
18965     this.paramNames = {
18966         "start" : "start",
18967         "limit" : "limit",
18968         "sort" : "sort",
18969         "dir" : "dir"
18970     };
18971
18972     if(config && config.data){
18973         this.inlineData = config.data;
18974         delete config.data;
18975     }
18976
18977     Roo.apply(this, config);
18978     
18979     if(this.reader){ // reader passed
18980         this.reader = Roo.factory(this.reader, Roo.data);
18981         this.reader.xmodule = this.xmodule || false;
18982         if(!this.recordType){
18983             this.recordType = this.reader.recordType;
18984         }
18985         if(this.reader.onMetaChange){
18986             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
18987         }
18988     }
18989
18990     if(this.recordType){
18991         this.fields = this.recordType.prototype.fields;
18992     }
18993     this.modified = [];
18994
18995     this.addEvents({
18996         /**
18997          * @event datachanged
18998          * Fires when the data cache has changed, and a widget which is using this Store
18999          * as a Record cache should refresh its view.
19000          * @param {Store} this
19001          */
19002         datachanged : true,
19003         /**
19004          * @event metachange
19005          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19006          * @param {Store} this
19007          * @param {Object} meta The JSON metadata
19008          */
19009         metachange : true,
19010         /**
19011          * @event add
19012          * Fires when Records have been added to the Store
19013          * @param {Store} this
19014          * @param {Roo.data.Record[]} records The array of Records added
19015          * @param {Number} index The index at which the record(s) were added
19016          */
19017         add : true,
19018         /**
19019          * @event remove
19020          * Fires when a Record has been removed from the Store
19021          * @param {Store} this
19022          * @param {Roo.data.Record} record The Record that was removed
19023          * @param {Number} index The index at which the record was removed
19024          */
19025         remove : true,
19026         /**
19027          * @event update
19028          * Fires when a Record has been updated
19029          * @param {Store} this
19030          * @param {Roo.data.Record} record The Record that was updated
19031          * @param {String} operation The update operation being performed.  Value may be one of:
19032          * <pre><code>
19033  Roo.data.Record.EDIT
19034  Roo.data.Record.REJECT
19035  Roo.data.Record.COMMIT
19036          * </code></pre>
19037          */
19038         update : true,
19039         /**
19040          * @event clear
19041          * Fires when the data cache has been cleared.
19042          * @param {Store} this
19043          */
19044         clear : true,
19045         /**
19046          * @event beforeload
19047          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19048          * the load action will be canceled.
19049          * @param {Store} this
19050          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19051          */
19052         beforeload : true,
19053         /**
19054          * @event load
19055          * Fires after a new set of Records has been loaded.
19056          * @param {Store} this
19057          * @param {Roo.data.Record[]} records The Records that were loaded
19058          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19059          */
19060         load : true,
19061         /**
19062          * @event loadexception
19063          * Fires if an exception occurs in the Proxy during loading.
19064          * Called with the signature of the Proxy's "loadexception" event.
19065          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19066          * 
19067          * @param {Proxy} 
19068          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19069          * @param {Object} load options 
19070          * @param {Object} jsonData from your request (normally this contains the Exception)
19071          */
19072         loadexception : true
19073     });
19074     
19075     if(this.proxy){
19076         this.proxy = Roo.factory(this.proxy, Roo.data);
19077         this.proxy.xmodule = this.xmodule || false;
19078         this.relayEvents(this.proxy,  ["loadexception"]);
19079     }
19080     this.sortToggle = {};
19081
19082     Roo.data.Store.superclass.constructor.call(this);
19083
19084     if(this.inlineData){
19085         this.loadData(this.inlineData);
19086         delete this.inlineData;
19087     }
19088 };
19089 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19090      /**
19091     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19092     * without a remote query - used by combo/forms at present.
19093     */
19094     
19095     /**
19096     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19097     */
19098     /**
19099     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19100     */
19101     /**
19102     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19103     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19104     */
19105     /**
19106     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19107     * on any HTTP request
19108     */
19109     /**
19110     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19111     */
19112     /**
19113     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19114     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19115     */
19116     remoteSort : false,
19117
19118     /**
19119     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19120      * loaded or when a record is removed. (defaults to false).
19121     */
19122     pruneModifiedRecords : false,
19123
19124     // private
19125     lastOptions : null,
19126
19127     /**
19128      * Add Records to the Store and fires the add event.
19129      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19130      */
19131     add : function(records){
19132         records = [].concat(records);
19133         for(var i = 0, len = records.length; i < len; i++){
19134             records[i].join(this);
19135         }
19136         var index = this.data.length;
19137         this.data.addAll(records);
19138         this.fireEvent("add", this, records, index);
19139     },
19140
19141     /**
19142      * Remove a Record from the Store and fires the remove event.
19143      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19144      */
19145     remove : function(record){
19146         var index = this.data.indexOf(record);
19147         this.data.removeAt(index);
19148         if(this.pruneModifiedRecords){
19149             this.modified.remove(record);
19150         }
19151         this.fireEvent("remove", this, record, index);
19152     },
19153
19154     /**
19155      * Remove all Records from the Store and fires the clear event.
19156      */
19157     removeAll : function(){
19158         this.data.clear();
19159         if(this.pruneModifiedRecords){
19160             this.modified = [];
19161         }
19162         this.fireEvent("clear", this);
19163     },
19164
19165     /**
19166      * Inserts Records to the Store at the given index and fires the add event.
19167      * @param {Number} index The start index at which to insert the passed Records.
19168      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19169      */
19170     insert : function(index, records){
19171         records = [].concat(records);
19172         for(var i = 0, len = records.length; i < len; i++){
19173             this.data.insert(index, records[i]);
19174             records[i].join(this);
19175         }
19176         this.fireEvent("add", this, records, index);
19177     },
19178
19179     /**
19180      * Get the index within the cache of the passed Record.
19181      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19182      * @return {Number} The index of the passed Record. Returns -1 if not found.
19183      */
19184     indexOf : function(record){
19185         return this.data.indexOf(record);
19186     },
19187
19188     /**
19189      * Get the index within the cache of the Record with the passed id.
19190      * @param {String} id The id of the Record to find.
19191      * @return {Number} The index of the Record. Returns -1 if not found.
19192      */
19193     indexOfId : function(id){
19194         return this.data.indexOfKey(id);
19195     },
19196
19197     /**
19198      * Get the Record with the specified id.
19199      * @param {String} id The id of the Record to find.
19200      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19201      */
19202     getById : function(id){
19203         return this.data.key(id);
19204     },
19205
19206     /**
19207      * Get the Record at the specified index.
19208      * @param {Number} index The index of the Record to find.
19209      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19210      */
19211     getAt : function(index){
19212         return this.data.itemAt(index);
19213     },
19214
19215     /**
19216      * Returns a range of Records between specified indices.
19217      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19218      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19219      * @return {Roo.data.Record[]} An array of Records
19220      */
19221     getRange : function(start, end){
19222         return this.data.getRange(start, end);
19223     },
19224
19225     // private
19226     storeOptions : function(o){
19227         o = Roo.apply({}, o);
19228         delete o.callback;
19229         delete o.scope;
19230         this.lastOptions = o;
19231     },
19232
19233     /**
19234      * Loads the Record cache from the configured Proxy using the configured Reader.
19235      * <p>
19236      * If using remote paging, then the first load call must specify the <em>start</em>
19237      * and <em>limit</em> properties in the options.params property to establish the initial
19238      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19239      * <p>
19240      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19241      * and this call will return before the new data has been loaded. Perform any post-processing
19242      * in a callback function, or in a "load" event handler.</strong>
19243      * <p>
19244      * @param {Object} options An object containing properties which control loading options:<ul>
19245      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19246      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19247      * passed the following arguments:<ul>
19248      * <li>r : Roo.data.Record[]</li>
19249      * <li>options: Options object from the load call</li>
19250      * <li>success: Boolean success indicator</li></ul></li>
19251      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19252      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19253      * </ul>
19254      */
19255     load : function(options){
19256         options = options || {};
19257         if(this.fireEvent("beforeload", this, options) !== false){
19258             this.storeOptions(options);
19259             var p = Roo.apply(options.params || {}, this.baseParams);
19260             if(this.sortInfo && this.remoteSort){
19261                 var pn = this.paramNames;
19262                 p[pn["sort"]] = this.sortInfo.field;
19263                 p[pn["dir"]] = this.sortInfo.direction;
19264             }
19265             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19266         }
19267     },
19268
19269     /**
19270      * Reloads the Record cache from the configured Proxy using the configured Reader and
19271      * the options from the last load operation performed.
19272      * @param {Object} options (optional) An object containing properties which may override the options
19273      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19274      * the most recently used options are reused).
19275      */
19276     reload : function(options){
19277         this.load(Roo.applyIf(options||{}, this.lastOptions));
19278     },
19279
19280     // private
19281     // Called as a callback by the Reader during a load operation.
19282     loadRecords : function(o, options, success){
19283         if(!o || success === false){
19284             if(success !== false){
19285                 this.fireEvent("load", this, [], options);
19286             }
19287             if(options.callback){
19288                 options.callback.call(options.scope || this, [], options, false);
19289             }
19290             return;
19291         }
19292         // if data returned failure - throw an exception.
19293         if (o.success === false) {
19294             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19295             return;
19296         }
19297         var r = o.records, t = o.totalRecords || r.length;
19298         if(!options || options.add !== true){
19299             if(this.pruneModifiedRecords){
19300                 this.modified = [];
19301             }
19302             for(var i = 0, len = r.length; i < len; i++){
19303                 r[i].join(this);
19304             }
19305             if(this.snapshot){
19306                 this.data = this.snapshot;
19307                 delete this.snapshot;
19308             }
19309             this.data.clear();
19310             this.data.addAll(r);
19311             this.totalLength = t;
19312             this.applySort();
19313             this.fireEvent("datachanged", this);
19314         }else{
19315             this.totalLength = Math.max(t, this.data.length+r.length);
19316             this.add(r);
19317         }
19318         this.fireEvent("load", this, r, options);
19319         if(options.callback){
19320             options.callback.call(options.scope || this, r, options, true);
19321         }
19322     },
19323
19324     /**
19325      * Loads data from a passed data block. A Reader which understands the format of the data
19326      * must have been configured in the constructor.
19327      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19328      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19329      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19330      */
19331     loadData : function(o, append){
19332         var r = this.reader.readRecords(o);
19333         this.loadRecords(r, {add: append}, true);
19334     },
19335
19336     /**
19337      * Gets the number of cached records.
19338      * <p>
19339      * <em>If using paging, this may not be the total size of the dataset. If the data object
19340      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19341      * the data set size</em>
19342      */
19343     getCount : function(){
19344         return this.data.length || 0;
19345     },
19346
19347     /**
19348      * Gets the total number of records in the dataset as returned by the server.
19349      * <p>
19350      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19351      * the dataset size</em>
19352      */
19353     getTotalCount : function(){
19354         return this.totalLength || 0;
19355     },
19356
19357     /**
19358      * Returns the sort state of the Store as an object with two properties:
19359      * <pre><code>
19360  field {String} The name of the field by which the Records are sorted
19361  direction {String} The sort order, "ASC" or "DESC"
19362      * </code></pre>
19363      */
19364     getSortState : function(){
19365         return this.sortInfo;
19366     },
19367
19368     // private
19369     applySort : function(){
19370         if(this.sortInfo && !this.remoteSort){
19371             var s = this.sortInfo, f = s.field;
19372             var st = this.fields.get(f).sortType;
19373             var fn = function(r1, r2){
19374                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19375                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19376             };
19377             this.data.sort(s.direction, fn);
19378             if(this.snapshot && this.snapshot != this.data){
19379                 this.snapshot.sort(s.direction, fn);
19380             }
19381         }
19382     },
19383
19384     /**
19385      * Sets the default sort column and order to be used by the next load operation.
19386      * @param {String} fieldName The name of the field to sort by.
19387      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19388      */
19389     setDefaultSort : function(field, dir){
19390         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19391     },
19392
19393     /**
19394      * Sort the Records.
19395      * If remote sorting is used, the sort is performed on the server, and the cache is
19396      * reloaded. If local sorting is used, the cache is sorted internally.
19397      * @param {String} fieldName The name of the field to sort by.
19398      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19399      */
19400     sort : function(fieldName, dir){
19401         var f = this.fields.get(fieldName);
19402         if(!dir){
19403             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19404                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19405             }else{
19406                 dir = f.sortDir;
19407             }
19408         }
19409         this.sortToggle[f.name] = dir;
19410         this.sortInfo = {field: f.name, direction: dir};
19411         if(!this.remoteSort){
19412             this.applySort();
19413             this.fireEvent("datachanged", this);
19414         }else{
19415             this.load(this.lastOptions);
19416         }
19417     },
19418
19419     /**
19420      * Calls the specified function for each of the Records in the cache.
19421      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19422      * Returning <em>false</em> aborts and exits the iteration.
19423      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19424      */
19425     each : function(fn, scope){
19426         this.data.each(fn, scope);
19427     },
19428
19429     /**
19430      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19431      * (e.g., during paging).
19432      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19433      */
19434     getModifiedRecords : function(){
19435         return this.modified;
19436     },
19437
19438     // private
19439     createFilterFn : function(property, value, anyMatch){
19440         if(!value.exec){ // not a regex
19441             value = String(value);
19442             if(value.length == 0){
19443                 return false;
19444             }
19445             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19446         }
19447         return function(r){
19448             return value.test(r.data[property]);
19449         };
19450     },
19451
19452     /**
19453      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19454      * @param {String} property A field on your records
19455      * @param {Number} start The record index to start at (defaults to 0)
19456      * @param {Number} end The last record index to include (defaults to length - 1)
19457      * @return {Number} The sum
19458      */
19459     sum : function(property, start, end){
19460         var rs = this.data.items, v = 0;
19461         start = start || 0;
19462         end = (end || end === 0) ? end : rs.length-1;
19463
19464         for(var i = start; i <= end; i++){
19465             v += (rs[i].data[property] || 0);
19466         }
19467         return v;
19468     },
19469
19470     /**
19471      * Filter the records by a specified property.
19472      * @param {String} field A field on your records
19473      * @param {String/RegExp} value Either a string that the field
19474      * should start with or a RegExp to test against the field
19475      * @param {Boolean} anyMatch True to match any part not just the beginning
19476      */
19477     filter : function(property, value, anyMatch){
19478         var fn = this.createFilterFn(property, value, anyMatch);
19479         return fn ? this.filterBy(fn) : this.clearFilter();
19480     },
19481
19482     /**
19483      * Filter by a function. The specified function will be called with each
19484      * record in this data source. If the function returns true the record is included,
19485      * otherwise it is filtered.
19486      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19487      * @param {Object} scope (optional) The scope of the function (defaults to this)
19488      */
19489     filterBy : function(fn, scope){
19490         this.snapshot = this.snapshot || this.data;
19491         this.data = this.queryBy(fn, scope||this);
19492         this.fireEvent("datachanged", this);
19493     },
19494
19495     /**
19496      * Query the records by a specified property.
19497      * @param {String} field A field on your records
19498      * @param {String/RegExp} value Either a string that the field
19499      * should start with or a RegExp to test against the field
19500      * @param {Boolean} anyMatch True to match any part not just the beginning
19501      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19502      */
19503     query : function(property, value, anyMatch){
19504         var fn = this.createFilterFn(property, value, anyMatch);
19505         return fn ? this.queryBy(fn) : this.data.clone();
19506     },
19507
19508     /**
19509      * Query by a function. The specified function will be called with each
19510      * record in this data source. If the function returns true the record is included
19511      * in the results.
19512      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19513      * @param {Object} scope (optional) The scope of the function (defaults to this)
19514       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19515      **/
19516     queryBy : function(fn, scope){
19517         var data = this.snapshot || this.data;
19518         return data.filterBy(fn, scope||this);
19519     },
19520
19521     /**
19522      * Collects unique values for a particular dataIndex from this store.
19523      * @param {String} dataIndex The property to collect
19524      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19525      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19526      * @return {Array} An array of the unique values
19527      **/
19528     collect : function(dataIndex, allowNull, bypassFilter){
19529         var d = (bypassFilter === true && this.snapshot) ?
19530                 this.snapshot.items : this.data.items;
19531         var v, sv, r = [], l = {};
19532         for(var i = 0, len = d.length; i < len; i++){
19533             v = d[i].data[dataIndex];
19534             sv = String(v);
19535             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19536                 l[sv] = true;
19537                 r[r.length] = v;
19538             }
19539         }
19540         return r;
19541     },
19542
19543     /**
19544      * Revert to a view of the Record cache with no filtering applied.
19545      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19546      */
19547     clearFilter : function(suppressEvent){
19548         if(this.snapshot && this.snapshot != this.data){
19549             this.data = this.snapshot;
19550             delete this.snapshot;
19551             if(suppressEvent !== true){
19552                 this.fireEvent("datachanged", this);
19553             }
19554         }
19555     },
19556
19557     // private
19558     afterEdit : function(record){
19559         if(this.modified.indexOf(record) == -1){
19560             this.modified.push(record);
19561         }
19562         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19563     },
19564
19565     // private
19566     afterReject : function(record){
19567         this.modified.remove(record);
19568         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19569     },
19570
19571     // private
19572     afterCommit : function(record){
19573         this.modified.remove(record);
19574         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19575     },
19576
19577     /**
19578      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19579      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19580      */
19581     commitChanges : function(){
19582         var m = this.modified.slice(0);
19583         this.modified = [];
19584         for(var i = 0, len = m.length; i < len; i++){
19585             m[i].commit();
19586         }
19587     },
19588
19589     /**
19590      * Cancel outstanding changes on all changed records.
19591      */
19592     rejectChanges : function(){
19593         var m = this.modified.slice(0);
19594         this.modified = [];
19595         for(var i = 0, len = m.length; i < len; i++){
19596             m[i].reject();
19597         }
19598     },
19599
19600     onMetaChange : function(meta, rtype, o){
19601         this.recordType = rtype;
19602         this.fields = rtype.prototype.fields;
19603         delete this.snapshot;
19604         this.sortInfo = meta.sortInfo;
19605         this.modified = [];
19606         this.fireEvent('metachange', this, this.reader.meta);
19607     }
19608 });/*
19609  * Based on:
19610  * Ext JS Library 1.1.1
19611  * Copyright(c) 2006-2007, Ext JS, LLC.
19612  *
19613  * Originally Released Under LGPL - original licence link has changed is not relivant.
19614  *
19615  * Fork - LGPL
19616  * <script type="text/javascript">
19617  */
19618
19619 /**
19620  * @class Roo.data.SimpleStore
19621  * @extends Roo.data.Store
19622  * Small helper class to make creating Stores from Array data easier.
19623  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19624  * @cfg {Array} fields An array of field definition objects, or field name strings.
19625  * @cfg {Array} data The multi-dimensional array of data
19626  * @constructor
19627  * @param {Object} config
19628  */
19629 Roo.data.SimpleStore = function(config){
19630     Roo.data.SimpleStore.superclass.constructor.call(this, {
19631         isLocal : true,
19632         reader: new Roo.data.ArrayReader({
19633                 id: config.id
19634             },
19635             Roo.data.Record.create(config.fields)
19636         ),
19637         proxy : new Roo.data.MemoryProxy(config.data)
19638     });
19639     this.load();
19640 };
19641 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19642  * Based on:
19643  * Ext JS Library 1.1.1
19644  * Copyright(c) 2006-2007, Ext JS, LLC.
19645  *
19646  * Originally Released Under LGPL - original licence link has changed is not relivant.
19647  *
19648  * Fork - LGPL
19649  * <script type="text/javascript">
19650  */
19651
19652 /**
19653 /**
19654  * @extends Roo.data.Store
19655  * @class Roo.data.JsonStore
19656  * Small helper class to make creating Stores for JSON data easier. <br/>
19657 <pre><code>
19658 var store = new Roo.data.JsonStore({
19659     url: 'get-images.php',
19660     root: 'images',
19661     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19662 });
19663 </code></pre>
19664  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19665  * JsonReader and HttpProxy (unless inline data is provided).</b>
19666  * @cfg {Array} fields An array of field definition objects, or field name strings.
19667  * @constructor
19668  * @param {Object} config
19669  */
19670 Roo.data.JsonStore = function(c){
19671     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19672         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19673         reader: new Roo.data.JsonReader(c, c.fields)
19674     }));
19675 };
19676 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19677  * Based on:
19678  * Ext JS Library 1.1.1
19679  * Copyright(c) 2006-2007, Ext JS, LLC.
19680  *
19681  * Originally Released Under LGPL - original licence link has changed is not relivant.
19682  *
19683  * Fork - LGPL
19684  * <script type="text/javascript">
19685  */
19686
19687  
19688 Roo.data.Field = function(config){
19689     if(typeof config == "string"){
19690         config = {name: config};
19691     }
19692     Roo.apply(this, config);
19693     
19694     if(!this.type){
19695         this.type = "auto";
19696     }
19697     
19698     var st = Roo.data.SortTypes;
19699     // named sortTypes are supported, here we look them up
19700     if(typeof this.sortType == "string"){
19701         this.sortType = st[this.sortType];
19702     }
19703     
19704     // set default sortType for strings and dates
19705     if(!this.sortType){
19706         switch(this.type){
19707             case "string":
19708                 this.sortType = st.asUCString;
19709                 break;
19710             case "date":
19711                 this.sortType = st.asDate;
19712                 break;
19713             default:
19714                 this.sortType = st.none;
19715         }
19716     }
19717
19718     // define once
19719     var stripRe = /[\$,%]/g;
19720
19721     // prebuilt conversion function for this field, instead of
19722     // switching every time we're reading a value
19723     if(!this.convert){
19724         var cv, dateFormat = this.dateFormat;
19725         switch(this.type){
19726             case "":
19727             case "auto":
19728             case undefined:
19729                 cv = function(v){ return v; };
19730                 break;
19731             case "string":
19732                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19733                 break;
19734             case "int":
19735                 cv = function(v){
19736                     return v !== undefined && v !== null && v !== '' ?
19737                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19738                     };
19739                 break;
19740             case "float":
19741                 cv = function(v){
19742                     return v !== undefined && v !== null && v !== '' ?
19743                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19744                     };
19745                 break;
19746             case "bool":
19747             case "boolean":
19748                 cv = function(v){ return v === true || v === "true" || v == 1; };
19749                 break;
19750             case "date":
19751                 cv = function(v){
19752                     if(!v){
19753                         return '';
19754                     }
19755                     if(v instanceof Date){
19756                         return v;
19757                     }
19758                     if(dateFormat){
19759                         if(dateFormat == "timestamp"){
19760                             return new Date(v*1000);
19761                         }
19762                         return Date.parseDate(v, dateFormat);
19763                     }
19764                     var parsed = Date.parse(v);
19765                     return parsed ? new Date(parsed) : null;
19766                 };
19767              break;
19768             
19769         }
19770         this.convert = cv;
19771     }
19772 };
19773
19774 Roo.data.Field.prototype = {
19775     dateFormat: null,
19776     defaultValue: "",
19777     mapping: null,
19778     sortType : null,
19779     sortDir : "ASC"
19780 };/*
19781  * Based on:
19782  * Ext JS Library 1.1.1
19783  * Copyright(c) 2006-2007, Ext JS, LLC.
19784  *
19785  * Originally Released Under LGPL - original licence link has changed is not relivant.
19786  *
19787  * Fork - LGPL
19788  * <script type="text/javascript">
19789  */
19790  
19791 // Base class for reading structured data from a data source.  This class is intended to be
19792 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19793
19794 /**
19795  * @class Roo.data.DataReader
19796  * Base class for reading structured data from a data source.  This class is intended to be
19797  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19798  */
19799
19800 Roo.data.DataReader = function(meta, recordType){
19801     
19802     this.meta = meta;
19803     
19804     this.recordType = recordType instanceof Array ? 
19805         Roo.data.Record.create(recordType) : recordType;
19806 };
19807
19808 Roo.data.DataReader.prototype = {
19809      /**
19810      * Create an empty record
19811      * @param {Object} data (optional) - overlay some values
19812      * @return {Roo.data.Record} record created.
19813      */
19814     newRow :  function(d) {
19815         var da =  {};
19816         this.recordType.prototype.fields.each(function(c) {
19817             switch( c.type) {
19818                 case 'int' : da[c.name] = 0; break;
19819                 case 'date' : da[c.name] = new Date(); break;
19820                 case 'float' : da[c.name] = 0.0; break;
19821                 case 'boolean' : da[c.name] = false; break;
19822                 default : da[c.name] = ""; break;
19823             }
19824             
19825         });
19826         return new this.recordType(Roo.apply(da, d));
19827     }
19828     
19829 };/*
19830  * Based on:
19831  * Ext JS Library 1.1.1
19832  * Copyright(c) 2006-2007, Ext JS, LLC.
19833  *
19834  * Originally Released Under LGPL - original licence link has changed is not relivant.
19835  *
19836  * Fork - LGPL
19837  * <script type="text/javascript">
19838  */
19839
19840 /**
19841  * @class Roo.data.DataProxy
19842  * @extends Roo.data.Observable
19843  * This class is an abstract base class for implementations which provide retrieval of
19844  * unformatted data objects.<br>
19845  * <p>
19846  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19847  * (of the appropriate type which knows how to parse the data object) to provide a block of
19848  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19849  * <p>
19850  * Custom implementations must implement the load method as described in
19851  * {@link Roo.data.HttpProxy#load}.
19852  */
19853 Roo.data.DataProxy = function(){
19854     this.addEvents({
19855         /**
19856          * @event beforeload
19857          * Fires before a network request is made to retrieve a data object.
19858          * @param {Object} This DataProxy object.
19859          * @param {Object} params The params parameter to the load function.
19860          */
19861         beforeload : true,
19862         /**
19863          * @event load
19864          * Fires before the load method's callback is called.
19865          * @param {Object} This DataProxy object.
19866          * @param {Object} o The data object.
19867          * @param {Object} arg The callback argument object passed to the load function.
19868          */
19869         load : true,
19870         /**
19871          * @event loadexception
19872          * Fires if an Exception occurs during data retrieval.
19873          * @param {Object} This DataProxy object.
19874          * @param {Object} o The data object.
19875          * @param {Object} arg The callback argument object passed to the load function.
19876          * @param {Object} e The Exception.
19877          */
19878         loadexception : true
19879     });
19880     Roo.data.DataProxy.superclass.constructor.call(this);
19881 };
19882
19883 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
19884
19885     /**
19886      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
19887      */
19888 /*
19889  * Based on:
19890  * Ext JS Library 1.1.1
19891  * Copyright(c) 2006-2007, Ext JS, LLC.
19892  *
19893  * Originally Released Under LGPL - original licence link has changed is not relivant.
19894  *
19895  * Fork - LGPL
19896  * <script type="text/javascript">
19897  */
19898 /**
19899  * @class Roo.data.MemoryProxy
19900  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
19901  * to the Reader when its load method is called.
19902  * @constructor
19903  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
19904  */
19905 Roo.data.MemoryProxy = function(data){
19906     if (data.data) {
19907         data = data.data;
19908     }
19909     Roo.data.MemoryProxy.superclass.constructor.call(this);
19910     this.data = data;
19911 };
19912
19913 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
19914     /**
19915      * Load data from the requested source (in this case an in-memory
19916      * data object passed to the constructor), read the data object into
19917      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
19918      * process that block using the passed callback.
19919      * @param {Object} params This parameter is not used by the MemoryProxy class.
19920      * @param {Roo.data.DataReader} reader The Reader object which converts the data
19921      * object into a block of Roo.data.Records.
19922      * @param {Function} callback The function into which to pass the block of Roo.data.records.
19923      * The function must be passed <ul>
19924      * <li>The Record block object</li>
19925      * <li>The "arg" argument from the load function</li>
19926      * <li>A boolean success indicator</li>
19927      * </ul>
19928      * @param {Object} scope The scope in which to call the callback
19929      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
19930      */
19931     load : function(params, reader, callback, scope, arg){
19932         params = params || {};
19933         var result;
19934         try {
19935             result = reader.readRecords(this.data);
19936         }catch(e){
19937             this.fireEvent("loadexception", this, arg, null, e);
19938             callback.call(scope, null, arg, false);
19939             return;
19940         }
19941         callback.call(scope, result, arg, true);
19942     },
19943     
19944     // private
19945     update : function(params, records){
19946         
19947     }
19948 });/*
19949  * Based on:
19950  * Ext JS Library 1.1.1
19951  * Copyright(c) 2006-2007, Ext JS, LLC.
19952  *
19953  * Originally Released Under LGPL - original licence link has changed is not relivant.
19954  *
19955  * Fork - LGPL
19956  * <script type="text/javascript">
19957  */
19958 /**
19959  * @class Roo.data.HttpProxy
19960  * @extends Roo.data.DataProxy
19961  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
19962  * configured to reference a certain URL.<br><br>
19963  * <p>
19964  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
19965  * from which the running page was served.<br><br>
19966  * <p>
19967  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
19968  * <p>
19969  * Be aware that to enable the browser to parse an XML document, the server must set
19970  * the Content-Type header in the HTTP response to "text/xml".
19971  * @constructor
19972  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
19973  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
19974  * will be used to make the request.
19975  */
19976 Roo.data.HttpProxy = function(conn){
19977     Roo.data.HttpProxy.superclass.constructor.call(this);
19978     // is conn a conn config or a real conn?
19979     this.conn = conn;
19980     this.useAjax = !conn || !conn.events;
19981   
19982 };
19983
19984 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
19985     // thse are take from connection...
19986     
19987     /**
19988      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
19989      */
19990     /**
19991      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
19992      * extra parameters to each request made by this object. (defaults to undefined)
19993      */
19994     /**
19995      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
19996      *  to each request made by this object. (defaults to undefined)
19997      */
19998     /**
19999      * @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)
20000      */
20001     /**
20002      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20003      */
20004      /**
20005      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20006      * @type Boolean
20007      */
20008   
20009
20010     /**
20011      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20012      * @type Boolean
20013      */
20014     /**
20015      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20016      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20017      * a finer-grained basis than the DataProxy events.
20018      */
20019     getConnection : function(){
20020         return this.useAjax ? Roo.Ajax : this.conn;
20021     },
20022
20023     /**
20024      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20025      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20026      * process that block using the passed callback.
20027      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20028      * for the request to the remote server.
20029      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20030      * object into a block of Roo.data.Records.
20031      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20032      * The function must be passed <ul>
20033      * <li>The Record block object</li>
20034      * <li>The "arg" argument from the load function</li>
20035      * <li>A boolean success indicator</li>
20036      * </ul>
20037      * @param {Object} scope The scope in which to call the callback
20038      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20039      */
20040     load : function(params, reader, callback, scope, arg){
20041         if(this.fireEvent("beforeload", this, params) !== false){
20042             var  o = {
20043                 params : params || {},
20044                 request: {
20045                     callback : callback,
20046                     scope : scope,
20047                     arg : arg
20048                 },
20049                 reader: reader,
20050                 callback : this.loadResponse,
20051                 scope: this
20052             };
20053             if(this.useAjax){
20054                 Roo.applyIf(o, this.conn);
20055                 if(this.activeRequest){
20056                     Roo.Ajax.abort(this.activeRequest);
20057                 }
20058                 this.activeRequest = Roo.Ajax.request(o);
20059             }else{
20060                 this.conn.request(o);
20061             }
20062         }else{
20063             callback.call(scope||this, null, arg, false);
20064         }
20065     },
20066
20067     // private
20068     loadResponse : function(o, success, response){
20069         delete this.activeRequest;
20070         if(!success){
20071             this.fireEvent("loadexception", this, o, response);
20072             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20073             return;
20074         }
20075         var result;
20076         try {
20077             result = o.reader.read(response);
20078         }catch(e){
20079             this.fireEvent("loadexception", this, o, response, e);
20080             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20081             return;
20082         }
20083         
20084         this.fireEvent("load", this, o, o.request.arg);
20085         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20086     },
20087
20088     // private
20089     update : function(dataSet){
20090
20091     },
20092
20093     // private
20094     updateResponse : function(dataSet){
20095
20096     }
20097 });/*
20098  * Based on:
20099  * Ext JS Library 1.1.1
20100  * Copyright(c) 2006-2007, Ext JS, LLC.
20101  *
20102  * Originally Released Under LGPL - original licence link has changed is not relivant.
20103  *
20104  * Fork - LGPL
20105  * <script type="text/javascript">
20106  */
20107
20108 /**
20109  * @class Roo.data.ScriptTagProxy
20110  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20111  * other than the originating domain of the running page.<br><br>
20112  * <p>
20113  * <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
20114  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20115  * <p>
20116  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20117  * source code that is used as the source inside a &lt;script> tag.<br><br>
20118  * <p>
20119  * In order for the browser to process the returned data, the server must wrap the data object
20120  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20121  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20122  * depending on whether the callback name was passed:
20123  * <p>
20124  * <pre><code>
20125 boolean scriptTag = false;
20126 String cb = request.getParameter("callback");
20127 if (cb != null) {
20128     scriptTag = true;
20129     response.setContentType("text/javascript");
20130 } else {
20131     response.setContentType("application/x-json");
20132 }
20133 Writer out = response.getWriter();
20134 if (scriptTag) {
20135     out.write(cb + "(");
20136 }
20137 out.print(dataBlock.toJsonString());
20138 if (scriptTag) {
20139     out.write(");");
20140 }
20141 </pre></code>
20142  *
20143  * @constructor
20144  * @param {Object} config A configuration object.
20145  */
20146 Roo.data.ScriptTagProxy = function(config){
20147     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20148     Roo.apply(this, config);
20149     this.head = document.getElementsByTagName("head")[0];
20150 };
20151
20152 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20153
20154 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20155     /**
20156      * @cfg {String} url The URL from which to request the data object.
20157      */
20158     /**
20159      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20160      */
20161     timeout : 30000,
20162     /**
20163      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20164      * the server the name of the callback function set up by the load call to process the returned data object.
20165      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20166      * javascript output which calls this named function passing the data object as its only parameter.
20167      */
20168     callbackParam : "callback",
20169     /**
20170      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20171      * name to the request.
20172      */
20173     nocache : true,
20174
20175     /**
20176      * Load data from the configured URL, read the data object into
20177      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20178      * process that block using the passed callback.
20179      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20180      * for the request to the remote server.
20181      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20182      * object into a block of Roo.data.Records.
20183      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20184      * The function must be passed <ul>
20185      * <li>The Record block object</li>
20186      * <li>The "arg" argument from the load function</li>
20187      * <li>A boolean success indicator</li>
20188      * </ul>
20189      * @param {Object} scope The scope in which to call the callback
20190      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20191      */
20192     load : function(params, reader, callback, scope, arg){
20193         if(this.fireEvent("beforeload", this, params) !== false){
20194
20195             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20196
20197             var url = this.url;
20198             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20199             if(this.nocache){
20200                 url += "&_dc=" + (new Date().getTime());
20201             }
20202             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20203             var trans = {
20204                 id : transId,
20205                 cb : "stcCallback"+transId,
20206                 scriptId : "stcScript"+transId,
20207                 params : params,
20208                 arg : arg,
20209                 url : url,
20210                 callback : callback,
20211                 scope : scope,
20212                 reader : reader
20213             };
20214             var conn = this;
20215
20216             window[trans.cb] = function(o){
20217                 conn.handleResponse(o, trans);
20218             };
20219
20220             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20221
20222             if(this.autoAbort !== false){
20223                 this.abort();
20224             }
20225
20226             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20227
20228             var script = document.createElement("script");
20229             script.setAttribute("src", url);
20230             script.setAttribute("type", "text/javascript");
20231             script.setAttribute("id", trans.scriptId);
20232             this.head.appendChild(script);
20233
20234             this.trans = trans;
20235         }else{
20236             callback.call(scope||this, null, arg, false);
20237         }
20238     },
20239
20240     // private
20241     isLoading : function(){
20242         return this.trans ? true : false;
20243     },
20244
20245     /**
20246      * Abort the current server request.
20247      */
20248     abort : function(){
20249         if(this.isLoading()){
20250             this.destroyTrans(this.trans);
20251         }
20252     },
20253
20254     // private
20255     destroyTrans : function(trans, isLoaded){
20256         this.head.removeChild(document.getElementById(trans.scriptId));
20257         clearTimeout(trans.timeoutId);
20258         if(isLoaded){
20259             window[trans.cb] = undefined;
20260             try{
20261                 delete window[trans.cb];
20262             }catch(e){}
20263         }else{
20264             // if hasn't been loaded, wait for load to remove it to prevent script error
20265             window[trans.cb] = function(){
20266                 window[trans.cb] = undefined;
20267                 try{
20268                     delete window[trans.cb];
20269                 }catch(e){}
20270             };
20271         }
20272     },
20273
20274     // private
20275     handleResponse : function(o, trans){
20276         this.trans = false;
20277         this.destroyTrans(trans, true);
20278         var result;
20279         try {
20280             result = trans.reader.readRecords(o);
20281         }catch(e){
20282             this.fireEvent("loadexception", this, o, trans.arg, e);
20283             trans.callback.call(trans.scope||window, null, trans.arg, false);
20284             return;
20285         }
20286         this.fireEvent("load", this, o, trans.arg);
20287         trans.callback.call(trans.scope||window, result, trans.arg, true);
20288     },
20289
20290     // private
20291     handleFailure : function(trans){
20292         this.trans = false;
20293         this.destroyTrans(trans, false);
20294         this.fireEvent("loadexception", this, null, trans.arg);
20295         trans.callback.call(trans.scope||window, null, trans.arg, false);
20296     }
20297 });/*
20298  * Based on:
20299  * Ext JS Library 1.1.1
20300  * Copyright(c) 2006-2007, Ext JS, LLC.
20301  *
20302  * Originally Released Under LGPL - original licence link has changed is not relivant.
20303  *
20304  * Fork - LGPL
20305  * <script type="text/javascript">
20306  */
20307
20308 /**
20309  * @class Roo.data.JsonReader
20310  * @extends Roo.data.DataReader
20311  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20312  * based on mappings in a provided Roo.data.Record constructor.
20313  * <p>
20314  * Example code:
20315  * <pre><code>
20316 var RecordDef = Roo.data.Record.create([
20317     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20318     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20319 ]);
20320 var myReader = new Roo.data.JsonReader({
20321     totalProperty: "results",    // The property which contains the total dataset size (optional)
20322     root: "rows",                // The property which contains an Array of row objects
20323     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20324 }, RecordDef);
20325 </code></pre>
20326  * <p>
20327  * This would consume a JSON file like this:
20328  * <pre><code>
20329 { 'results': 2, 'rows': [
20330     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20331     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20332 }
20333 </code></pre>
20334  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20335  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20336  * paged from the remote server.
20337  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20338  * @cfg {String} root name of the property which contains the Array of row objects.
20339  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20340  * @constructor
20341  * Create a new JsonReader
20342  * @param {Object} meta Metadata configuration options
20343  * @param {Object} recordType Either an Array of field definition objects,
20344  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20345  */
20346 Roo.data.JsonReader = function(meta, recordType){
20347     
20348     meta = meta || {};
20349     // set some defaults:
20350     Roo.applyIf(meta, {
20351         totalProperty: 'total',
20352         successProperty : 'success',
20353         root : 'data',
20354         id : 'id'
20355     });
20356     
20357     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20358 };
20359 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20360     /**
20361      * This method is only used by a DataProxy which has retrieved data from a remote server.
20362      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20363      * @return {Object} data A data block which is used by an Roo.data.Store object as
20364      * a cache of Roo.data.Records.
20365      */
20366     read : function(response){
20367         var json = response.responseText;
20368        
20369         var o = /* eval:var:o */ eval("("+json+")");
20370         if(!o) {
20371             throw {message: "JsonReader.read: Json object not found"};
20372         }
20373         
20374         if(o.metaData){
20375             delete this.ef;
20376             this.meta = o.metaData;
20377             this.recordType = Roo.data.Record.create(o.metaData.fields);
20378             this.onMetaChange(this.meta, this.recordType, o);
20379         }
20380         return this.readRecords(o);
20381     },
20382
20383     // private function a store will implement
20384     onMetaChange : function(meta, recordType, o){
20385
20386     },
20387
20388     /**
20389          * @ignore
20390          */
20391     simpleAccess: function(obj, subsc) {
20392         return obj[subsc];
20393     },
20394
20395         /**
20396          * @ignore
20397          */
20398     getJsonAccessor: function(){
20399         var re = /[\[\.]/;
20400         return function(expr) {
20401             try {
20402                 return(re.test(expr))
20403                     ? new Function("obj", "return obj." + expr)
20404                     : function(obj){
20405                         return obj[expr];
20406                     };
20407             } catch(e){}
20408             return Roo.emptyFn;
20409         };
20410     }(),
20411
20412     /**
20413      * Create a data block containing Roo.data.Records from an XML document.
20414      * @param {Object} o An object which contains an Array of row objects in the property specified
20415      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20416      * which contains the total size of the dataset.
20417      * @return {Object} data A data block which is used by an Roo.data.Store object as
20418      * a cache of Roo.data.Records.
20419      */
20420     readRecords : function(o){
20421         /**
20422          * After any data loads, the raw JSON data is available for further custom processing.
20423          * @type Object
20424          */
20425         this.jsonData = o;
20426         var s = this.meta, Record = this.recordType,
20427             f = Record.prototype.fields, fi = f.items, fl = f.length;
20428
20429 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20430         if (!this.ef) {
20431             if(s.totalProperty) {
20432                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20433                 }
20434                 if(s.successProperty) {
20435                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20436                 }
20437                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20438                 if (s.id) {
20439                         var g = this.getJsonAccessor(s.id);
20440                         this.getId = function(rec) {
20441                                 var r = g(rec);
20442                                 return (r === undefined || r === "") ? null : r;
20443                         };
20444                 } else {
20445                         this.getId = function(){return null;};
20446                 }
20447             this.ef = [];
20448             for(var jj = 0; jj < fl; jj++){
20449                 f = fi[jj];
20450                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20451                 this.ef[jj] = this.getJsonAccessor(map);
20452             }
20453         }
20454
20455         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20456         if(s.totalProperty){
20457             var vt = parseInt(this.getTotal(o), 10);
20458             if(!isNaN(vt)){
20459                 totalRecords = vt;
20460             }
20461         }
20462         if(s.successProperty){
20463             var vs = this.getSuccess(o);
20464             if(vs === false || vs === 'false'){
20465                 success = false;
20466             }
20467         }
20468         var records = [];
20469             for(var i = 0; i < c; i++){
20470                     var n = root[i];
20471                 var values = {};
20472                 var id = this.getId(n);
20473                 for(var j = 0; j < fl; j++){
20474                     f = fi[j];
20475                 var v = this.ef[j](n);
20476                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20477                 }
20478                 var record = new Record(values, id);
20479                 record.json = n;
20480                 records[i] = record;
20481             }
20482             return {
20483                 success : success,
20484                 records : records,
20485                 totalRecords : totalRecords
20486             };
20487     }
20488 });/*
20489  * Based on:
20490  * Ext JS Library 1.1.1
20491  * Copyright(c) 2006-2007, Ext JS, LLC.
20492  *
20493  * Originally Released Under LGPL - original licence link has changed is not relivant.
20494  *
20495  * Fork - LGPL
20496  * <script type="text/javascript">
20497  */
20498
20499 /**
20500  * @class Roo.data.XmlReader
20501  * @extends Roo.data.DataReader
20502  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20503  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20504  * <p>
20505  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20506  * header in the HTTP response must be set to "text/xml".</em>
20507  * <p>
20508  * Example code:
20509  * <pre><code>
20510 var RecordDef = Roo.data.Record.create([
20511    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20512    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20513 ]);
20514 var myReader = new Roo.data.XmlReader({
20515    totalRecords: "results", // The element which contains the total dataset size (optional)
20516    record: "row",           // The repeated element which contains row information
20517    id: "id"                 // The element within the row that provides an ID for the record (optional)
20518 }, RecordDef);
20519 </code></pre>
20520  * <p>
20521  * This would consume an XML file like this:
20522  * <pre><code>
20523 &lt;?xml?>
20524 &lt;dataset>
20525  &lt;results>2&lt;/results>
20526  &lt;row>
20527    &lt;id>1&lt;/id>
20528    &lt;name>Bill&lt;/name>
20529    &lt;occupation>Gardener&lt;/occupation>
20530  &lt;/row>
20531  &lt;row>
20532    &lt;id>2&lt;/id>
20533    &lt;name>Ben&lt;/name>
20534    &lt;occupation>Horticulturalist&lt;/occupation>
20535  &lt;/row>
20536 &lt;/dataset>
20537 </code></pre>
20538  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20539  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20540  * paged from the remote server.
20541  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20542  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20543  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20544  * a record identifier value.
20545  * @constructor
20546  * Create a new XmlReader
20547  * @param {Object} meta Metadata configuration options
20548  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20549  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20550  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20551  */
20552 Roo.data.XmlReader = function(meta, recordType){
20553     meta = meta || {};
20554     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20555 };
20556 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20557     /**
20558      * This method is only used by a DataProxy which has retrieved data from a remote server.
20559          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20560          * to contain a method called 'responseXML' that returns an XML document object.
20561      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20562      * a cache of Roo.data.Records.
20563      */
20564     read : function(response){
20565         var doc = response.responseXML;
20566         if(!doc) {
20567             throw {message: "XmlReader.read: XML Document not available"};
20568         }
20569         return this.readRecords(doc);
20570     },
20571
20572     /**
20573      * Create a data block containing Roo.data.Records from an XML document.
20574          * @param {Object} doc A parsed XML document.
20575      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20576      * a cache of Roo.data.Records.
20577      */
20578     readRecords : function(doc){
20579         /**
20580          * After any data loads/reads, the raw XML Document is available for further custom processing.
20581          * @type XMLDocument
20582          */
20583         this.xmlData = doc;
20584         var root = doc.documentElement || doc;
20585         var q = Roo.DomQuery;
20586         var recordType = this.recordType, fields = recordType.prototype.fields;
20587         var sid = this.meta.id;
20588         var totalRecords = 0, success = true;
20589         if(this.meta.totalRecords){
20590             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20591         }
20592         
20593         if(this.meta.success){
20594             var sv = q.selectValue(this.meta.success, root, true);
20595             success = sv !== false && sv !== 'false';
20596         }
20597         var records = [];
20598         var ns = q.select(this.meta.record, root);
20599         for(var i = 0, len = ns.length; i < len; i++) {
20600                 var n = ns[i];
20601                 var values = {};
20602                 var id = sid ? q.selectValue(sid, n) : undefined;
20603                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20604                     var f = fields.items[j];
20605                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20606                     v = f.convert(v);
20607                     values[f.name] = v;
20608                 }
20609                 var record = new recordType(values, id);
20610                 record.node = n;
20611                 records[records.length] = record;
20612             }
20613
20614             return {
20615                 success : success,
20616                 records : records,
20617                 totalRecords : totalRecords || records.length
20618             };
20619     }
20620 });/*
20621  * Based on:
20622  * Ext JS Library 1.1.1
20623  * Copyright(c) 2006-2007, Ext JS, LLC.
20624  *
20625  * Originally Released Under LGPL - original licence link has changed is not relivant.
20626  *
20627  * Fork - LGPL
20628  * <script type="text/javascript">
20629  */
20630
20631 /**
20632  * @class Roo.data.ArrayReader
20633  * @extends Roo.data.DataReader
20634  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20635  * Each element of that Array represents a row of data fields. The
20636  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20637  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20638  * <p>
20639  * Example code:.
20640  * <pre><code>
20641 var RecordDef = Roo.data.Record.create([
20642     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20643     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20644 ]);
20645 var myReader = new Roo.data.ArrayReader({
20646     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20647 }, RecordDef);
20648 </code></pre>
20649  * <p>
20650  * This would consume an Array like this:
20651  * <pre><code>
20652 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20653   </code></pre>
20654  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20655  * @constructor
20656  * Create a new JsonReader
20657  * @param {Object} meta Metadata configuration options.
20658  * @param {Object} recordType Either an Array of field definition objects
20659  * as specified to {@link Roo.data.Record#create},
20660  * or an {@link Roo.data.Record} object
20661  * created using {@link Roo.data.Record#create}.
20662  */
20663 Roo.data.ArrayReader = function(meta, recordType){
20664     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20665 };
20666
20667 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20668     /**
20669      * Create a data block containing Roo.data.Records from an XML document.
20670      * @param {Object} o An Array of row objects which represents the dataset.
20671      * @return {Object} data A data block which is used by an Roo.data.Store object as
20672      * a cache of Roo.data.Records.
20673      */
20674     readRecords : function(o){
20675         var sid = this.meta ? this.meta.id : null;
20676         var recordType = this.recordType, fields = recordType.prototype.fields;
20677         var records = [];
20678         var root = o;
20679             for(var i = 0; i < root.length; i++){
20680                     var n = root[i];
20681                 var values = {};
20682                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20683                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20684                 var f = fields.items[j];
20685                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20686                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20687                 v = f.convert(v);
20688                 values[f.name] = v;
20689             }
20690                 var record = new recordType(values, id);
20691                 record.json = n;
20692                 records[records.length] = record;
20693             }
20694             return {
20695                 records : records,
20696                 totalRecords : records.length
20697             };
20698     }
20699 });/*
20700  * Based on:
20701  * Ext JS Library 1.1.1
20702  * Copyright(c) 2006-2007, Ext JS, LLC.
20703  *
20704  * Originally Released Under LGPL - original licence link has changed is not relivant.
20705  *
20706  * Fork - LGPL
20707  * <script type="text/javascript">
20708  */
20709
20710
20711 /**
20712  * @class Roo.data.Tree
20713  * @extends Roo.util.Observable
20714  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20715  * in the tree have most standard DOM functionality.
20716  * @constructor
20717  * @param {Node} root (optional) The root node
20718  */
20719 Roo.data.Tree = function(root){
20720    this.nodeHash = {};
20721    /**
20722     * The root node for this tree
20723     * @type Node
20724     */
20725    this.root = null;
20726    if(root){
20727        this.setRootNode(root);
20728    }
20729    this.addEvents({
20730        /**
20731         * @event append
20732         * Fires when a new child node is appended to a node in this tree.
20733         * @param {Tree} tree The owner tree
20734         * @param {Node} parent The parent node
20735         * @param {Node} node The newly appended node
20736         * @param {Number} index The index of the newly appended node
20737         */
20738        "append" : true,
20739        /**
20740         * @event remove
20741         * Fires when a child node is removed from a node in this tree.
20742         * @param {Tree} tree The owner tree
20743         * @param {Node} parent The parent node
20744         * @param {Node} node The child node removed
20745         */
20746        "remove" : true,
20747        /**
20748         * @event move
20749         * Fires when a node is moved to a new location in the tree
20750         * @param {Tree} tree The owner tree
20751         * @param {Node} node The node moved
20752         * @param {Node} oldParent The old parent of this node
20753         * @param {Node} newParent The new parent of this node
20754         * @param {Number} index The index it was moved to
20755         */
20756        "move" : true,
20757        /**
20758         * @event insert
20759         * Fires when a new child node is inserted in a node in this tree.
20760         * @param {Tree} tree The owner tree
20761         * @param {Node} parent The parent node
20762         * @param {Node} node The child node inserted
20763         * @param {Node} refNode The child node the node was inserted before
20764         */
20765        "insert" : true,
20766        /**
20767         * @event beforeappend
20768         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20769         * @param {Tree} tree The owner tree
20770         * @param {Node} parent The parent node
20771         * @param {Node} node The child node to be appended
20772         */
20773        "beforeappend" : true,
20774        /**
20775         * @event beforeremove
20776         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20777         * @param {Tree} tree The owner tree
20778         * @param {Node} parent The parent node
20779         * @param {Node} node The child node to be removed
20780         */
20781        "beforeremove" : true,
20782        /**
20783         * @event beforemove
20784         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20785         * @param {Tree} tree The owner tree
20786         * @param {Node} node The node being moved
20787         * @param {Node} oldParent The parent of the node
20788         * @param {Node} newParent The new parent the node is moving to
20789         * @param {Number} index The index it is being moved to
20790         */
20791        "beforemove" : true,
20792        /**
20793         * @event beforeinsert
20794         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20795         * @param {Tree} tree The owner tree
20796         * @param {Node} parent The parent node
20797         * @param {Node} node The child node to be inserted
20798         * @param {Node} refNode The child node the node is being inserted before
20799         */
20800        "beforeinsert" : true
20801    });
20802
20803     Roo.data.Tree.superclass.constructor.call(this);
20804 };
20805
20806 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20807     pathSeparator: "/",
20808
20809     proxyNodeEvent : function(){
20810         return this.fireEvent.apply(this, arguments);
20811     },
20812
20813     /**
20814      * Returns the root node for this tree.
20815      * @return {Node}
20816      */
20817     getRootNode : function(){
20818         return this.root;
20819     },
20820
20821     /**
20822      * Sets the root node for this tree.
20823      * @param {Node} node
20824      * @return {Node}
20825      */
20826     setRootNode : function(node){
20827         this.root = node;
20828         node.ownerTree = this;
20829         node.isRoot = true;
20830         this.registerNode(node);
20831         return node;
20832     },
20833
20834     /**
20835      * Gets a node in this tree by its id.
20836      * @param {String} id
20837      * @return {Node}
20838      */
20839     getNodeById : function(id){
20840         return this.nodeHash[id];
20841     },
20842
20843     registerNode : function(node){
20844         this.nodeHash[node.id] = node;
20845     },
20846
20847     unregisterNode : function(node){
20848         delete this.nodeHash[node.id];
20849     },
20850
20851     toString : function(){
20852         return "[Tree"+(this.id?" "+this.id:"")+"]";
20853     }
20854 });
20855
20856 /**
20857  * @class Roo.data.Node
20858  * @extends Roo.util.Observable
20859  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
20860  * @cfg {String} id The id for this node. If one is not specified, one is generated.
20861  * @constructor
20862  * @param {Object} attributes The attributes/config for the node
20863  */
20864 Roo.data.Node = function(attributes){
20865     /**
20866      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
20867      * @type {Object}
20868      */
20869     this.attributes = attributes || {};
20870     this.leaf = this.attributes.leaf;
20871     /**
20872      * The node id. @type String
20873      */
20874     this.id = this.attributes.id;
20875     if(!this.id){
20876         this.id = Roo.id(null, "ynode-");
20877         this.attributes.id = this.id;
20878     }
20879     /**
20880      * All child nodes of this node. @type Array
20881      */
20882     this.childNodes = [];
20883     if(!this.childNodes.indexOf){ // indexOf is a must
20884         this.childNodes.indexOf = function(o){
20885             for(var i = 0, len = this.length; i < len; i++){
20886                 if(this[i] == o) return i;
20887             }
20888             return -1;
20889         };
20890     }
20891     /**
20892      * The parent node for this node. @type Node
20893      */
20894     this.parentNode = null;
20895     /**
20896      * The first direct child node of this node, or null if this node has no child nodes. @type Node
20897      */
20898     this.firstChild = null;
20899     /**
20900      * The last direct child node of this node, or null if this node has no child nodes. @type Node
20901      */
20902     this.lastChild = null;
20903     /**
20904      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
20905      */
20906     this.previousSibling = null;
20907     /**
20908      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
20909      */
20910     this.nextSibling = null;
20911
20912     this.addEvents({
20913        /**
20914         * @event append
20915         * Fires when a new child node is appended
20916         * @param {Tree} tree The owner tree
20917         * @param {Node} this This node
20918         * @param {Node} node The newly appended node
20919         * @param {Number} index The index of the newly appended node
20920         */
20921        "append" : true,
20922        /**
20923         * @event remove
20924         * Fires when a child node is removed
20925         * @param {Tree} tree The owner tree
20926         * @param {Node} this This node
20927         * @param {Node} node The removed node
20928         */
20929        "remove" : true,
20930        /**
20931         * @event move
20932         * Fires when this node is moved to a new location in the tree
20933         * @param {Tree} tree The owner tree
20934         * @param {Node} this This node
20935         * @param {Node} oldParent The old parent of this node
20936         * @param {Node} newParent The new parent of this node
20937         * @param {Number} index The index it was moved to
20938         */
20939        "move" : true,
20940        /**
20941         * @event insert
20942         * Fires when a new child node is inserted.
20943         * @param {Tree} tree The owner tree
20944         * @param {Node} this This node
20945         * @param {Node} node The child node inserted
20946         * @param {Node} refNode The child node the node was inserted before
20947         */
20948        "insert" : true,
20949        /**
20950         * @event beforeappend
20951         * Fires before a new child is appended, return false to cancel the append.
20952         * @param {Tree} tree The owner tree
20953         * @param {Node} this This node
20954         * @param {Node} node The child node to be appended
20955         */
20956        "beforeappend" : true,
20957        /**
20958         * @event beforeremove
20959         * Fires before a child is removed, return false to cancel the remove.
20960         * @param {Tree} tree The owner tree
20961         * @param {Node} this This node
20962         * @param {Node} node The child node to be removed
20963         */
20964        "beforeremove" : true,
20965        /**
20966         * @event beforemove
20967         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
20968         * @param {Tree} tree The owner tree
20969         * @param {Node} this This node
20970         * @param {Node} oldParent The parent of this node
20971         * @param {Node} newParent The new parent this node is moving to
20972         * @param {Number} index The index it is being moved to
20973         */
20974        "beforemove" : true,
20975        /**
20976         * @event beforeinsert
20977         * Fires before a new child is inserted, return false to cancel the insert.
20978         * @param {Tree} tree The owner tree
20979         * @param {Node} this This node
20980         * @param {Node} node The child node to be inserted
20981         * @param {Node} refNode The child node the node is being inserted before
20982         */
20983        "beforeinsert" : true
20984    });
20985     this.listeners = this.attributes.listeners;
20986     Roo.data.Node.superclass.constructor.call(this);
20987 };
20988
20989 Roo.extend(Roo.data.Node, Roo.util.Observable, {
20990     fireEvent : function(evtName){
20991         // first do standard event for this node
20992         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
20993             return false;
20994         }
20995         // then bubble it up to the tree if the event wasn't cancelled
20996         var ot = this.getOwnerTree();
20997         if(ot){
20998             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
20999                 return false;
21000             }
21001         }
21002         return true;
21003     },
21004
21005     /**
21006      * Returns true if this node is a leaf
21007      * @return {Boolean}
21008      */
21009     isLeaf : function(){
21010         return this.leaf === true;
21011     },
21012
21013     // private
21014     setFirstChild : function(node){
21015         this.firstChild = node;
21016     },
21017
21018     //private
21019     setLastChild : function(node){
21020         this.lastChild = node;
21021     },
21022
21023
21024     /**
21025      * Returns true if this node is the last child of its parent
21026      * @return {Boolean}
21027      */
21028     isLast : function(){
21029        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21030     },
21031
21032     /**
21033      * Returns true if this node is the first child of its parent
21034      * @return {Boolean}
21035      */
21036     isFirst : function(){
21037        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21038     },
21039
21040     hasChildNodes : function(){
21041         return !this.isLeaf() && this.childNodes.length > 0;
21042     },
21043
21044     /**
21045      * Insert node(s) as the last child node of this node.
21046      * @param {Node/Array} node The node or Array of nodes to append
21047      * @return {Node} The appended node if single append, or null if an array was passed
21048      */
21049     appendChild : function(node){
21050         var multi = false;
21051         if(node instanceof Array){
21052             multi = node;
21053         }else if(arguments.length > 1){
21054             multi = arguments;
21055         }
21056         // if passed an array or multiple args do them one by one
21057         if(multi){
21058             for(var i = 0, len = multi.length; i < len; i++) {
21059                 this.appendChild(multi[i]);
21060             }
21061         }else{
21062             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21063                 return false;
21064             }
21065             var index = this.childNodes.length;
21066             var oldParent = node.parentNode;
21067             // it's a move, make sure we move it cleanly
21068             if(oldParent){
21069                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21070                     return false;
21071                 }
21072                 oldParent.removeChild(node);
21073             }
21074             index = this.childNodes.length;
21075             if(index == 0){
21076                 this.setFirstChild(node);
21077             }
21078             this.childNodes.push(node);
21079             node.parentNode = this;
21080             var ps = this.childNodes[index-1];
21081             if(ps){
21082                 node.previousSibling = ps;
21083                 ps.nextSibling = node;
21084             }else{
21085                 node.previousSibling = null;
21086             }
21087             node.nextSibling = null;
21088             this.setLastChild(node);
21089             node.setOwnerTree(this.getOwnerTree());
21090             this.fireEvent("append", this.ownerTree, this, node, index);
21091             if(oldParent){
21092                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21093             }
21094             return node;
21095         }
21096     },
21097
21098     /**
21099      * Removes a child node from this node.
21100      * @param {Node} node The node to remove
21101      * @return {Node} The removed node
21102      */
21103     removeChild : function(node){
21104         var index = this.childNodes.indexOf(node);
21105         if(index == -1){
21106             return false;
21107         }
21108         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21109             return false;
21110         }
21111
21112         // remove it from childNodes collection
21113         this.childNodes.splice(index, 1);
21114
21115         // update siblings
21116         if(node.previousSibling){
21117             node.previousSibling.nextSibling = node.nextSibling;
21118         }
21119         if(node.nextSibling){
21120             node.nextSibling.previousSibling = node.previousSibling;
21121         }
21122
21123         // update child refs
21124         if(this.firstChild == node){
21125             this.setFirstChild(node.nextSibling);
21126         }
21127         if(this.lastChild == node){
21128             this.setLastChild(node.previousSibling);
21129         }
21130
21131         node.setOwnerTree(null);
21132         // clear any references from the node
21133         node.parentNode = null;
21134         node.previousSibling = null;
21135         node.nextSibling = null;
21136         this.fireEvent("remove", this.ownerTree, this, node);
21137         return node;
21138     },
21139
21140     /**
21141      * Inserts the first node before the second node in this nodes childNodes collection.
21142      * @param {Node} node The node to insert
21143      * @param {Node} refNode The node to insert before (if null the node is appended)
21144      * @return {Node} The inserted node
21145      */
21146     insertBefore : function(node, refNode){
21147         if(!refNode){ // like standard Dom, refNode can be null for append
21148             return this.appendChild(node);
21149         }
21150         // nothing to do
21151         if(node == refNode){
21152             return false;
21153         }
21154
21155         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21156             return false;
21157         }
21158         var index = this.childNodes.indexOf(refNode);
21159         var oldParent = node.parentNode;
21160         var refIndex = index;
21161
21162         // when moving internally, indexes will change after remove
21163         if(oldParent == this && this.childNodes.indexOf(node) < index){
21164             refIndex--;
21165         }
21166
21167         // it's a move, make sure we move it cleanly
21168         if(oldParent){
21169             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21170                 return false;
21171             }
21172             oldParent.removeChild(node);
21173         }
21174         if(refIndex == 0){
21175             this.setFirstChild(node);
21176         }
21177         this.childNodes.splice(refIndex, 0, node);
21178         node.parentNode = this;
21179         var ps = this.childNodes[refIndex-1];
21180         if(ps){
21181             node.previousSibling = ps;
21182             ps.nextSibling = node;
21183         }else{
21184             node.previousSibling = null;
21185         }
21186         node.nextSibling = refNode;
21187         refNode.previousSibling = node;
21188         node.setOwnerTree(this.getOwnerTree());
21189         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21190         if(oldParent){
21191             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21192         }
21193         return node;
21194     },
21195
21196     /**
21197      * Returns the child node at the specified index.
21198      * @param {Number} index
21199      * @return {Node}
21200      */
21201     item : function(index){
21202         return this.childNodes[index];
21203     },
21204
21205     /**
21206      * Replaces one child node in this node with another.
21207      * @param {Node} newChild The replacement node
21208      * @param {Node} oldChild The node to replace
21209      * @return {Node} The replaced node
21210      */
21211     replaceChild : function(newChild, oldChild){
21212         this.insertBefore(newChild, oldChild);
21213         this.removeChild(oldChild);
21214         return oldChild;
21215     },
21216
21217     /**
21218      * Returns the index of a child node
21219      * @param {Node} node
21220      * @return {Number} The index of the node or -1 if it was not found
21221      */
21222     indexOf : function(child){
21223         return this.childNodes.indexOf(child);
21224     },
21225
21226     /**
21227      * Returns the tree this node is in.
21228      * @return {Tree}
21229      */
21230     getOwnerTree : function(){
21231         // if it doesn't have one, look for one
21232         if(!this.ownerTree){
21233             var p = this;
21234             while(p){
21235                 if(p.ownerTree){
21236                     this.ownerTree = p.ownerTree;
21237                     break;
21238                 }
21239                 p = p.parentNode;
21240             }
21241         }
21242         return this.ownerTree;
21243     },
21244
21245     /**
21246      * Returns depth of this node (the root node has a depth of 0)
21247      * @return {Number}
21248      */
21249     getDepth : function(){
21250         var depth = 0;
21251         var p = this;
21252         while(p.parentNode){
21253             ++depth;
21254             p = p.parentNode;
21255         }
21256         return depth;
21257     },
21258
21259     // private
21260     setOwnerTree : function(tree){
21261         // if it's move, we need to update everyone
21262         if(tree != this.ownerTree){
21263             if(this.ownerTree){
21264                 this.ownerTree.unregisterNode(this);
21265             }
21266             this.ownerTree = tree;
21267             var cs = this.childNodes;
21268             for(var i = 0, len = cs.length; i < len; i++) {
21269                 cs[i].setOwnerTree(tree);
21270             }
21271             if(tree){
21272                 tree.registerNode(this);
21273             }
21274         }
21275     },
21276
21277     /**
21278      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21279      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21280      * @return {String} The path
21281      */
21282     getPath : function(attr){
21283         attr = attr || "id";
21284         var p = this.parentNode;
21285         var b = [this.attributes[attr]];
21286         while(p){
21287             b.unshift(p.attributes[attr]);
21288             p = p.parentNode;
21289         }
21290         var sep = this.getOwnerTree().pathSeparator;
21291         return sep + b.join(sep);
21292     },
21293
21294     /**
21295      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21296      * function call will be the scope provided or the current node. The arguments to the function
21297      * will be the args provided or the current node. If the function returns false at any point,
21298      * the bubble is stopped.
21299      * @param {Function} fn The function to call
21300      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21301      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21302      */
21303     bubble : function(fn, scope, args){
21304         var p = this;
21305         while(p){
21306             if(fn.call(scope || p, args || p) === false){
21307                 break;
21308             }
21309             p = p.parentNode;
21310         }
21311     },
21312
21313     /**
21314      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21315      * function call will be the scope provided or the current node. The arguments to the function
21316      * will be the args provided or the current node. If the function returns false at any point,
21317      * the cascade is stopped on that branch.
21318      * @param {Function} fn The function to call
21319      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21320      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21321      */
21322     cascade : function(fn, scope, args){
21323         if(fn.call(scope || this, args || this) !== false){
21324             var cs = this.childNodes;
21325             for(var i = 0, len = cs.length; i < len; i++) {
21326                 cs[i].cascade(fn, scope, args);
21327             }
21328         }
21329     },
21330
21331     /**
21332      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21333      * function call will be the scope provided or the current node. The arguments to the function
21334      * will be the args provided or the current node. If the function returns false at any point,
21335      * the iteration stops.
21336      * @param {Function} fn The function to call
21337      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21338      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21339      */
21340     eachChild : function(fn, scope, args){
21341         var cs = this.childNodes;
21342         for(var i = 0, len = cs.length; i < len; i++) {
21343                 if(fn.call(scope || this, args || cs[i]) === false){
21344                     break;
21345                 }
21346         }
21347     },
21348
21349     /**
21350      * Finds the first child that has the attribute with the specified value.
21351      * @param {String} attribute The attribute name
21352      * @param {Mixed} value The value to search for
21353      * @return {Node} The found child or null if none was found
21354      */
21355     findChild : function(attribute, value){
21356         var cs = this.childNodes;
21357         for(var i = 0, len = cs.length; i < len; i++) {
21358                 if(cs[i].attributes[attribute] == value){
21359                     return cs[i];
21360                 }
21361         }
21362         return null;
21363     },
21364
21365     /**
21366      * Finds the first child by a custom function. The child matches if the function passed
21367      * returns true.
21368      * @param {Function} fn
21369      * @param {Object} scope (optional)
21370      * @return {Node} The found child or null if none was found
21371      */
21372     findChildBy : function(fn, scope){
21373         var cs = this.childNodes;
21374         for(var i = 0, len = cs.length; i < len; i++) {
21375                 if(fn.call(scope||cs[i], cs[i]) === true){
21376                     return cs[i];
21377                 }
21378         }
21379         return null;
21380     },
21381
21382     /**
21383      * Sorts this nodes children using the supplied sort function
21384      * @param {Function} fn
21385      * @param {Object} scope (optional)
21386      */
21387     sort : function(fn, scope){
21388         var cs = this.childNodes;
21389         var len = cs.length;
21390         if(len > 0){
21391             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21392             cs.sort(sortFn);
21393             for(var i = 0; i < len; i++){
21394                 var n = cs[i];
21395                 n.previousSibling = cs[i-1];
21396                 n.nextSibling = cs[i+1];
21397                 if(i == 0){
21398                     this.setFirstChild(n);
21399                 }
21400                 if(i == len-1){
21401                     this.setLastChild(n);
21402                 }
21403             }
21404         }
21405     },
21406
21407     /**
21408      * Returns true if this node is an ancestor (at any point) of the passed node.
21409      * @param {Node} node
21410      * @return {Boolean}
21411      */
21412     contains : function(node){
21413         return node.isAncestor(this);
21414     },
21415
21416     /**
21417      * Returns true if the passed node is an ancestor (at any point) of this node.
21418      * @param {Node} node
21419      * @return {Boolean}
21420      */
21421     isAncestor : function(node){
21422         var p = this.parentNode;
21423         while(p){
21424             if(p == node){
21425                 return true;
21426             }
21427             p = p.parentNode;
21428         }
21429         return false;
21430     },
21431
21432     toString : function(){
21433         return "[Node"+(this.id?" "+this.id:"")+"]";
21434     }
21435 });/*
21436  * Based on:
21437  * Ext JS Library 1.1.1
21438  * Copyright(c) 2006-2007, Ext JS, LLC.
21439  *
21440  * Originally Released Under LGPL - original licence link has changed is not relivant.
21441  *
21442  * Fork - LGPL
21443  * <script type="text/javascript">
21444  */
21445  
21446
21447 /**
21448  * @class Roo.ComponentMgr
21449  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21450  * @singleton
21451  */
21452 Roo.ComponentMgr = function(){
21453     var all = new Roo.util.MixedCollection();
21454
21455     return {
21456         /**
21457          * Registers a component.
21458          * @param {Roo.Component} c The component
21459          */
21460         register : function(c){
21461             all.add(c);
21462         },
21463
21464         /**
21465          * Unregisters a component.
21466          * @param {Roo.Component} c The component
21467          */
21468         unregister : function(c){
21469             all.remove(c);
21470         },
21471
21472         /**
21473          * Returns a component by id
21474          * @param {String} id The component id
21475          */
21476         get : function(id){
21477             return all.get(id);
21478         },
21479
21480         /**
21481          * Registers a function that will be called when a specified component is added to ComponentMgr
21482          * @param {String} id The component id
21483          * @param {Funtction} fn The callback function
21484          * @param {Object} scope The scope of the callback
21485          */
21486         onAvailable : function(id, fn, scope){
21487             all.on("add", function(index, o){
21488                 if(o.id == id){
21489                     fn.call(scope || o, o);
21490                     all.un("add", fn, scope);
21491                 }
21492             });
21493         }
21494     };
21495 }();/*
21496  * Based on:
21497  * Ext JS Library 1.1.1
21498  * Copyright(c) 2006-2007, Ext JS, LLC.
21499  *
21500  * Originally Released Under LGPL - original licence link has changed is not relivant.
21501  *
21502  * Fork - LGPL
21503  * <script type="text/javascript">
21504  */
21505  
21506 /**
21507  * @class Roo.Component
21508  * @extends Roo.util.Observable
21509  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21510  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21511  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21512  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21513  * All visual components (widgets) that require rendering into a layout should subclass Component.
21514  * @constructor
21515  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21516  * 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
21517  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21518  */
21519 Roo.Component = function(config){
21520     config = config || {};
21521     if(config.tagName || config.dom || typeof config == "string"){ // element object
21522         config = {el: config, id: config.id || config};
21523     }
21524     this.initialConfig = config;
21525
21526     Roo.apply(this, config);
21527     this.addEvents({
21528         /**
21529          * @event disable
21530          * Fires after the component is disabled.
21531              * @param {Roo.Component} this
21532              */
21533         disable : true,
21534         /**
21535          * @event enable
21536          * Fires after the component is enabled.
21537              * @param {Roo.Component} this
21538              */
21539         enable : true,
21540         /**
21541          * @event beforeshow
21542          * Fires before the component is shown.  Return false to stop the show.
21543              * @param {Roo.Component} this
21544              */
21545         beforeshow : true,
21546         /**
21547          * @event show
21548          * Fires after the component is shown.
21549              * @param {Roo.Component} this
21550              */
21551         show : true,
21552         /**
21553          * @event beforehide
21554          * Fires before the component is hidden. Return false to stop the hide.
21555              * @param {Roo.Component} this
21556              */
21557         beforehide : true,
21558         /**
21559          * @event hide
21560          * Fires after the component is hidden.
21561              * @param {Roo.Component} this
21562              */
21563         hide : true,
21564         /**
21565          * @event beforerender
21566          * Fires before the component is rendered. Return false to stop the render.
21567              * @param {Roo.Component} this
21568              */
21569         beforerender : true,
21570         /**
21571          * @event render
21572          * Fires after the component is rendered.
21573              * @param {Roo.Component} this
21574              */
21575         render : true,
21576         /**
21577          * @event beforedestroy
21578          * Fires before the component is destroyed. Return false to stop the destroy.
21579              * @param {Roo.Component} this
21580              */
21581         beforedestroy : true,
21582         /**
21583          * @event destroy
21584          * Fires after the component is destroyed.
21585              * @param {Roo.Component} this
21586              */
21587         destroy : true
21588     });
21589     if(!this.id){
21590         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21591     }
21592     Roo.ComponentMgr.register(this);
21593     Roo.Component.superclass.constructor.call(this);
21594     this.initComponent();
21595     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21596         this.render(this.renderTo);
21597         delete this.renderTo;
21598     }
21599 };
21600
21601 // private
21602 Roo.Component.AUTO_ID = 1000;
21603
21604 Roo.extend(Roo.Component, Roo.util.Observable, {
21605     /**
21606      * @property {Boolean} hidden
21607      * true if this component is hidden. Read-only.
21608      */
21609     hidden : false,
21610     /**
21611      * true if this component is disabled. Read-only.
21612      */
21613     disabled : false,
21614     /**
21615      * true if this component has been rendered. Read-only.
21616      */
21617     rendered : false,
21618     
21619     /** @cfg {String} disableClass
21620      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21621      */
21622     disabledClass : "x-item-disabled",
21623         /** @cfg {Boolean} allowDomMove
21624          * Whether the component can move the Dom node when rendering (defaults to true).
21625          */
21626     allowDomMove : true,
21627     /** @cfg {String} hideMode
21628      * How this component should hidden. Supported values are
21629      * "visibility" (css visibility), "offsets" (negative offset position) and
21630      * "display" (css display) - defaults to "display".
21631      */
21632     hideMode: 'display',
21633
21634     // private
21635     ctype : "Roo.Component",
21636
21637     /** @cfg {String} actionMode 
21638      * which property holds the element that used for  hide() / show() / disable() / enable()
21639      * default is 'el' 
21640      */
21641     actionMode : "el",
21642
21643     // private
21644     getActionEl : function(){
21645         return this[this.actionMode];
21646     },
21647
21648     initComponent : Roo.emptyFn,
21649     /**
21650      * If this is a lazy rendering component, render it to its container element.
21651      * @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.
21652      */
21653     render : function(container, position){
21654         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21655             if(!container && this.el){
21656                 this.el = Roo.get(this.el);
21657                 container = this.el.dom.parentNode;
21658                 this.allowDomMove = false;
21659             }
21660             this.container = Roo.get(container);
21661             this.rendered = true;
21662             if(position !== undefined){
21663                 if(typeof position == 'number'){
21664                     position = this.container.dom.childNodes[position];
21665                 }else{
21666                     position = Roo.getDom(position);
21667                 }
21668             }
21669             this.onRender(this.container, position || null);
21670             if(this.cls){
21671                 this.el.addClass(this.cls);
21672                 delete this.cls;
21673             }
21674             if(this.style){
21675                 this.el.applyStyles(this.style);
21676                 delete this.style;
21677             }
21678             this.fireEvent("render", this);
21679             this.afterRender(this.container);
21680             if(this.hidden){
21681                 this.hide();
21682             }
21683             if(this.disabled){
21684                 this.disable();
21685             }
21686         }
21687         return this;
21688     },
21689
21690     // private
21691     // default function is not really useful
21692     onRender : function(ct, position){
21693         if(this.el){
21694             this.el = Roo.get(this.el);
21695             if(this.allowDomMove !== false){
21696                 ct.dom.insertBefore(this.el.dom, position);
21697             }
21698         }
21699     },
21700
21701     // private
21702     getAutoCreate : function(){
21703         var cfg = typeof this.autoCreate == "object" ?
21704                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21705         if(this.id && !cfg.id){
21706             cfg.id = this.id;
21707         }
21708         return cfg;
21709     },
21710
21711     // private
21712     afterRender : Roo.emptyFn,
21713
21714     /**
21715      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21716      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21717      */
21718     destroy : function(){
21719         if(this.fireEvent("beforedestroy", this) !== false){
21720             this.purgeListeners();
21721             this.beforeDestroy();
21722             if(this.rendered){
21723                 this.el.removeAllListeners();
21724                 this.el.remove();
21725                 if(this.actionMode == "container"){
21726                     this.container.remove();
21727                 }
21728             }
21729             this.onDestroy();
21730             Roo.ComponentMgr.unregister(this);
21731             this.fireEvent("destroy", this);
21732         }
21733     },
21734
21735         // private
21736     beforeDestroy : function(){
21737
21738     },
21739
21740         // private
21741         onDestroy : function(){
21742
21743     },
21744
21745     /**
21746      * Returns the underlying {@link Roo.Element}.
21747      * @return {Roo.Element} The element
21748      */
21749     getEl : function(){
21750         return this.el;
21751     },
21752
21753     /**
21754      * Returns the id of this component.
21755      * @return {String}
21756      */
21757     getId : function(){
21758         return this.id;
21759     },
21760
21761     /**
21762      * Try to focus this component.
21763      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21764      * @return {Roo.Component} this
21765      */
21766     focus : function(selectText){
21767         if(this.rendered){
21768             this.el.focus();
21769             if(selectText === true){
21770                 this.el.dom.select();
21771             }
21772         }
21773         return this;
21774     },
21775
21776     // private
21777     blur : function(){
21778         if(this.rendered){
21779             this.el.blur();
21780         }
21781         return this;
21782     },
21783
21784     /**
21785      * Disable this component.
21786      * @return {Roo.Component} this
21787      */
21788     disable : function(){
21789         if(this.rendered){
21790             this.onDisable();
21791         }
21792         this.disabled = true;
21793         this.fireEvent("disable", this);
21794         return this;
21795     },
21796
21797         // private
21798     onDisable : function(){
21799         this.getActionEl().addClass(this.disabledClass);
21800         this.el.dom.disabled = true;
21801     },
21802
21803     /**
21804      * Enable this component.
21805      * @return {Roo.Component} this
21806      */
21807     enable : function(){
21808         if(this.rendered){
21809             this.onEnable();
21810         }
21811         this.disabled = false;
21812         this.fireEvent("enable", this);
21813         return this;
21814     },
21815
21816         // private
21817     onEnable : function(){
21818         this.getActionEl().removeClass(this.disabledClass);
21819         this.el.dom.disabled = false;
21820     },
21821
21822     /**
21823      * Convenience function for setting disabled/enabled by boolean.
21824      * @param {Boolean} disabled
21825      */
21826     setDisabled : function(disabled){
21827         this[disabled ? "disable" : "enable"]();
21828     },
21829
21830     /**
21831      * Show this component.
21832      * @return {Roo.Component} this
21833      */
21834     show: function(){
21835         if(this.fireEvent("beforeshow", this) !== false){
21836             this.hidden = false;
21837             if(this.rendered){
21838                 this.onShow();
21839             }
21840             this.fireEvent("show", this);
21841         }
21842         return this;
21843     },
21844
21845     // private
21846     onShow : function(){
21847         var ae = this.getActionEl();
21848         if(this.hideMode == 'visibility'){
21849             ae.dom.style.visibility = "visible";
21850         }else if(this.hideMode == 'offsets'){
21851             ae.removeClass('x-hidden');
21852         }else{
21853             ae.dom.style.display = "";
21854         }
21855     },
21856
21857     /**
21858      * Hide this component.
21859      * @return {Roo.Component} this
21860      */
21861     hide: function(){
21862         if(this.fireEvent("beforehide", this) !== false){
21863             this.hidden = true;
21864             if(this.rendered){
21865                 this.onHide();
21866             }
21867             this.fireEvent("hide", this);
21868         }
21869         return this;
21870     },
21871
21872     // private
21873     onHide : function(){
21874         var ae = this.getActionEl();
21875         if(this.hideMode == 'visibility'){
21876             ae.dom.style.visibility = "hidden";
21877         }else if(this.hideMode == 'offsets'){
21878             ae.addClass('x-hidden');
21879         }else{
21880             ae.dom.style.display = "none";
21881         }
21882     },
21883
21884     /**
21885      * Convenience function to hide or show this component by boolean.
21886      * @param {Boolean} visible True to show, false to hide
21887      * @return {Roo.Component} this
21888      */
21889     setVisible: function(visible){
21890         if(visible) {
21891             this.show();
21892         }else{
21893             this.hide();
21894         }
21895         return this;
21896     },
21897
21898     /**
21899      * Returns true if this component is visible.
21900      */
21901     isVisible : function(){
21902         return this.getActionEl().isVisible();
21903     },
21904
21905     cloneConfig : function(overrides){
21906         overrides = overrides || {};
21907         var id = overrides.id || Roo.id();
21908         var cfg = Roo.applyIf(overrides, this.initialConfig);
21909         cfg.id = id; // prevent dup id
21910         return new this.constructor(cfg);
21911     }
21912 });/*
21913  * Based on:
21914  * Ext JS Library 1.1.1
21915  * Copyright(c) 2006-2007, Ext JS, LLC.
21916  *
21917  * Originally Released Under LGPL - original licence link has changed is not relivant.
21918  *
21919  * Fork - LGPL
21920  * <script type="text/javascript">
21921  */
21922  (function(){ 
21923 /**
21924  * @class Roo.Layer
21925  * @extends Roo.Element
21926  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
21927  * automatic maintaining of shadow/shim positions.
21928  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
21929  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
21930  * you can pass a string with a CSS class name. False turns off the shadow.
21931  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
21932  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
21933  * @cfg {String} cls CSS class to add to the element
21934  * @cfg {Number} zindex Starting z-index (defaults to 11000)
21935  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
21936  * @constructor
21937  * @param {Object} config An object with config options.
21938  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
21939  */
21940
21941 Roo.Layer = function(config, existingEl){
21942     config = config || {};
21943     var dh = Roo.DomHelper;
21944     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
21945     if(existingEl){
21946         this.dom = Roo.getDom(existingEl);
21947     }
21948     if(!this.dom){
21949         var o = config.dh || {tag: "div", cls: "x-layer"};
21950         this.dom = dh.append(pel, o);
21951     }
21952     if(config.cls){
21953         this.addClass(config.cls);
21954     }
21955     this.constrain = config.constrain !== false;
21956     this.visibilityMode = Roo.Element.VISIBILITY;
21957     if(config.id){
21958         this.id = this.dom.id = config.id;
21959     }else{
21960         this.id = Roo.id(this.dom);
21961     }
21962     this.zindex = config.zindex || this.getZIndex();
21963     this.position("absolute", this.zindex);
21964     if(config.shadow){
21965         this.shadowOffset = config.shadowOffset || 4;
21966         this.shadow = new Roo.Shadow({
21967             offset : this.shadowOffset,
21968             mode : config.shadow
21969         });
21970     }else{
21971         this.shadowOffset = 0;
21972     }
21973     this.useShim = config.shim !== false && Roo.useShims;
21974     this.useDisplay = config.useDisplay;
21975     this.hide();
21976 };
21977
21978 var supr = Roo.Element.prototype;
21979
21980 // shims are shared among layer to keep from having 100 iframes
21981 var shims = [];
21982
21983 Roo.extend(Roo.Layer, Roo.Element, {
21984
21985     getZIndex : function(){
21986         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
21987     },
21988
21989     getShim : function(){
21990         if(!this.useShim){
21991             return null;
21992         }
21993         if(this.shim){
21994             return this.shim;
21995         }
21996         var shim = shims.shift();
21997         if(!shim){
21998             shim = this.createShim();
21999             shim.enableDisplayMode('block');
22000             shim.dom.style.display = 'none';
22001             shim.dom.style.visibility = 'visible';
22002         }
22003         var pn = this.dom.parentNode;
22004         if(shim.dom.parentNode != pn){
22005             pn.insertBefore(shim.dom, this.dom);
22006         }
22007         shim.setStyle('z-index', this.getZIndex()-2);
22008         this.shim = shim;
22009         return shim;
22010     },
22011
22012     hideShim : function(){
22013         if(this.shim){
22014             this.shim.setDisplayed(false);
22015             shims.push(this.shim);
22016             delete this.shim;
22017         }
22018     },
22019
22020     disableShadow : function(){
22021         if(this.shadow){
22022             this.shadowDisabled = true;
22023             this.shadow.hide();
22024             this.lastShadowOffset = this.shadowOffset;
22025             this.shadowOffset = 0;
22026         }
22027     },
22028
22029     enableShadow : function(show){
22030         if(this.shadow){
22031             this.shadowDisabled = false;
22032             this.shadowOffset = this.lastShadowOffset;
22033             delete this.lastShadowOffset;
22034             if(show){
22035                 this.sync(true);
22036             }
22037         }
22038     },
22039
22040     // private
22041     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22042     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22043     sync : function(doShow){
22044         var sw = this.shadow;
22045         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22046             var sh = this.getShim();
22047
22048             var w = this.getWidth(),
22049                 h = this.getHeight();
22050
22051             var l = this.getLeft(true),
22052                 t = this.getTop(true);
22053
22054             if(sw && !this.shadowDisabled){
22055                 if(doShow && !sw.isVisible()){
22056                     sw.show(this);
22057                 }else{
22058                     sw.realign(l, t, w, h);
22059                 }
22060                 if(sh){
22061                     if(doShow){
22062                        sh.show();
22063                     }
22064                     // fit the shim behind the shadow, so it is shimmed too
22065                     var a = sw.adjusts, s = sh.dom.style;
22066                     s.left = (Math.min(l, l+a.l))+"px";
22067                     s.top = (Math.min(t, t+a.t))+"px";
22068                     s.width = (w+a.w)+"px";
22069                     s.height = (h+a.h)+"px";
22070                 }
22071             }else if(sh){
22072                 if(doShow){
22073                    sh.show();
22074                 }
22075                 sh.setSize(w, h);
22076                 sh.setLeftTop(l, t);
22077             }
22078             
22079         }
22080     },
22081
22082     // private
22083     destroy : function(){
22084         this.hideShim();
22085         if(this.shadow){
22086             this.shadow.hide();
22087         }
22088         this.removeAllListeners();
22089         var pn = this.dom.parentNode;
22090         if(pn){
22091             pn.removeChild(this.dom);
22092         }
22093         Roo.Element.uncache(this.id);
22094     },
22095
22096     remove : function(){
22097         this.destroy();
22098     },
22099
22100     // private
22101     beginUpdate : function(){
22102         this.updating = true;
22103     },
22104
22105     // private
22106     endUpdate : function(){
22107         this.updating = false;
22108         this.sync(true);
22109     },
22110
22111     // private
22112     hideUnders : function(negOffset){
22113         if(this.shadow){
22114             this.shadow.hide();
22115         }
22116         this.hideShim();
22117     },
22118
22119     // private
22120     constrainXY : function(){
22121         if(this.constrain){
22122             var vw = Roo.lib.Dom.getViewWidth(),
22123                 vh = Roo.lib.Dom.getViewHeight();
22124             var s = Roo.get(document).getScroll();
22125
22126             var xy = this.getXY();
22127             var x = xy[0], y = xy[1];   
22128             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22129             // only move it if it needs it
22130             var moved = false;
22131             // first validate right/bottom
22132             if((x + w) > vw+s.left){
22133                 x = vw - w - this.shadowOffset;
22134                 moved = true;
22135             }
22136             if((y + h) > vh+s.top){
22137                 y = vh - h - this.shadowOffset;
22138                 moved = true;
22139             }
22140             // then make sure top/left isn't negative
22141             if(x < s.left){
22142                 x = s.left;
22143                 moved = true;
22144             }
22145             if(y < s.top){
22146                 y = s.top;
22147                 moved = true;
22148             }
22149             if(moved){
22150                 if(this.avoidY){
22151                     var ay = this.avoidY;
22152                     if(y <= ay && (y+h) >= ay){
22153                         y = ay-h-5;   
22154                     }
22155                 }
22156                 xy = [x, y];
22157                 this.storeXY(xy);
22158                 supr.setXY.call(this, xy);
22159                 this.sync();
22160             }
22161         }
22162     },
22163
22164     isVisible : function(){
22165         return this.visible;    
22166     },
22167
22168     // private
22169     showAction : function(){
22170         this.visible = true; // track visibility to prevent getStyle calls
22171         if(this.useDisplay === true){
22172             this.setDisplayed("");
22173         }else if(this.lastXY){
22174             supr.setXY.call(this, this.lastXY);
22175         }else if(this.lastLT){
22176             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22177         }
22178     },
22179
22180     // private
22181     hideAction : function(){
22182         this.visible = false;
22183         if(this.useDisplay === true){
22184             this.setDisplayed(false);
22185         }else{
22186             this.setLeftTop(-10000,-10000);
22187         }
22188     },
22189
22190     // overridden Element method
22191     setVisible : function(v, a, d, c, e){
22192         if(v){
22193             this.showAction();
22194         }
22195         if(a && v){
22196             var cb = function(){
22197                 this.sync(true);
22198                 if(c){
22199                     c();
22200                 }
22201             }.createDelegate(this);
22202             supr.setVisible.call(this, true, true, d, cb, e);
22203         }else{
22204             if(!v){
22205                 this.hideUnders(true);
22206             }
22207             var cb = c;
22208             if(a){
22209                 cb = function(){
22210                     this.hideAction();
22211                     if(c){
22212                         c();
22213                     }
22214                 }.createDelegate(this);
22215             }
22216             supr.setVisible.call(this, v, a, d, cb, e);
22217             if(v){
22218                 this.sync(true);
22219             }else if(!a){
22220                 this.hideAction();
22221             }
22222         }
22223     },
22224
22225     storeXY : function(xy){
22226         delete this.lastLT;
22227         this.lastXY = xy;
22228     },
22229
22230     storeLeftTop : function(left, top){
22231         delete this.lastXY;
22232         this.lastLT = [left, top];
22233     },
22234
22235     // private
22236     beforeFx : function(){
22237         this.beforeAction();
22238         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22239     },
22240
22241     // private
22242     afterFx : function(){
22243         Roo.Layer.superclass.afterFx.apply(this, arguments);
22244         this.sync(this.isVisible());
22245     },
22246
22247     // private
22248     beforeAction : function(){
22249         if(!this.updating && this.shadow){
22250             this.shadow.hide();
22251         }
22252     },
22253
22254     // overridden Element method
22255     setLeft : function(left){
22256         this.storeLeftTop(left, this.getTop(true));
22257         supr.setLeft.apply(this, arguments);
22258         this.sync();
22259     },
22260
22261     setTop : function(top){
22262         this.storeLeftTop(this.getLeft(true), top);
22263         supr.setTop.apply(this, arguments);
22264         this.sync();
22265     },
22266
22267     setLeftTop : function(left, top){
22268         this.storeLeftTop(left, top);
22269         supr.setLeftTop.apply(this, arguments);
22270         this.sync();
22271     },
22272
22273     setXY : function(xy, a, d, c, e){
22274         this.fixDisplay();
22275         this.beforeAction();
22276         this.storeXY(xy);
22277         var cb = this.createCB(c);
22278         supr.setXY.call(this, xy, a, d, cb, e);
22279         if(!a){
22280             cb();
22281         }
22282     },
22283
22284     // private
22285     createCB : function(c){
22286         var el = this;
22287         return function(){
22288             el.constrainXY();
22289             el.sync(true);
22290             if(c){
22291                 c();
22292             }
22293         };
22294     },
22295
22296     // overridden Element method
22297     setX : function(x, a, d, c, e){
22298         this.setXY([x, this.getY()], a, d, c, e);
22299     },
22300
22301     // overridden Element method
22302     setY : function(y, a, d, c, e){
22303         this.setXY([this.getX(), y], a, d, c, e);
22304     },
22305
22306     // overridden Element method
22307     setSize : function(w, h, a, d, c, e){
22308         this.beforeAction();
22309         var cb = this.createCB(c);
22310         supr.setSize.call(this, w, h, a, d, cb, e);
22311         if(!a){
22312             cb();
22313         }
22314     },
22315
22316     // overridden Element method
22317     setWidth : function(w, a, d, c, e){
22318         this.beforeAction();
22319         var cb = this.createCB(c);
22320         supr.setWidth.call(this, w, a, d, cb, e);
22321         if(!a){
22322             cb();
22323         }
22324     },
22325
22326     // overridden Element method
22327     setHeight : function(h, a, d, c, e){
22328         this.beforeAction();
22329         var cb = this.createCB(c);
22330         supr.setHeight.call(this, h, a, d, cb, e);
22331         if(!a){
22332             cb();
22333         }
22334     },
22335
22336     // overridden Element method
22337     setBounds : function(x, y, w, h, a, d, c, e){
22338         this.beforeAction();
22339         var cb = this.createCB(c);
22340         if(!a){
22341             this.storeXY([x, y]);
22342             supr.setXY.call(this, [x, y]);
22343             supr.setSize.call(this, w, h, a, d, cb, e);
22344             cb();
22345         }else{
22346             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22347         }
22348         return this;
22349     },
22350     
22351     /**
22352      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22353      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22354      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22355      * @param {Number} zindex The new z-index to set
22356      * @return {this} The Layer
22357      */
22358     setZIndex : function(zindex){
22359         this.zindex = zindex;
22360         this.setStyle("z-index", zindex + 2);
22361         if(this.shadow){
22362             this.shadow.setZIndex(zindex + 1);
22363         }
22364         if(this.shim){
22365             this.shim.setStyle("z-index", zindex);
22366         }
22367     }
22368 });
22369 })();/*
22370  * Based on:
22371  * Ext JS Library 1.1.1
22372  * Copyright(c) 2006-2007, Ext JS, LLC.
22373  *
22374  * Originally Released Under LGPL - original licence link has changed is not relivant.
22375  *
22376  * Fork - LGPL
22377  * <script type="text/javascript">
22378  */
22379
22380
22381 /**
22382  * @class Roo.Shadow
22383  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22384  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22385  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22386  * @constructor
22387  * Create a new Shadow
22388  * @param {Object} config The config object
22389  */
22390 Roo.Shadow = function(config){
22391     Roo.apply(this, config);
22392     if(typeof this.mode != "string"){
22393         this.mode = this.defaultMode;
22394     }
22395     var o = this.offset, a = {h: 0};
22396     var rad = Math.floor(this.offset/2);
22397     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22398         case "drop":
22399             a.w = 0;
22400             a.l = a.t = o;
22401             a.t -= 1;
22402             if(Roo.isIE){
22403                 a.l -= this.offset + rad;
22404                 a.t -= this.offset + rad;
22405                 a.w -= rad;
22406                 a.h -= rad;
22407                 a.t += 1;
22408             }
22409         break;
22410         case "sides":
22411             a.w = (o*2);
22412             a.l = -o;
22413             a.t = o-1;
22414             if(Roo.isIE){
22415                 a.l -= (this.offset - rad);
22416                 a.t -= this.offset + rad;
22417                 a.l += 1;
22418                 a.w -= (this.offset - rad)*2;
22419                 a.w -= rad + 1;
22420                 a.h -= 1;
22421             }
22422         break;
22423         case "frame":
22424             a.w = a.h = (o*2);
22425             a.l = a.t = -o;
22426             a.t += 1;
22427             a.h -= 2;
22428             if(Roo.isIE){
22429                 a.l -= (this.offset - rad);
22430                 a.t -= (this.offset - rad);
22431                 a.l += 1;
22432                 a.w -= (this.offset + rad + 1);
22433                 a.h -= (this.offset + rad);
22434                 a.h += 1;
22435             }
22436         break;
22437     };
22438
22439     this.adjusts = a;
22440 };
22441
22442 Roo.Shadow.prototype = {
22443     /**
22444      * @cfg {String} mode
22445      * The shadow display mode.  Supports the following options:<br />
22446      * sides: Shadow displays on both sides and bottom only<br />
22447      * frame: Shadow displays equally on all four sides<br />
22448      * drop: Traditional bottom-right drop shadow (default)
22449      */
22450     /**
22451      * @cfg {String} offset
22452      * The number of pixels to offset the shadow from the element (defaults to 4)
22453      */
22454     offset: 4,
22455
22456     // private
22457     defaultMode: "drop",
22458
22459     /**
22460      * Displays the shadow under the target element
22461      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22462      */
22463     show : function(target){
22464         target = Roo.get(target);
22465         if(!this.el){
22466             this.el = Roo.Shadow.Pool.pull();
22467             if(this.el.dom.nextSibling != target.dom){
22468                 this.el.insertBefore(target);
22469             }
22470         }
22471         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22472         if(Roo.isIE){
22473             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22474         }
22475         this.realign(
22476             target.getLeft(true),
22477             target.getTop(true),
22478             target.getWidth(),
22479             target.getHeight()
22480         );
22481         this.el.dom.style.display = "block";
22482     },
22483
22484     /**
22485      * Returns true if the shadow is visible, else false
22486      */
22487     isVisible : function(){
22488         return this.el ? true : false;  
22489     },
22490
22491     /**
22492      * Direct alignment when values are already available. Show must be called at least once before
22493      * calling this method to ensure it is initialized.
22494      * @param {Number} left The target element left position
22495      * @param {Number} top The target element top position
22496      * @param {Number} width The target element width
22497      * @param {Number} height The target element height
22498      */
22499     realign : function(l, t, w, h){
22500         if(!this.el){
22501             return;
22502         }
22503         var a = this.adjusts, d = this.el.dom, s = d.style;
22504         var iea = 0;
22505         s.left = (l+a.l)+"px";
22506         s.top = (t+a.t)+"px";
22507         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22508         if(s.width != sws || s.height != shs){
22509             s.width = sws;
22510             s.height = shs;
22511             if(!Roo.isIE){
22512                 var cn = d.childNodes;
22513                 var sww = Math.max(0, (sw-12))+"px";
22514                 cn[0].childNodes[1].style.width = sww;
22515                 cn[1].childNodes[1].style.width = sww;
22516                 cn[2].childNodes[1].style.width = sww;
22517                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22518             }
22519         }
22520     },
22521
22522     /**
22523      * Hides this shadow
22524      */
22525     hide : function(){
22526         if(this.el){
22527             this.el.dom.style.display = "none";
22528             Roo.Shadow.Pool.push(this.el);
22529             delete this.el;
22530         }
22531     },
22532
22533     /**
22534      * Adjust the z-index of this shadow
22535      * @param {Number} zindex The new z-index
22536      */
22537     setZIndex : function(z){
22538         this.zIndex = z;
22539         if(this.el){
22540             this.el.setStyle("z-index", z);
22541         }
22542     }
22543 };
22544
22545 // Private utility class that manages the internal Shadow cache
22546 Roo.Shadow.Pool = function(){
22547     var p = [];
22548     var markup = Roo.isIE ?
22549                  '<div class="x-ie-shadow"></div>' :
22550                  '<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>';
22551     return {
22552         pull : function(){
22553             var sh = p.shift();
22554             if(!sh){
22555                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22556                 sh.autoBoxAdjust = false;
22557             }
22558             return sh;
22559         },
22560
22561         push : function(sh){
22562             p.push(sh);
22563         }
22564     };
22565 }();/*
22566  * Based on:
22567  * Ext JS Library 1.1.1
22568  * Copyright(c) 2006-2007, Ext JS, LLC.
22569  *
22570  * Originally Released Under LGPL - original licence link has changed is not relivant.
22571  *
22572  * Fork - LGPL
22573  * <script type="text/javascript">
22574  */
22575
22576 /**
22577  * @class Roo.BoxComponent
22578  * @extends Roo.Component
22579  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22580  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22581  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22582  * layout containers.
22583  * @constructor
22584  * @param {Roo.Element/String/Object} config The configuration options.
22585  */
22586 Roo.BoxComponent = function(config){
22587     Roo.Component.call(this, config);
22588     this.addEvents({
22589         /**
22590          * @event resize
22591          * Fires after the component is resized.
22592              * @param {Roo.Component} this
22593              * @param {Number} adjWidth The box-adjusted width that was set
22594              * @param {Number} adjHeight The box-adjusted height that was set
22595              * @param {Number} rawWidth The width that was originally specified
22596              * @param {Number} rawHeight The height that was originally specified
22597              */
22598         resize : true,
22599         /**
22600          * @event move
22601          * Fires after the component is moved.
22602              * @param {Roo.Component} this
22603              * @param {Number} x The new x position
22604              * @param {Number} y The new y position
22605              */
22606         move : true
22607     });
22608 };
22609
22610 Roo.extend(Roo.BoxComponent, Roo.Component, {
22611     // private, set in afterRender to signify that the component has been rendered
22612     boxReady : false,
22613     // private, used to defer height settings to subclasses
22614     deferHeight: false,
22615     /** @cfg {Number} width
22616      * width (optional) size of component
22617      */
22618      /** @cfg {Number} height
22619      * height (optional) size of component
22620      */
22621      
22622     /**
22623      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22624      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22625      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22626      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22627      * @return {Roo.BoxComponent} this
22628      */
22629     setSize : function(w, h){
22630         // support for standard size objects
22631         if(typeof w == 'object'){
22632             h = w.height;
22633             w = w.width;
22634         }
22635         // not rendered
22636         if(!this.boxReady){
22637             this.width = w;
22638             this.height = h;
22639             return this;
22640         }
22641
22642         // prevent recalcs when not needed
22643         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22644             return this;
22645         }
22646         this.lastSize = {width: w, height: h};
22647
22648         var adj = this.adjustSize(w, h);
22649         var aw = adj.width, ah = adj.height;
22650         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22651             var rz = this.getResizeEl();
22652             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22653                 rz.setSize(aw, ah);
22654             }else if(!this.deferHeight && ah !== undefined){
22655                 rz.setHeight(ah);
22656             }else if(aw !== undefined){
22657                 rz.setWidth(aw);
22658             }
22659             this.onResize(aw, ah, w, h);
22660             this.fireEvent('resize', this, aw, ah, w, h);
22661         }
22662         return this;
22663     },
22664
22665     /**
22666      * Gets the current size of the component's underlying element.
22667      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22668      */
22669     getSize : function(){
22670         return this.el.getSize();
22671     },
22672
22673     /**
22674      * Gets the current XY position of the component's underlying element.
22675      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22676      * @return {Array} The XY position of the element (e.g., [100, 200])
22677      */
22678     getPosition : function(local){
22679         if(local === true){
22680             return [this.el.getLeft(true), this.el.getTop(true)];
22681         }
22682         return this.xy || this.el.getXY();
22683     },
22684
22685     /**
22686      * Gets the current box measurements of the component's underlying element.
22687      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22688      * @returns {Object} box An object in the format {x, y, width, height}
22689      */
22690     getBox : function(local){
22691         var s = this.el.getSize();
22692         if(local){
22693             s.x = this.el.getLeft(true);
22694             s.y = this.el.getTop(true);
22695         }else{
22696             var xy = this.xy || this.el.getXY();
22697             s.x = xy[0];
22698             s.y = xy[1];
22699         }
22700         return s;
22701     },
22702
22703     /**
22704      * Sets the current box measurements of the component's underlying element.
22705      * @param {Object} box An object in the format {x, y, width, height}
22706      * @returns {Roo.BoxComponent} this
22707      */
22708     updateBox : function(box){
22709         this.setSize(box.width, box.height);
22710         this.setPagePosition(box.x, box.y);
22711         return this;
22712     },
22713
22714     // protected
22715     getResizeEl : function(){
22716         return this.resizeEl || this.el;
22717     },
22718
22719     // protected
22720     getPositionEl : function(){
22721         return this.positionEl || this.el;
22722     },
22723
22724     /**
22725      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22726      * This method fires the move event.
22727      * @param {Number} left The new left
22728      * @param {Number} top The new top
22729      * @returns {Roo.BoxComponent} this
22730      */
22731     setPosition : function(x, y){
22732         this.x = x;
22733         this.y = y;
22734         if(!this.boxReady){
22735             return this;
22736         }
22737         var adj = this.adjustPosition(x, y);
22738         var ax = adj.x, ay = adj.y;
22739
22740         var el = this.getPositionEl();
22741         if(ax !== undefined || ay !== undefined){
22742             if(ax !== undefined && ay !== undefined){
22743                 el.setLeftTop(ax, ay);
22744             }else if(ax !== undefined){
22745                 el.setLeft(ax);
22746             }else if(ay !== undefined){
22747                 el.setTop(ay);
22748             }
22749             this.onPosition(ax, ay);
22750             this.fireEvent('move', this, ax, ay);
22751         }
22752         return this;
22753     },
22754
22755     /**
22756      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22757      * This method fires the move event.
22758      * @param {Number} x The new x position
22759      * @param {Number} y The new y position
22760      * @returns {Roo.BoxComponent} this
22761      */
22762     setPagePosition : function(x, y){
22763         this.pageX = x;
22764         this.pageY = y;
22765         if(!this.boxReady){
22766             return;
22767         }
22768         if(x === undefined || y === undefined){ // cannot translate undefined points
22769             return;
22770         }
22771         var p = this.el.translatePoints(x, y);
22772         this.setPosition(p.left, p.top);
22773         return this;
22774     },
22775
22776     // private
22777     onRender : function(ct, position){
22778         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22779         if(this.resizeEl){
22780             this.resizeEl = Roo.get(this.resizeEl);
22781         }
22782         if(this.positionEl){
22783             this.positionEl = Roo.get(this.positionEl);
22784         }
22785     },
22786
22787     // private
22788     afterRender : function(){
22789         Roo.BoxComponent.superclass.afterRender.call(this);
22790         this.boxReady = true;
22791         this.setSize(this.width, this.height);
22792         if(this.x || this.y){
22793             this.setPosition(this.x, this.y);
22794         }
22795         if(this.pageX || this.pageY){
22796             this.setPagePosition(this.pageX, this.pageY);
22797         }
22798     },
22799
22800     /**
22801      * Force the component's size to recalculate based on the underlying element's current height and width.
22802      * @returns {Roo.BoxComponent} this
22803      */
22804     syncSize : function(){
22805         delete this.lastSize;
22806         this.setSize(this.el.getWidth(), this.el.getHeight());
22807         return this;
22808     },
22809
22810     /**
22811      * Called after the component is resized, this method is empty by default but can be implemented by any
22812      * subclass that needs to perform custom logic after a resize occurs.
22813      * @param {Number} adjWidth The box-adjusted width that was set
22814      * @param {Number} adjHeight The box-adjusted height that was set
22815      * @param {Number} rawWidth The width that was originally specified
22816      * @param {Number} rawHeight The height that was originally specified
22817      */
22818     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22819
22820     },
22821
22822     /**
22823      * Called after the component is moved, this method is empty by default but can be implemented by any
22824      * subclass that needs to perform custom logic after a move occurs.
22825      * @param {Number} x The new x position
22826      * @param {Number} y The new y position
22827      */
22828     onPosition : function(x, y){
22829
22830     },
22831
22832     // private
22833     adjustSize : function(w, h){
22834         if(this.autoWidth){
22835             w = 'auto';
22836         }
22837         if(this.autoHeight){
22838             h = 'auto';
22839         }
22840         return {width : w, height: h};
22841     },
22842
22843     // private
22844     adjustPosition : function(x, y){
22845         return {x : x, y: y};
22846     }
22847 });/*
22848  * Based on:
22849  * Ext JS Library 1.1.1
22850  * Copyright(c) 2006-2007, Ext JS, LLC.
22851  *
22852  * Originally Released Under LGPL - original licence link has changed is not relivant.
22853  *
22854  * Fork - LGPL
22855  * <script type="text/javascript">
22856  */
22857
22858
22859 /**
22860  * @class Roo.SplitBar
22861  * @extends Roo.util.Observable
22862  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
22863  * <br><br>
22864  * Usage:
22865  * <pre><code>
22866 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
22867                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
22868 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
22869 split.minSize = 100;
22870 split.maxSize = 600;
22871 split.animate = true;
22872 split.on('moved', splitterMoved);
22873 </code></pre>
22874  * @constructor
22875  * Create a new SplitBar
22876  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
22877  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
22878  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22879  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
22880                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
22881                         position of the SplitBar).
22882  */
22883 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
22884     
22885     /** @private */
22886     this.el = Roo.get(dragElement, true);
22887     this.el.dom.unselectable = "on";
22888     /** @private */
22889     this.resizingEl = Roo.get(resizingElement, true);
22890
22891     /**
22892      * @private
22893      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
22894      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
22895      * @type Number
22896      */
22897     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
22898     
22899     /**
22900      * The minimum size of the resizing element. (Defaults to 0)
22901      * @type Number
22902      */
22903     this.minSize = 0;
22904     
22905     /**
22906      * The maximum size of the resizing element. (Defaults to 2000)
22907      * @type Number
22908      */
22909     this.maxSize = 2000;
22910     
22911     /**
22912      * Whether to animate the transition to the new size
22913      * @type Boolean
22914      */
22915     this.animate = false;
22916     
22917     /**
22918      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
22919      * @type Boolean
22920      */
22921     this.useShim = false;
22922     
22923     /** @private */
22924     this.shim = null;
22925     
22926     if(!existingProxy){
22927         /** @private */
22928         this.proxy = Roo.SplitBar.createProxy(this.orientation);
22929     }else{
22930         this.proxy = Roo.get(existingProxy).dom;
22931     }
22932     /** @private */
22933     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
22934     
22935     /** @private */
22936     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
22937     
22938     /** @private */
22939     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
22940     
22941     /** @private */
22942     this.dragSpecs = {};
22943     
22944     /**
22945      * @private The adapter to use to positon and resize elements
22946      */
22947     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
22948     this.adapter.init(this);
22949     
22950     if(this.orientation == Roo.SplitBar.HORIZONTAL){
22951         /** @private */
22952         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
22953         this.el.addClass("x-splitbar-h");
22954     }else{
22955         /** @private */
22956         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
22957         this.el.addClass("x-splitbar-v");
22958     }
22959     
22960     this.addEvents({
22961         /**
22962          * @event resize
22963          * Fires when the splitter is moved (alias for {@link #event-moved})
22964          * @param {Roo.SplitBar} this
22965          * @param {Number} newSize the new width or height
22966          */
22967         "resize" : true,
22968         /**
22969          * @event moved
22970          * Fires when the splitter is moved
22971          * @param {Roo.SplitBar} this
22972          * @param {Number} newSize the new width or height
22973          */
22974         "moved" : true,
22975         /**
22976          * @event beforeresize
22977          * Fires before the splitter is dragged
22978          * @param {Roo.SplitBar} this
22979          */
22980         "beforeresize" : true,
22981
22982         "beforeapply" : true
22983     });
22984
22985     Roo.util.Observable.call(this);
22986 };
22987
22988 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
22989     onStartProxyDrag : function(x, y){
22990         this.fireEvent("beforeresize", this);
22991         if(!this.overlay){
22992             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
22993             o.unselectable();
22994             o.enableDisplayMode("block");
22995             // all splitbars share the same overlay
22996             Roo.SplitBar.prototype.overlay = o;
22997         }
22998         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
22999         this.overlay.show();
23000         Roo.get(this.proxy).setDisplayed("block");
23001         var size = this.adapter.getElementSize(this);
23002         this.activeMinSize = this.getMinimumSize();;
23003         this.activeMaxSize = this.getMaximumSize();;
23004         var c1 = size - this.activeMinSize;
23005         var c2 = Math.max(this.activeMaxSize - size, 0);
23006         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23007             this.dd.resetConstraints();
23008             this.dd.setXConstraint(
23009                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23010                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23011             );
23012             this.dd.setYConstraint(0, 0);
23013         }else{
23014             this.dd.resetConstraints();
23015             this.dd.setXConstraint(0, 0);
23016             this.dd.setYConstraint(
23017                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23018                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23019             );
23020          }
23021         this.dragSpecs.startSize = size;
23022         this.dragSpecs.startPoint = [x, y];
23023         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23024     },
23025     
23026     /** 
23027      * @private Called after the drag operation by the DDProxy
23028      */
23029     onEndProxyDrag : function(e){
23030         Roo.get(this.proxy).setDisplayed(false);
23031         var endPoint = Roo.lib.Event.getXY(e);
23032         if(this.overlay){
23033             this.overlay.hide();
23034         }
23035         var newSize;
23036         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23037             newSize = this.dragSpecs.startSize + 
23038                 (this.placement == Roo.SplitBar.LEFT ?
23039                     endPoint[0] - this.dragSpecs.startPoint[0] :
23040                     this.dragSpecs.startPoint[0] - endPoint[0]
23041                 );
23042         }else{
23043             newSize = this.dragSpecs.startSize + 
23044                 (this.placement == Roo.SplitBar.TOP ?
23045                     endPoint[1] - this.dragSpecs.startPoint[1] :
23046                     this.dragSpecs.startPoint[1] - endPoint[1]
23047                 );
23048         }
23049         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23050         if(newSize != this.dragSpecs.startSize){
23051             if(this.fireEvent('beforeapply', this, newSize) !== false){
23052                 this.adapter.setElementSize(this, newSize);
23053                 this.fireEvent("moved", this, newSize);
23054                 this.fireEvent("resize", this, newSize);
23055             }
23056         }
23057     },
23058     
23059     /**
23060      * Get the adapter this SplitBar uses
23061      * @return The adapter object
23062      */
23063     getAdapter : function(){
23064         return this.adapter;
23065     },
23066     
23067     /**
23068      * Set the adapter this SplitBar uses
23069      * @param {Object} adapter A SplitBar adapter object
23070      */
23071     setAdapter : function(adapter){
23072         this.adapter = adapter;
23073         this.adapter.init(this);
23074     },
23075     
23076     /**
23077      * Gets the minimum size for the resizing element
23078      * @return {Number} The minimum size
23079      */
23080     getMinimumSize : function(){
23081         return this.minSize;
23082     },
23083     
23084     /**
23085      * Sets the minimum size for the resizing element
23086      * @param {Number} minSize The minimum size
23087      */
23088     setMinimumSize : function(minSize){
23089         this.minSize = minSize;
23090     },
23091     
23092     /**
23093      * Gets the maximum size for the resizing element
23094      * @return {Number} The maximum size
23095      */
23096     getMaximumSize : function(){
23097         return this.maxSize;
23098     },
23099     
23100     /**
23101      * Sets the maximum size for the resizing element
23102      * @param {Number} maxSize The maximum size
23103      */
23104     setMaximumSize : function(maxSize){
23105         this.maxSize = maxSize;
23106     },
23107     
23108     /**
23109      * Sets the initialize size for the resizing element
23110      * @param {Number} size The initial size
23111      */
23112     setCurrentSize : function(size){
23113         var oldAnimate = this.animate;
23114         this.animate = false;
23115         this.adapter.setElementSize(this, size);
23116         this.animate = oldAnimate;
23117     },
23118     
23119     /**
23120      * Destroy this splitbar. 
23121      * @param {Boolean} removeEl True to remove the element
23122      */
23123     destroy : function(removeEl){
23124         if(this.shim){
23125             this.shim.remove();
23126         }
23127         this.dd.unreg();
23128         this.proxy.parentNode.removeChild(this.proxy);
23129         if(removeEl){
23130             this.el.remove();
23131         }
23132     }
23133 });
23134
23135 /**
23136  * @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.
23137  */
23138 Roo.SplitBar.createProxy = function(dir){
23139     var proxy = new Roo.Element(document.createElement("div"));
23140     proxy.unselectable();
23141     var cls = 'x-splitbar-proxy';
23142     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23143     document.body.appendChild(proxy.dom);
23144     return proxy.dom;
23145 };
23146
23147 /** 
23148  * @class Roo.SplitBar.BasicLayoutAdapter
23149  * Default Adapter. It assumes the splitter and resizing element are not positioned
23150  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23151  */
23152 Roo.SplitBar.BasicLayoutAdapter = function(){
23153 };
23154
23155 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23156     // do nothing for now
23157     init : function(s){
23158     
23159     },
23160     /**
23161      * Called before drag operations to get the current size of the resizing element. 
23162      * @param {Roo.SplitBar} s The SplitBar using this adapter
23163      */
23164      getElementSize : function(s){
23165         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23166             return s.resizingEl.getWidth();
23167         }else{
23168             return s.resizingEl.getHeight();
23169         }
23170     },
23171     
23172     /**
23173      * Called after drag operations to set the size of the resizing element.
23174      * @param {Roo.SplitBar} s The SplitBar using this adapter
23175      * @param {Number} newSize The new size to set
23176      * @param {Function} onComplete A function to be invoked when resizing is complete
23177      */
23178     setElementSize : function(s, newSize, onComplete){
23179         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23180             if(!s.animate){
23181                 s.resizingEl.setWidth(newSize);
23182                 if(onComplete){
23183                     onComplete(s, newSize);
23184                 }
23185             }else{
23186                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23187             }
23188         }else{
23189             
23190             if(!s.animate){
23191                 s.resizingEl.setHeight(newSize);
23192                 if(onComplete){
23193                     onComplete(s, newSize);
23194                 }
23195             }else{
23196                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23197             }
23198         }
23199     }
23200 };
23201
23202 /** 
23203  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23204  * @extends Roo.SplitBar.BasicLayoutAdapter
23205  * Adapter that  moves the splitter element to align with the resized sizing element. 
23206  * Used with an absolute positioned SplitBar.
23207  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23208  * document.body, make sure you assign an id to the body element.
23209  */
23210 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23211     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23212     this.container = Roo.get(container);
23213 };
23214
23215 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23216     init : function(s){
23217         this.basic.init(s);
23218     },
23219     
23220     getElementSize : function(s){
23221         return this.basic.getElementSize(s);
23222     },
23223     
23224     setElementSize : function(s, newSize, onComplete){
23225         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23226     },
23227     
23228     moveSplitter : function(s){
23229         var yes = Roo.SplitBar;
23230         switch(s.placement){
23231             case yes.LEFT:
23232                 s.el.setX(s.resizingEl.getRight());
23233                 break;
23234             case yes.RIGHT:
23235                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23236                 break;
23237             case yes.TOP:
23238                 s.el.setY(s.resizingEl.getBottom());
23239                 break;
23240             case yes.BOTTOM:
23241                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23242                 break;
23243         }
23244     }
23245 };
23246
23247 /**
23248  * Orientation constant - Create a vertical SplitBar
23249  * @static
23250  * @type Number
23251  */
23252 Roo.SplitBar.VERTICAL = 1;
23253
23254 /**
23255  * Orientation constant - Create a horizontal SplitBar
23256  * @static
23257  * @type Number
23258  */
23259 Roo.SplitBar.HORIZONTAL = 2;
23260
23261 /**
23262  * Placement constant - The resizing element is to the left of the splitter element
23263  * @static
23264  * @type Number
23265  */
23266 Roo.SplitBar.LEFT = 1;
23267
23268 /**
23269  * Placement constant - The resizing element is to the right of the splitter element
23270  * @static
23271  * @type Number
23272  */
23273 Roo.SplitBar.RIGHT = 2;
23274
23275 /**
23276  * Placement constant - The resizing element is positioned above the splitter element
23277  * @static
23278  * @type Number
23279  */
23280 Roo.SplitBar.TOP = 3;
23281
23282 /**
23283  * Placement constant - The resizing element is positioned under splitter element
23284  * @static
23285  * @type Number
23286  */
23287 Roo.SplitBar.BOTTOM = 4;
23288 /*
23289  * Based on:
23290  * Ext JS Library 1.1.1
23291  * Copyright(c) 2006-2007, Ext JS, LLC.
23292  *
23293  * Originally Released Under LGPL - original licence link has changed is not relivant.
23294  *
23295  * Fork - LGPL
23296  * <script type="text/javascript">
23297  */
23298
23299 /**
23300  * @class Roo.View
23301  * @extends Roo.util.Observable
23302  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23303  * This class also supports single and multi selection modes. <br>
23304  * Create a data model bound view:
23305  <pre><code>
23306  var store = new Roo.data.Store(...);
23307
23308  var view = new Roo.View({
23309     el : "my-element",
23310     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23311  
23312     singleSelect: true,
23313     selectedClass: "ydataview-selected",
23314     store: store
23315  });
23316
23317  // listen for node click?
23318  view.on("click", function(vw, index, node, e){
23319  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23320  });
23321
23322  // load XML data
23323  dataModel.load("foobar.xml");
23324  </code></pre>
23325  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23326  * <br><br>
23327  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23328  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23329  * 
23330  * Note: old style constructor is still suported (container, template, config)
23331  * 
23332  * @constructor
23333  * Create a new View
23334  * @param {Object} config The config object
23335  * 
23336  */
23337 Roo.View = function(config, depreciated_tpl, depreciated_config){
23338     
23339     if (typeof(depreciated_tpl) == 'undefined') {
23340         // new way.. - universal constructor.
23341         Roo.apply(this, config);
23342         this.el  = Roo.get(this.el);
23343     } else {
23344         // old format..
23345         this.el  = Roo.get(config);
23346         this.tpl = depreciated_tpl;
23347         Roo.apply(this, depreciated_config);
23348     }
23349      
23350     
23351     if(typeof(this.tpl) == "string"){
23352         this.tpl = new Roo.Template(this.tpl);
23353     } else {
23354         // support xtype ctors..
23355         this.tpl = new Roo.factory(this.tpl, Roo);
23356     }
23357     
23358     
23359     this.tpl.compile();
23360    
23361
23362      
23363     /** @private */
23364     this.addEvents({
23365     /**
23366      * @event beforeclick
23367      * Fires before a click is processed. Returns false to cancel the default action.
23368      * @param {Roo.View} this
23369      * @param {Number} index The index of the target node
23370      * @param {HTMLElement} node The target node
23371      * @param {Roo.EventObject} e The raw event object
23372      */
23373         "beforeclick" : true,
23374     /**
23375      * @event click
23376      * Fires when a template node is clicked.
23377      * @param {Roo.View} this
23378      * @param {Number} index The index of the target node
23379      * @param {HTMLElement} node The target node
23380      * @param {Roo.EventObject} e The raw event object
23381      */
23382         "click" : true,
23383     /**
23384      * @event dblclick
23385      * Fires when a template node is double clicked.
23386      * @param {Roo.View} this
23387      * @param {Number} index The index of the target node
23388      * @param {HTMLElement} node The target node
23389      * @param {Roo.EventObject} e The raw event object
23390      */
23391         "dblclick" : true,
23392     /**
23393      * @event contextmenu
23394      * Fires when a template node is right clicked.
23395      * @param {Roo.View} this
23396      * @param {Number} index The index of the target node
23397      * @param {HTMLElement} node The target node
23398      * @param {Roo.EventObject} e The raw event object
23399      */
23400         "contextmenu" : true,
23401     /**
23402      * @event selectionchange
23403      * Fires when the selected nodes change.
23404      * @param {Roo.View} this
23405      * @param {Array} selections Array of the selected nodes
23406      */
23407         "selectionchange" : true,
23408
23409     /**
23410      * @event beforeselect
23411      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23412      * @param {Roo.View} this
23413      * @param {HTMLElement} node The node to be selected
23414      * @param {Array} selections Array of currently selected nodes
23415      */
23416         "beforeselect" : true
23417     });
23418
23419     this.el.on({
23420         "click": this.onClick,
23421         "dblclick": this.onDblClick,
23422         "contextmenu": this.onContextMenu,
23423         scope:this
23424     });
23425
23426     this.selections = [];
23427     this.nodes = [];
23428     this.cmp = new Roo.CompositeElementLite([]);
23429     if(this.store){
23430         this.store = Roo.factory(this.store, Roo.data);
23431         this.setStore(this.store, true);
23432     }
23433     Roo.View.superclass.constructor.call(this);
23434 };
23435
23436 Roo.extend(Roo.View, Roo.util.Observable, {
23437     
23438      /**
23439      * @cfg {Roo.data.Store} store Data store to load data from.
23440      */
23441     store : false,
23442     
23443     /**
23444      * @cfg {String|Roo.Element} el The container element.
23445      */
23446     el : '',
23447     
23448     /**
23449      * @cfg {String|Roo.Template} tpl The template used by this View 
23450      */
23451     tpl : false,
23452     
23453     /**
23454      * @cfg {String} selectedClass The css class to add to selected nodes
23455      */
23456     selectedClass : "x-view-selected",
23457      /**
23458      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23459      */
23460     emptyText : "",
23461     /**
23462      * @cfg {Boolean} multiSelect Allow multiple selection
23463      */
23464     
23465     multiSelect : false,
23466     /**
23467      * @cfg {Boolean} singleSelect Allow single selection
23468      */
23469     singleSelect:  false,
23470     
23471     /**
23472      * Returns the element this view is bound to.
23473      * @return {Roo.Element}
23474      */
23475     getEl : function(){
23476         return this.el;
23477     },
23478
23479     /**
23480      * Refreshes the view.
23481      */
23482     refresh : function(){
23483         var t = this.tpl;
23484         this.clearSelections();
23485         this.el.update("");
23486         var html = [];
23487         var records = this.store.getRange();
23488         if(records.length < 1){
23489             this.el.update(this.emptyText);
23490             return;
23491         }
23492         for(var i = 0, len = records.length; i < len; i++){
23493             var data = this.prepareData(records[i].data, i, records[i]);
23494             html[html.length] = t.apply(data);
23495         }
23496         this.el.update(html.join(""));
23497         this.nodes = this.el.dom.childNodes;
23498         this.updateIndexes(0);
23499     },
23500
23501     /**
23502      * Function to override to reformat the data that is sent to
23503      * the template for each node.
23504      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23505      * a JSON object for an UpdateManager bound view).
23506      */
23507     prepareData : function(data){
23508         return data;
23509     },
23510
23511     onUpdate : function(ds, record){
23512         this.clearSelections();
23513         var index = this.store.indexOf(record);
23514         var n = this.nodes[index];
23515         this.tpl.insertBefore(n, this.prepareData(record.data));
23516         n.parentNode.removeChild(n);
23517         this.updateIndexes(index, index);
23518     },
23519
23520     onAdd : function(ds, records, index){
23521         this.clearSelections();
23522         if(this.nodes.length == 0){
23523             this.refresh();
23524             return;
23525         }
23526         var n = this.nodes[index];
23527         for(var i = 0, len = records.length; i < len; i++){
23528             var d = this.prepareData(records[i].data);
23529             if(n){
23530                 this.tpl.insertBefore(n, d);
23531             }else{
23532                 this.tpl.append(this.el, d);
23533             }
23534         }
23535         this.updateIndexes(index);
23536     },
23537
23538     onRemove : function(ds, record, index){
23539         this.clearSelections();
23540         this.el.dom.removeChild(this.nodes[index]);
23541         this.updateIndexes(index);
23542     },
23543
23544     /**
23545      * Refresh an individual node.
23546      * @param {Number} index
23547      */
23548     refreshNode : function(index){
23549         this.onUpdate(this.store, this.store.getAt(index));
23550     },
23551
23552     updateIndexes : function(startIndex, endIndex){
23553         var ns = this.nodes;
23554         startIndex = startIndex || 0;
23555         endIndex = endIndex || ns.length - 1;
23556         for(var i = startIndex; i <= endIndex; i++){
23557             ns[i].nodeIndex = i;
23558         }
23559     },
23560
23561     /**
23562      * Changes the data store this view uses and refresh the view.
23563      * @param {Store} store
23564      */
23565     setStore : function(store, initial){
23566         if(!initial && this.store){
23567             this.store.un("datachanged", this.refresh);
23568             this.store.un("add", this.onAdd);
23569             this.store.un("remove", this.onRemove);
23570             this.store.un("update", this.onUpdate);
23571             this.store.un("clear", this.refresh);
23572         }
23573         if(store){
23574           
23575             store.on("datachanged", this.refresh, this);
23576             store.on("add", this.onAdd, this);
23577             store.on("remove", this.onRemove, this);
23578             store.on("update", this.onUpdate, this);
23579             store.on("clear", this.refresh, this);
23580         }
23581         
23582         if(store){
23583             this.refresh();
23584         }
23585     },
23586
23587     /**
23588      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23589      * @param {HTMLElement} node
23590      * @return {HTMLElement} The template node
23591      */
23592     findItemFromChild : function(node){
23593         var el = this.el.dom;
23594         if(!node || node.parentNode == el){
23595                     return node;
23596             }
23597             var p = node.parentNode;
23598             while(p && p != el){
23599             if(p.parentNode == el){
23600                 return p;
23601             }
23602             p = p.parentNode;
23603         }
23604             return null;
23605     },
23606
23607     /** @ignore */
23608     onClick : function(e){
23609         var item = this.findItemFromChild(e.getTarget());
23610         if(item){
23611             var index = this.indexOf(item);
23612             if(this.onItemClick(item, index, e) !== false){
23613                 this.fireEvent("click", this, index, item, e);
23614             }
23615         }else{
23616             this.clearSelections();
23617         }
23618     },
23619
23620     /** @ignore */
23621     onContextMenu : function(e){
23622         var item = this.findItemFromChild(e.getTarget());
23623         if(item){
23624             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23625         }
23626     },
23627
23628     /** @ignore */
23629     onDblClick : function(e){
23630         var item = this.findItemFromChild(e.getTarget());
23631         if(item){
23632             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23633         }
23634     },
23635
23636     onItemClick : function(item, index, e){
23637         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23638             return false;
23639         }
23640         if(this.multiSelect || this.singleSelect){
23641             if(this.multiSelect && e.shiftKey && this.lastSelection){
23642                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23643             }else{
23644                 this.select(item, this.multiSelect && e.ctrlKey);
23645                 this.lastSelection = item;
23646             }
23647             e.preventDefault();
23648         }
23649         return true;
23650     },
23651
23652     /**
23653      * Get the number of selected nodes.
23654      * @return {Number}
23655      */
23656     getSelectionCount : function(){
23657         return this.selections.length;
23658     },
23659
23660     /**
23661      * Get the currently selected nodes.
23662      * @return {Array} An array of HTMLElements
23663      */
23664     getSelectedNodes : function(){
23665         return this.selections;
23666     },
23667
23668     /**
23669      * Get the indexes of the selected nodes.
23670      * @return {Array}
23671      */
23672     getSelectedIndexes : function(){
23673         var indexes = [], s = this.selections;
23674         for(var i = 0, len = s.length; i < len; i++){
23675             indexes.push(s[i].nodeIndex);
23676         }
23677         return indexes;
23678     },
23679
23680     /**
23681      * Clear all selections
23682      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23683      */
23684     clearSelections : function(suppressEvent){
23685         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23686             this.cmp.elements = this.selections;
23687             this.cmp.removeClass(this.selectedClass);
23688             this.selections = [];
23689             if(!suppressEvent){
23690                 this.fireEvent("selectionchange", this, this.selections);
23691             }
23692         }
23693     },
23694
23695     /**
23696      * Returns true if the passed node is selected
23697      * @param {HTMLElement/Number} node The node or node index
23698      * @return {Boolean}
23699      */
23700     isSelected : function(node){
23701         var s = this.selections;
23702         if(s.length < 1){
23703             return false;
23704         }
23705         node = this.getNode(node);
23706         return s.indexOf(node) !== -1;
23707     },
23708
23709     /**
23710      * Selects nodes.
23711      * @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
23712      * @param {Boolean} keepExisting (optional) true to keep existing selections
23713      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23714      */
23715     select : function(nodeInfo, keepExisting, suppressEvent){
23716         if(nodeInfo instanceof Array){
23717             if(!keepExisting){
23718                 this.clearSelections(true);
23719             }
23720             for(var i = 0, len = nodeInfo.length; i < len; i++){
23721                 this.select(nodeInfo[i], true, true);
23722             }
23723         } else{
23724             var node = this.getNode(nodeInfo);
23725             if(node && !this.isSelected(node)){
23726                 if(!keepExisting){
23727                     this.clearSelections(true);
23728                 }
23729                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23730                     Roo.fly(node).addClass(this.selectedClass);
23731                     this.selections.push(node);
23732                     if(!suppressEvent){
23733                         this.fireEvent("selectionchange", this, this.selections);
23734                     }
23735                 }
23736             }
23737         }
23738     },
23739
23740     /**
23741      * Gets a template node.
23742      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23743      * @return {HTMLElement} The node or null if it wasn't found
23744      */
23745     getNode : function(nodeInfo){
23746         if(typeof nodeInfo == "string"){
23747             return document.getElementById(nodeInfo);
23748         }else if(typeof nodeInfo == "number"){
23749             return this.nodes[nodeInfo];
23750         }
23751         return nodeInfo;
23752     },
23753
23754     /**
23755      * Gets a range template nodes.
23756      * @param {Number} startIndex
23757      * @param {Number} endIndex
23758      * @return {Array} An array of nodes
23759      */
23760     getNodes : function(start, end){
23761         var ns = this.nodes;
23762         start = start || 0;
23763         end = typeof end == "undefined" ? ns.length - 1 : end;
23764         var nodes = [];
23765         if(start <= end){
23766             for(var i = start; i <= end; i++){
23767                 nodes.push(ns[i]);
23768             }
23769         } else{
23770             for(var i = start; i >= end; i--){
23771                 nodes.push(ns[i]);
23772             }
23773         }
23774         return nodes;
23775     },
23776
23777     /**
23778      * Finds the index of the passed node
23779      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23780      * @return {Number} The index of the node or -1
23781      */
23782     indexOf : function(node){
23783         node = this.getNode(node);
23784         if(typeof node.nodeIndex == "number"){
23785             return node.nodeIndex;
23786         }
23787         var ns = this.nodes;
23788         for(var i = 0, len = ns.length; i < len; i++){
23789             if(ns[i] == node){
23790                 return i;
23791             }
23792         }
23793         return -1;
23794     }
23795 });
23796 /*
23797  * Based on:
23798  * Ext JS Library 1.1.1
23799  * Copyright(c) 2006-2007, Ext JS, LLC.
23800  *
23801  * Originally Released Under LGPL - original licence link has changed is not relivant.
23802  *
23803  * Fork - LGPL
23804  * <script type="text/javascript">
23805  */
23806
23807 /**
23808  * @class Roo.JsonView
23809  * @extends Roo.View
23810  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23811 <pre><code>
23812 var view = new Roo.JsonView({
23813     container: "my-element",
23814     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23815     multiSelect: true, 
23816     jsonRoot: "data" 
23817 });
23818
23819 // listen for node click?
23820 view.on("click", function(vw, index, node, e){
23821     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23822 });
23823
23824 // direct load of JSON data
23825 view.load("foobar.php");
23826
23827 // Example from my blog list
23828 var tpl = new Roo.Template(
23829     '&lt;div class="entry"&gt;' +
23830     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23831     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23832     "&lt;/div&gt;&lt;hr /&gt;"
23833 );
23834
23835 var moreView = new Roo.JsonView({
23836     container :  "entry-list", 
23837     template : tpl,
23838     jsonRoot: "posts"
23839 });
23840 moreView.on("beforerender", this.sortEntries, this);
23841 moreView.load({
23842     url: "/blog/get-posts.php",
23843     params: "allposts=true",
23844     text: "Loading Blog Entries..."
23845 });
23846 </code></pre>
23847
23848 * Note: old code is supported with arguments : (container, template, config)
23849
23850
23851  * @constructor
23852  * Create a new JsonView
23853  * 
23854  * @param {Object} config The config object
23855  * 
23856  */
23857 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
23858     
23859     
23860     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
23861
23862     var um = this.el.getUpdateManager();
23863     um.setRenderer(this);
23864     um.on("update", this.onLoad, this);
23865     um.on("failure", this.onLoadException, this);
23866
23867     /**
23868      * @event beforerender
23869      * Fires before rendering of the downloaded JSON data.
23870      * @param {Roo.JsonView} this
23871      * @param {Object} data The JSON data loaded
23872      */
23873     /**
23874      * @event load
23875      * Fires when data is loaded.
23876      * @param {Roo.JsonView} this
23877      * @param {Object} data The JSON data loaded
23878      * @param {Object} response The raw Connect response object
23879      */
23880     /**
23881      * @event loadexception
23882      * Fires when loading fails.
23883      * @param {Roo.JsonView} this
23884      * @param {Object} response The raw Connect response object
23885      */
23886     this.addEvents({
23887         'beforerender' : true,
23888         'load' : true,
23889         'loadexception' : true
23890     });
23891 };
23892 Roo.extend(Roo.JsonView, Roo.View, {
23893     /**
23894      * @type {String} The root property in the loaded JSON object that contains the data
23895      */
23896     jsonRoot : "",
23897
23898     /**
23899      * Refreshes the view.
23900      */
23901     refresh : function(){
23902         this.clearSelections();
23903         this.el.update("");
23904         var html = [];
23905         var o = this.jsonData;
23906         if(o && o.length > 0){
23907             for(var i = 0, len = o.length; i < len; i++){
23908                 var data = this.prepareData(o[i], i, o);
23909                 html[html.length] = this.tpl.apply(data);
23910             }
23911         }else{
23912             html.push(this.emptyText);
23913         }
23914         this.el.update(html.join(""));
23915         this.nodes = this.el.dom.childNodes;
23916         this.updateIndexes(0);
23917     },
23918
23919     /**
23920      * 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.
23921      * @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:
23922      <pre><code>
23923      view.load({
23924          url: "your-url.php",
23925          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
23926          callback: yourFunction,
23927          scope: yourObject, //(optional scope)
23928          discardUrl: false,
23929          nocache: false,
23930          text: "Loading...",
23931          timeout: 30,
23932          scripts: false
23933      });
23934      </code></pre>
23935      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
23936      * 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.
23937      * @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}
23938      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
23939      * @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.
23940      */
23941     load : function(){
23942         var um = this.el.getUpdateManager();
23943         um.update.apply(um, arguments);
23944     },
23945
23946     render : function(el, response){
23947         this.clearSelections();
23948         this.el.update("");
23949         var o;
23950         try{
23951             o = Roo.util.JSON.decode(response.responseText);
23952             if(this.jsonRoot){
23953                 
23954                 o = o[this.jsonRoot];
23955             }
23956         } catch(e){
23957         }
23958         /**
23959          * The current JSON data or null
23960          */
23961         this.jsonData = o;
23962         this.beforeRender();
23963         this.refresh();
23964     },
23965
23966 /**
23967  * Get the number of records in the current JSON dataset
23968  * @return {Number}
23969  */
23970     getCount : function(){
23971         return this.jsonData ? this.jsonData.length : 0;
23972     },
23973
23974 /**
23975  * Returns the JSON object for the specified node(s)
23976  * @param {HTMLElement/Array} node The node or an array of nodes
23977  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
23978  * you get the JSON object for the node
23979  */
23980     getNodeData : function(node){
23981         if(node instanceof Array){
23982             var data = [];
23983             for(var i = 0, len = node.length; i < len; i++){
23984                 data.push(this.getNodeData(node[i]));
23985             }
23986             return data;
23987         }
23988         return this.jsonData[this.indexOf(node)] || null;
23989     },
23990
23991     beforeRender : function(){
23992         this.snapshot = this.jsonData;
23993         if(this.sortInfo){
23994             this.sort.apply(this, this.sortInfo);
23995         }
23996         this.fireEvent("beforerender", this, this.jsonData);
23997     },
23998
23999     onLoad : function(el, o){
24000         this.fireEvent("load", this, this.jsonData, o);
24001     },
24002
24003     onLoadException : function(el, o){
24004         this.fireEvent("loadexception", this, o);
24005     },
24006
24007 /**
24008  * Filter the data by a specific property.
24009  * @param {String} property A property on your JSON objects
24010  * @param {String/RegExp} value Either string that the property values
24011  * should start with, or a RegExp to test against the property
24012  */
24013     filter : function(property, value){
24014         if(this.jsonData){
24015             var data = [];
24016             var ss = this.snapshot;
24017             if(typeof value == "string"){
24018                 var vlen = value.length;
24019                 if(vlen == 0){
24020                     this.clearFilter();
24021                     return;
24022                 }
24023                 value = value.toLowerCase();
24024                 for(var i = 0, len = ss.length; i < len; i++){
24025                     var o = ss[i];
24026                     if(o[property].substr(0, vlen).toLowerCase() == value){
24027                         data.push(o);
24028                     }
24029                 }
24030             } else if(value.exec){ // regex?
24031                 for(var i = 0, len = ss.length; i < len; i++){
24032                     var o = ss[i];
24033                     if(value.test(o[property])){
24034                         data.push(o);
24035                     }
24036                 }
24037             } else{
24038                 return;
24039             }
24040             this.jsonData = data;
24041             this.refresh();
24042         }
24043     },
24044
24045 /**
24046  * Filter by a function. The passed function will be called with each
24047  * object in the current dataset. If the function returns true the value is kept,
24048  * otherwise it is filtered.
24049  * @param {Function} fn
24050  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24051  */
24052     filterBy : function(fn, scope){
24053         if(this.jsonData){
24054             var data = [];
24055             var ss = this.snapshot;
24056             for(var i = 0, len = ss.length; i < len; i++){
24057                 var o = ss[i];
24058                 if(fn.call(scope || this, o)){
24059                     data.push(o);
24060                 }
24061             }
24062             this.jsonData = data;
24063             this.refresh();
24064         }
24065     },
24066
24067 /**
24068  * Clears the current filter.
24069  */
24070     clearFilter : function(){
24071         if(this.snapshot && this.jsonData != this.snapshot){
24072             this.jsonData = this.snapshot;
24073             this.refresh();
24074         }
24075     },
24076
24077
24078 /**
24079  * Sorts the data for this view and refreshes it.
24080  * @param {String} property A property on your JSON objects to sort on
24081  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24082  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24083  */
24084     sort : function(property, dir, sortType){
24085         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24086         if(this.jsonData){
24087             var p = property;
24088             var dsc = dir && dir.toLowerCase() == "desc";
24089             var f = function(o1, o2){
24090                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24091                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24092                 ;
24093                 if(v1 < v2){
24094                     return dsc ? +1 : -1;
24095                 } else if(v1 > v2){
24096                     return dsc ? -1 : +1;
24097                 } else{
24098                     return 0;
24099                 }
24100             };
24101             this.jsonData.sort(f);
24102             this.refresh();
24103             if(this.jsonData != this.snapshot){
24104                 this.snapshot.sort(f);
24105             }
24106         }
24107     }
24108 });/*
24109  * Based on:
24110  * Ext JS Library 1.1.1
24111  * Copyright(c) 2006-2007, Ext JS, LLC.
24112  *
24113  * Originally Released Under LGPL - original licence link has changed is not relivant.
24114  *
24115  * Fork - LGPL
24116  * <script type="text/javascript">
24117  */
24118  
24119
24120 /**
24121  * @class Roo.ColorPalette
24122  * @extends Roo.Component
24123  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24124  * Here's an example of typical usage:
24125  * <pre><code>
24126 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24127 cp.render('my-div');
24128
24129 cp.on('select', function(palette, selColor){
24130     // do something with selColor
24131 });
24132 </code></pre>
24133  * @constructor
24134  * Create a new ColorPalette
24135  * @param {Object} config The config object
24136  */
24137 Roo.ColorPalette = function(config){
24138     Roo.ColorPalette.superclass.constructor.call(this, config);
24139     this.addEvents({
24140         /**
24141              * @event select
24142              * Fires when a color is selected
24143              * @param {ColorPalette} this
24144              * @param {String} color The 6-digit color hex code (without the # symbol)
24145              */
24146         select: true
24147     });
24148
24149     if(this.handler){
24150         this.on("select", this.handler, this.scope, true);
24151     }
24152 };
24153 Roo.extend(Roo.ColorPalette, Roo.Component, {
24154     /**
24155      * @cfg {String} itemCls
24156      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24157      */
24158     itemCls : "x-color-palette",
24159     /**
24160      * @cfg {String} value
24161      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24162      * the hex codes are case-sensitive.
24163      */
24164     value : null,
24165     clickEvent:'click',
24166     // private
24167     ctype: "Roo.ColorPalette",
24168
24169     /**
24170      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24171      */
24172     allowReselect : false,
24173
24174     /**
24175      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24176      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24177      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24178      * of colors with the width setting until the box is symmetrical.</p>
24179      * <p>You can override individual colors if needed:</p>
24180      * <pre><code>
24181 var cp = new Roo.ColorPalette();
24182 cp.colors[0] = "FF0000";  // change the first box to red
24183 </code></pre>
24184
24185 Or you can provide a custom array of your own for complete control:
24186 <pre><code>
24187 var cp = new Roo.ColorPalette();
24188 cp.colors = ["000000", "993300", "333300"];
24189 </code></pre>
24190      * @type Array
24191      */
24192     colors : [
24193         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24194         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24195         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24196         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24197         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24198     ],
24199
24200     // private
24201     onRender : function(container, position){
24202         var t = new Roo.MasterTemplate(
24203             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24204         );
24205         var c = this.colors;
24206         for(var i = 0, len = c.length; i < len; i++){
24207             t.add([c[i]]);
24208         }
24209         var el = document.createElement("div");
24210         el.className = this.itemCls;
24211         t.overwrite(el);
24212         container.dom.insertBefore(el, position);
24213         this.el = Roo.get(el);
24214         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24215         if(this.clickEvent != 'click'){
24216             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24217         }
24218     },
24219
24220     // private
24221     afterRender : function(){
24222         Roo.ColorPalette.superclass.afterRender.call(this);
24223         if(this.value){
24224             var s = this.value;
24225             this.value = null;
24226             this.select(s);
24227         }
24228     },
24229
24230     // private
24231     handleClick : function(e, t){
24232         e.preventDefault();
24233         if(!this.disabled){
24234             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24235             this.select(c.toUpperCase());
24236         }
24237     },
24238
24239     /**
24240      * Selects the specified color in the palette (fires the select event)
24241      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24242      */
24243     select : function(color){
24244         color = color.replace("#", "");
24245         if(color != this.value || this.allowReselect){
24246             var el = this.el;
24247             if(this.value){
24248                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24249             }
24250             el.child("a.color-"+color).addClass("x-color-palette-sel");
24251             this.value = color;
24252             this.fireEvent("select", this, color);
24253         }
24254     }
24255 });/*
24256  * Based on:
24257  * Ext JS Library 1.1.1
24258  * Copyright(c) 2006-2007, Ext JS, LLC.
24259  *
24260  * Originally Released Under LGPL - original licence link has changed is not relivant.
24261  *
24262  * Fork - LGPL
24263  * <script type="text/javascript">
24264  */
24265  
24266 /**
24267  * @class Roo.DatePicker
24268  * @extends Roo.Component
24269  * Simple date picker class.
24270  * @constructor
24271  * Create a new DatePicker
24272  * @param {Object} config The config object
24273  */
24274 Roo.DatePicker = function(config){
24275     Roo.DatePicker.superclass.constructor.call(this, config);
24276
24277     this.value = config && config.value ?
24278                  config.value.clearTime() : new Date().clearTime();
24279
24280     this.addEvents({
24281         /**
24282              * @event select
24283              * Fires when a date is selected
24284              * @param {DatePicker} this
24285              * @param {Date} date The selected date
24286              */
24287         select: true
24288     });
24289
24290     if(this.handler){
24291         this.on("select", this.handler,  this.scope || this);
24292     }
24293     // build the disabledDatesRE
24294     if(!this.disabledDatesRE && this.disabledDates){
24295         var dd = this.disabledDates;
24296         var re = "(?:";
24297         for(var i = 0; i < dd.length; i++){
24298             re += dd[i];
24299             if(i != dd.length-1) re += "|";
24300         }
24301         this.disabledDatesRE = new RegExp(re + ")");
24302     }
24303 };
24304
24305 Roo.extend(Roo.DatePicker, Roo.Component, {
24306     /**
24307      * @cfg {String} todayText
24308      * The text to display on the button that selects the current date (defaults to "Today")
24309      */
24310     todayText : "Today",
24311     /**
24312      * @cfg {String} okText
24313      * The text to display on the ok button
24314      */
24315     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24316     /**
24317      * @cfg {String} cancelText
24318      * The text to display on the cancel button
24319      */
24320     cancelText : "Cancel",
24321     /**
24322      * @cfg {String} todayTip
24323      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24324      */
24325     todayTip : "{0} (Spacebar)",
24326     /**
24327      * @cfg {Date} minDate
24328      * Minimum allowable date (JavaScript date object, defaults to null)
24329      */
24330     minDate : null,
24331     /**
24332      * @cfg {Date} maxDate
24333      * Maximum allowable date (JavaScript date object, defaults to null)
24334      */
24335     maxDate : null,
24336     /**
24337      * @cfg {String} minText
24338      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24339      */
24340     minText : "This date is before the minimum date",
24341     /**
24342      * @cfg {String} maxText
24343      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24344      */
24345     maxText : "This date is after the maximum date",
24346     /**
24347      * @cfg {String} format
24348      * The default date format string which can be overriden for localization support.  The format must be
24349      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24350      */
24351     format : "m/d/y",
24352     /**
24353      * @cfg {Array} disabledDays
24354      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24355      */
24356     disabledDays : null,
24357     /**
24358      * @cfg {String} disabledDaysText
24359      * The tooltip to display when the date falls on a disabled day (defaults to "")
24360      */
24361     disabledDaysText : "",
24362     /**
24363      * @cfg {RegExp} disabledDatesRE
24364      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24365      */
24366     disabledDatesRE : null,
24367     /**
24368      * @cfg {String} disabledDatesText
24369      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24370      */
24371     disabledDatesText : "",
24372     /**
24373      * @cfg {Boolean} constrainToViewport
24374      * True to constrain the date picker to the viewport (defaults to true)
24375      */
24376     constrainToViewport : true,
24377     /**
24378      * @cfg {Array} monthNames
24379      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24380      */
24381     monthNames : Date.monthNames,
24382     /**
24383      * @cfg {Array} dayNames
24384      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24385      */
24386     dayNames : Date.dayNames,
24387     /**
24388      * @cfg {String} nextText
24389      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24390      */
24391     nextText: 'Next Month (Control+Right)',
24392     /**
24393      * @cfg {String} prevText
24394      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24395      */
24396     prevText: 'Previous Month (Control+Left)',
24397     /**
24398      * @cfg {String} monthYearText
24399      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24400      */
24401     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24402     /**
24403      * @cfg {Number} startDay
24404      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24405      */
24406     startDay : 0,
24407     /**
24408      * @cfg {Bool} showClear
24409      * Show a clear button (usefull for date form elements that can be blank.)
24410      */
24411     
24412     showClear: false,
24413     
24414     /**
24415      * Sets the value of the date field
24416      * @param {Date} value The date to set
24417      */
24418     setValue : function(value){
24419         var old = this.value;
24420         this.value = value.clearTime(true);
24421         if(this.el){
24422             this.update(this.value);
24423         }
24424     },
24425
24426     /**
24427      * Gets the current selected value of the date field
24428      * @return {Date} The selected date
24429      */
24430     getValue : function(){
24431         return this.value;
24432     },
24433
24434     // private
24435     focus : function(){
24436         if(this.el){
24437             this.update(this.activeDate);
24438         }
24439     },
24440
24441     // private
24442     onRender : function(container, position){
24443         var m = [
24444              '<table cellspacing="0">',
24445                 '<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>',
24446                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24447         var dn = this.dayNames;
24448         for(var i = 0; i < 7; i++){
24449             var d = this.startDay+i;
24450             if(d > 6){
24451                 d = d-7;
24452             }
24453             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24454         }
24455         m[m.length] = "</tr></thead><tbody><tr>";
24456         for(var i = 0; i < 42; i++) {
24457             if(i % 7 == 0 && i != 0){
24458                 m[m.length] = "</tr><tr>";
24459             }
24460             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24461         }
24462         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24463             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24464
24465         var el = document.createElement("div");
24466         el.className = "x-date-picker";
24467         el.innerHTML = m.join("");
24468
24469         container.dom.insertBefore(el, position);
24470
24471         this.el = Roo.get(el);
24472         this.eventEl = Roo.get(el.firstChild);
24473
24474         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24475             handler: this.showPrevMonth,
24476             scope: this,
24477             preventDefault:true,
24478             stopDefault:true
24479         });
24480
24481         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24482             handler: this.showNextMonth,
24483             scope: this,
24484             preventDefault:true,
24485             stopDefault:true
24486         });
24487
24488         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24489
24490         this.monthPicker = this.el.down('div.x-date-mp');
24491         this.monthPicker.enableDisplayMode('block');
24492         
24493         var kn = new Roo.KeyNav(this.eventEl, {
24494             "left" : function(e){
24495                 e.ctrlKey ?
24496                     this.showPrevMonth() :
24497                     this.update(this.activeDate.add("d", -1));
24498             },
24499
24500             "right" : function(e){
24501                 e.ctrlKey ?
24502                     this.showNextMonth() :
24503                     this.update(this.activeDate.add("d", 1));
24504             },
24505
24506             "up" : function(e){
24507                 e.ctrlKey ?
24508                     this.showNextYear() :
24509                     this.update(this.activeDate.add("d", -7));
24510             },
24511
24512             "down" : function(e){
24513                 e.ctrlKey ?
24514                     this.showPrevYear() :
24515                     this.update(this.activeDate.add("d", 7));
24516             },
24517
24518             "pageUp" : function(e){
24519                 this.showNextMonth();
24520             },
24521
24522             "pageDown" : function(e){
24523                 this.showPrevMonth();
24524             },
24525
24526             "enter" : function(e){
24527                 e.stopPropagation();
24528                 return true;
24529             },
24530
24531             scope : this
24532         });
24533
24534         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24535
24536         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24537
24538         this.el.unselectable();
24539         
24540         this.cells = this.el.select("table.x-date-inner tbody td");
24541         this.textNodes = this.el.query("table.x-date-inner tbody span");
24542
24543         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24544             text: "&#160;",
24545             tooltip: this.monthYearText
24546         });
24547
24548         this.mbtn.on('click', this.showMonthPicker, this);
24549         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24550
24551
24552         var today = (new Date()).dateFormat(this.format);
24553         
24554         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24555         baseTb.add({
24556             text: String.format(this.todayText, today),
24557             tooltip: String.format(this.todayTip, today),
24558             handler: this.selectToday,
24559             scope: this
24560         });
24561         
24562         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24563             
24564         //});
24565         if (this.showClear) {
24566             
24567             baseTb.add( new Roo.Toolbar.Fill());
24568             baseTb.add({
24569                 text: '&#160;',
24570                 cls: 'x-btn-icon x-btn-clear',
24571                 handler: function() {
24572                     //this.value = '';
24573                     this.fireEvent("select", this, '');
24574                 },
24575                 scope: this
24576             });
24577         }
24578         
24579         
24580         if(Roo.isIE){
24581             this.el.repaint();
24582         }
24583         this.update(this.value);
24584     },
24585
24586     createMonthPicker : function(){
24587         if(!this.monthPicker.dom.firstChild){
24588             var buf = ['<table border="0" cellspacing="0">'];
24589             for(var i = 0; i < 6; i++){
24590                 buf.push(
24591                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24592                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24593                     i == 0 ?
24594                     '<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>' :
24595                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24596                 );
24597             }
24598             buf.push(
24599                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24600                     this.okText,
24601                     '</button><button type="button" class="x-date-mp-cancel">',
24602                     this.cancelText,
24603                     '</button></td></tr>',
24604                 '</table>'
24605             );
24606             this.monthPicker.update(buf.join(''));
24607             this.monthPicker.on('click', this.onMonthClick, this);
24608             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24609
24610             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24611             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24612
24613             this.mpMonths.each(function(m, a, i){
24614                 i += 1;
24615                 if((i%2) == 0){
24616                     m.dom.xmonth = 5 + Math.round(i * .5);
24617                 }else{
24618                     m.dom.xmonth = Math.round((i-1) * .5);
24619                 }
24620             });
24621         }
24622     },
24623
24624     showMonthPicker : function(){
24625         this.createMonthPicker();
24626         var size = this.el.getSize();
24627         this.monthPicker.setSize(size);
24628         this.monthPicker.child('table').setSize(size);
24629
24630         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24631         this.updateMPMonth(this.mpSelMonth);
24632         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24633         this.updateMPYear(this.mpSelYear);
24634
24635         this.monthPicker.slideIn('t', {duration:.2});
24636     },
24637
24638     updateMPYear : function(y){
24639         this.mpyear = y;
24640         var ys = this.mpYears.elements;
24641         for(var i = 1; i <= 10; i++){
24642             var td = ys[i-1], y2;
24643             if((i%2) == 0){
24644                 y2 = y + Math.round(i * .5);
24645                 td.firstChild.innerHTML = y2;
24646                 td.xyear = y2;
24647             }else{
24648                 y2 = y - (5-Math.round(i * .5));
24649                 td.firstChild.innerHTML = y2;
24650                 td.xyear = y2;
24651             }
24652             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24653         }
24654     },
24655
24656     updateMPMonth : function(sm){
24657         this.mpMonths.each(function(m, a, i){
24658             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24659         });
24660     },
24661
24662     selectMPMonth: function(m){
24663         
24664     },
24665
24666     onMonthClick : function(e, t){
24667         e.stopEvent();
24668         var el = new Roo.Element(t), pn;
24669         if(el.is('button.x-date-mp-cancel')){
24670             this.hideMonthPicker();
24671         }
24672         else if(el.is('button.x-date-mp-ok')){
24673             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24674             this.hideMonthPicker();
24675         }
24676         else if(pn = el.up('td.x-date-mp-month', 2)){
24677             this.mpMonths.removeClass('x-date-mp-sel');
24678             pn.addClass('x-date-mp-sel');
24679             this.mpSelMonth = pn.dom.xmonth;
24680         }
24681         else if(pn = el.up('td.x-date-mp-year', 2)){
24682             this.mpYears.removeClass('x-date-mp-sel');
24683             pn.addClass('x-date-mp-sel');
24684             this.mpSelYear = pn.dom.xyear;
24685         }
24686         else if(el.is('a.x-date-mp-prev')){
24687             this.updateMPYear(this.mpyear-10);
24688         }
24689         else if(el.is('a.x-date-mp-next')){
24690             this.updateMPYear(this.mpyear+10);
24691         }
24692     },
24693
24694     onMonthDblClick : function(e, t){
24695         e.stopEvent();
24696         var el = new Roo.Element(t), pn;
24697         if(pn = el.up('td.x-date-mp-month', 2)){
24698             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24699             this.hideMonthPicker();
24700         }
24701         else if(pn = el.up('td.x-date-mp-year', 2)){
24702             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24703             this.hideMonthPicker();
24704         }
24705     },
24706
24707     hideMonthPicker : function(disableAnim){
24708         if(this.monthPicker){
24709             if(disableAnim === true){
24710                 this.monthPicker.hide();
24711             }else{
24712                 this.monthPicker.slideOut('t', {duration:.2});
24713             }
24714         }
24715     },
24716
24717     // private
24718     showPrevMonth : function(e){
24719         this.update(this.activeDate.add("mo", -1));
24720     },
24721
24722     // private
24723     showNextMonth : function(e){
24724         this.update(this.activeDate.add("mo", 1));
24725     },
24726
24727     // private
24728     showPrevYear : function(){
24729         this.update(this.activeDate.add("y", -1));
24730     },
24731
24732     // private
24733     showNextYear : function(){
24734         this.update(this.activeDate.add("y", 1));
24735     },
24736
24737     // private
24738     handleMouseWheel : function(e){
24739         var delta = e.getWheelDelta();
24740         if(delta > 0){
24741             this.showPrevMonth();
24742             e.stopEvent();
24743         } else if(delta < 0){
24744             this.showNextMonth();
24745             e.stopEvent();
24746         }
24747     },
24748
24749     // private
24750     handleDateClick : function(e, t){
24751         e.stopEvent();
24752         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24753             this.setValue(new Date(t.dateValue));
24754             this.fireEvent("select", this, this.value);
24755         }
24756     },
24757
24758     // private
24759     selectToday : function(){
24760         this.setValue(new Date().clearTime());
24761         this.fireEvent("select", this, this.value);
24762     },
24763
24764     // private
24765     update : function(date){
24766         var vd = this.activeDate;
24767         this.activeDate = date;
24768         if(vd && this.el){
24769             var t = date.getTime();
24770             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24771                 this.cells.removeClass("x-date-selected");
24772                 this.cells.each(function(c){
24773                    if(c.dom.firstChild.dateValue == t){
24774                        c.addClass("x-date-selected");
24775                        setTimeout(function(){
24776                             try{c.dom.firstChild.focus();}catch(e){}
24777                        }, 50);
24778                        return false;
24779                    }
24780                 });
24781                 return;
24782             }
24783         }
24784         var days = date.getDaysInMonth();
24785         var firstOfMonth = date.getFirstDateOfMonth();
24786         var startingPos = firstOfMonth.getDay()-this.startDay;
24787
24788         if(startingPos <= this.startDay){
24789             startingPos += 7;
24790         }
24791
24792         var pm = date.add("mo", -1);
24793         var prevStart = pm.getDaysInMonth()-startingPos;
24794
24795         var cells = this.cells.elements;
24796         var textEls = this.textNodes;
24797         days += startingPos;
24798
24799         // convert everything to numbers so it's fast
24800         var day = 86400000;
24801         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24802         var today = new Date().clearTime().getTime();
24803         var sel = date.clearTime().getTime();
24804         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24805         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24806         var ddMatch = this.disabledDatesRE;
24807         var ddText = this.disabledDatesText;
24808         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24809         var ddaysText = this.disabledDaysText;
24810         var format = this.format;
24811
24812         var setCellClass = function(cal, cell){
24813             cell.title = "";
24814             var t = d.getTime();
24815             cell.firstChild.dateValue = t;
24816             if(t == today){
24817                 cell.className += " x-date-today";
24818                 cell.title = cal.todayText;
24819             }
24820             if(t == sel){
24821                 cell.className += " x-date-selected";
24822                 setTimeout(function(){
24823                     try{cell.firstChild.focus();}catch(e){}
24824                 }, 50);
24825             }
24826             // disabling
24827             if(t < min) {
24828                 cell.className = " x-date-disabled";
24829                 cell.title = cal.minText;
24830                 return;
24831             }
24832             if(t > max) {
24833                 cell.className = " x-date-disabled";
24834                 cell.title = cal.maxText;
24835                 return;
24836             }
24837             if(ddays){
24838                 if(ddays.indexOf(d.getDay()) != -1){
24839                     cell.title = ddaysText;
24840                     cell.className = " x-date-disabled";
24841                 }
24842             }
24843             if(ddMatch && format){
24844                 var fvalue = d.dateFormat(format);
24845                 if(ddMatch.test(fvalue)){
24846                     cell.title = ddText.replace("%0", fvalue);
24847                     cell.className = " x-date-disabled";
24848                 }
24849             }
24850         };
24851
24852         var i = 0;
24853         for(; i < startingPos; i++) {
24854             textEls[i].innerHTML = (++prevStart);
24855             d.setDate(d.getDate()+1);
24856             cells[i].className = "x-date-prevday";
24857             setCellClass(this, cells[i]);
24858         }
24859         for(; i < days; i++){
24860             intDay = i - startingPos + 1;
24861             textEls[i].innerHTML = (intDay);
24862             d.setDate(d.getDate()+1);
24863             cells[i].className = "x-date-active";
24864             setCellClass(this, cells[i]);
24865         }
24866         var extraDays = 0;
24867         for(; i < 42; i++) {
24868              textEls[i].innerHTML = (++extraDays);
24869              d.setDate(d.getDate()+1);
24870              cells[i].className = "x-date-nextday";
24871              setCellClass(this, cells[i]);
24872         }
24873
24874         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
24875
24876         if(!this.internalRender){
24877             var main = this.el.dom.firstChild;
24878             var w = main.offsetWidth;
24879             this.el.setWidth(w + this.el.getBorderWidth("lr"));
24880             Roo.fly(main).setWidth(w);
24881             this.internalRender = true;
24882             // opera does not respect the auto grow header center column
24883             // then, after it gets a width opera refuses to recalculate
24884             // without a second pass
24885             if(Roo.isOpera && !this.secondPass){
24886                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
24887                 this.secondPass = true;
24888                 this.update.defer(10, this, [date]);
24889             }
24890         }
24891     }
24892 });/*
24893  * Based on:
24894  * Ext JS Library 1.1.1
24895  * Copyright(c) 2006-2007, Ext JS, LLC.
24896  *
24897  * Originally Released Under LGPL - original licence link has changed is not relivant.
24898  *
24899  * Fork - LGPL
24900  * <script type="text/javascript">
24901  */
24902 /**
24903  * @class Roo.TabPanel
24904  * @extends Roo.util.Observable
24905  * A lightweight tab container.
24906  * <br><br>
24907  * Usage:
24908  * <pre><code>
24909 // basic tabs 1, built from existing content
24910 var tabs = new Roo.TabPanel("tabs1");
24911 tabs.addTab("script", "View Script");
24912 tabs.addTab("markup", "View Markup");
24913 tabs.activate("script");
24914
24915 // more advanced tabs, built from javascript
24916 var jtabs = new Roo.TabPanel("jtabs");
24917 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
24918
24919 // set up the UpdateManager
24920 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
24921 var updater = tab2.getUpdateManager();
24922 updater.setDefaultUrl("ajax1.htm");
24923 tab2.on('activate', updater.refresh, updater, true);
24924
24925 // Use setUrl for Ajax loading
24926 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
24927 tab3.setUrl("ajax2.htm", null, true);
24928
24929 // Disabled tab
24930 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
24931 tab4.disable();
24932
24933 jtabs.activate("jtabs-1");
24934  * </code></pre>
24935  * @constructor
24936  * Create a new TabPanel.
24937  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
24938  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
24939  */
24940 Roo.TabPanel = function(container, config){
24941     /**
24942     * The container element for this TabPanel.
24943     * @type Roo.Element
24944     */
24945     this.el = Roo.get(container, true);
24946     if(config){
24947         if(typeof config == "boolean"){
24948             this.tabPosition = config ? "bottom" : "top";
24949         }else{
24950             Roo.apply(this, config);
24951         }
24952     }
24953     if(this.tabPosition == "bottom"){
24954         this.bodyEl = Roo.get(this.createBody(this.el.dom));
24955         this.el.addClass("x-tabs-bottom");
24956     }
24957     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
24958     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
24959     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
24960     if(Roo.isIE){
24961         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
24962     }
24963     if(this.tabPosition != "bottom"){
24964     /** The body element that contains {@link Roo.TabPanelItem} bodies.
24965      * @type Roo.Element
24966      */
24967       this.bodyEl = Roo.get(this.createBody(this.el.dom));
24968       this.el.addClass("x-tabs-top");
24969     }
24970     this.items = [];
24971
24972     this.bodyEl.setStyle("position", "relative");
24973
24974     this.active = null;
24975     this.activateDelegate = this.activate.createDelegate(this);
24976
24977     this.addEvents({
24978         /**
24979          * @event tabchange
24980          * Fires when the active tab changes
24981          * @param {Roo.TabPanel} this
24982          * @param {Roo.TabPanelItem} activePanel The new active tab
24983          */
24984         "tabchange": true,
24985         /**
24986          * @event beforetabchange
24987          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
24988          * @param {Roo.TabPanel} this
24989          * @param {Object} e Set cancel to true on this object to cancel the tab change
24990          * @param {Roo.TabPanelItem} tab The tab being changed to
24991          */
24992         "beforetabchange" : true
24993     });
24994
24995     Roo.EventManager.onWindowResize(this.onResize, this);
24996     this.cpad = this.el.getPadding("lr");
24997     this.hiddenCount = 0;
24998
24999     Roo.TabPanel.superclass.constructor.call(this);
25000 };
25001
25002 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25003         /*
25004          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25005          */
25006     tabPosition : "top",
25007         /*
25008          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25009          */
25010     currentTabWidth : 0,
25011         /*
25012          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25013          */
25014     minTabWidth : 40,
25015         /*
25016          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25017          */
25018     maxTabWidth : 250,
25019         /*
25020          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25021          */
25022     preferredTabWidth : 175,
25023         /*
25024          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25025          */
25026     resizeTabs : false,
25027         /*
25028          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25029          */
25030     monitorResize : true,
25031
25032     /**
25033      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25034      * @param {String} id The id of the div to use <b>or create</b>
25035      * @param {String} text The text for the tab
25036      * @param {String} content (optional) Content to put in the TabPanelItem body
25037      * @param {Boolean} closable (optional) True to create a close icon on the tab
25038      * @return {Roo.TabPanelItem} The created TabPanelItem
25039      */
25040     addTab : function(id, text, content, closable){
25041         var item = new Roo.TabPanelItem(this, id, text, closable);
25042         this.addTabItem(item);
25043         if(content){
25044             item.setContent(content);
25045         }
25046         return item;
25047     },
25048
25049     /**
25050      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25051      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25052      * @return {Roo.TabPanelItem}
25053      */
25054     getTab : function(id){
25055         return this.items[id];
25056     },
25057
25058     /**
25059      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25060      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25061      */
25062     hideTab : function(id){
25063         var t = this.items[id];
25064         if(!t.isHidden()){
25065            t.setHidden(true);
25066            this.hiddenCount++;
25067            this.autoSizeTabs();
25068         }
25069     },
25070
25071     /**
25072      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25073      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25074      */
25075     unhideTab : function(id){
25076         var t = this.items[id];
25077         if(t.isHidden()){
25078            t.setHidden(false);
25079            this.hiddenCount--;
25080            this.autoSizeTabs();
25081         }
25082     },
25083
25084     /**
25085      * Adds an existing {@link Roo.TabPanelItem}.
25086      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25087      */
25088     addTabItem : function(item){
25089         this.items[item.id] = item;
25090         this.items.push(item);
25091         if(this.resizeTabs){
25092            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25093            this.autoSizeTabs();
25094         }else{
25095             item.autoSize();
25096         }
25097     },
25098
25099     /**
25100      * Removes a {@link Roo.TabPanelItem}.
25101      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25102      */
25103     removeTab : function(id){
25104         var items = this.items;
25105         var tab = items[id];
25106         if(!tab) return;
25107         var index = items.indexOf(tab);
25108         if(this.active == tab && items.length > 1){
25109             var newTab = this.getNextAvailable(index);
25110             if(newTab)newTab.activate();
25111         }
25112         this.stripEl.dom.removeChild(tab.pnode.dom);
25113         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25114             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25115         }
25116         items.splice(index, 1);
25117         delete this.items[tab.id];
25118         tab.fireEvent("close", tab);
25119         tab.purgeListeners();
25120         this.autoSizeTabs();
25121     },
25122
25123     getNextAvailable : function(start){
25124         var items = this.items;
25125         var index = start;
25126         // look for a next tab that will slide over to
25127         // replace the one being removed
25128         while(index < items.length){
25129             var item = items[++index];
25130             if(item && !item.isHidden()){
25131                 return item;
25132             }
25133         }
25134         // if one isn't found select the previous tab (on the left)
25135         index = start;
25136         while(index >= 0){
25137             var item = items[--index];
25138             if(item && !item.isHidden()){
25139                 return item;
25140             }
25141         }
25142         return null;
25143     },
25144
25145     /**
25146      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25147      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25148      */
25149     disableTab : function(id){
25150         var tab = this.items[id];
25151         if(tab && this.active != tab){
25152             tab.disable();
25153         }
25154     },
25155
25156     /**
25157      * Enables a {@link Roo.TabPanelItem} that is disabled.
25158      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25159      */
25160     enableTab : function(id){
25161         var tab = this.items[id];
25162         tab.enable();
25163     },
25164
25165     /**
25166      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25167      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25168      * @return {Roo.TabPanelItem} The TabPanelItem.
25169      */
25170     activate : function(id){
25171         var tab = this.items[id];
25172         if(!tab){
25173             return null;
25174         }
25175         if(tab == this.active || tab.disabled){
25176             return tab;
25177         }
25178         var e = {};
25179         this.fireEvent("beforetabchange", this, e, tab);
25180         if(e.cancel !== true && !tab.disabled){
25181             if(this.active){
25182                 this.active.hide();
25183             }
25184             this.active = this.items[id];
25185             this.active.show();
25186             this.fireEvent("tabchange", this, this.active);
25187         }
25188         return tab;
25189     },
25190
25191     /**
25192      * Gets the active {@link Roo.TabPanelItem}.
25193      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25194      */
25195     getActiveTab : function(){
25196         return this.active;
25197     },
25198
25199     /**
25200      * Updates the tab body element to fit the height of the container element
25201      * for overflow scrolling
25202      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25203      */
25204     syncHeight : function(targetHeight){
25205         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25206         var bm = this.bodyEl.getMargins();
25207         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25208         this.bodyEl.setHeight(newHeight);
25209         return newHeight;
25210     },
25211
25212     onResize : function(){
25213         if(this.monitorResize){
25214             this.autoSizeTabs();
25215         }
25216     },
25217
25218     /**
25219      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25220      */
25221     beginUpdate : function(){
25222         this.updating = true;
25223     },
25224
25225     /**
25226      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25227      */
25228     endUpdate : function(){
25229         this.updating = false;
25230         this.autoSizeTabs();
25231     },
25232
25233     /**
25234      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25235      */
25236     autoSizeTabs : function(){
25237         var count = this.items.length;
25238         var vcount = count - this.hiddenCount;
25239         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25240         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25241         var availWidth = Math.floor(w / vcount);
25242         var b = this.stripBody;
25243         if(b.getWidth() > w){
25244             var tabs = this.items;
25245             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25246             if(availWidth < this.minTabWidth){
25247                 /*if(!this.sleft){    // incomplete scrolling code
25248                     this.createScrollButtons();
25249                 }
25250                 this.showScroll();
25251                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25252             }
25253         }else{
25254             if(this.currentTabWidth < this.preferredTabWidth){
25255                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25256             }
25257         }
25258     },
25259
25260     /**
25261      * Returns the number of tabs in this TabPanel.
25262      * @return {Number}
25263      */
25264      getCount : function(){
25265          return this.items.length;
25266      },
25267
25268     /**
25269      * Resizes all the tabs to the passed width
25270      * @param {Number} The new width
25271      */
25272     setTabWidth : function(width){
25273         this.currentTabWidth = width;
25274         for(var i = 0, len = this.items.length; i < len; i++) {
25275                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25276         }
25277     },
25278
25279     /**
25280      * Destroys this TabPanel
25281      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25282      */
25283     destroy : function(removeEl){
25284         Roo.EventManager.removeResizeListener(this.onResize, this);
25285         for(var i = 0, len = this.items.length; i < len; i++){
25286             this.items[i].purgeListeners();
25287         }
25288         if(removeEl === true){
25289             this.el.update("");
25290             this.el.remove();
25291         }
25292     }
25293 });
25294
25295 /**
25296  * @class Roo.TabPanelItem
25297  * @extends Roo.util.Observable
25298  * Represents an individual item (tab plus body) in a TabPanel.
25299  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25300  * @param {String} id The id of this TabPanelItem
25301  * @param {String} text The text for the tab of this TabPanelItem
25302  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25303  */
25304 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25305     /**
25306      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25307      * @type Roo.TabPanel
25308      */
25309     this.tabPanel = tabPanel;
25310     /**
25311      * The id for this TabPanelItem
25312      * @type String
25313      */
25314     this.id = id;
25315     /** @private */
25316     this.disabled = false;
25317     /** @private */
25318     this.text = text;
25319     /** @private */
25320     this.loaded = false;
25321     this.closable = closable;
25322
25323     /**
25324      * The body element for this TabPanelItem.
25325      * @type Roo.Element
25326      */
25327     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25328     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25329     this.bodyEl.setStyle("display", "block");
25330     this.bodyEl.setStyle("zoom", "1");
25331     this.hideAction();
25332
25333     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25334     /** @private */
25335     this.el = Roo.get(els.el, true);
25336     this.inner = Roo.get(els.inner, true);
25337     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25338     this.pnode = Roo.get(els.el.parentNode, true);
25339     this.el.on("mousedown", this.onTabMouseDown, this);
25340     this.el.on("click", this.onTabClick, this);
25341     /** @private */
25342     if(closable){
25343         var c = Roo.get(els.close, true);
25344         c.dom.title = this.closeText;
25345         c.addClassOnOver("close-over");
25346         c.on("click", this.closeClick, this);
25347      }
25348
25349     this.addEvents({
25350          /**
25351          * @event activate
25352          * Fires when this tab becomes the active tab.
25353          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25354          * @param {Roo.TabPanelItem} this
25355          */
25356         "activate": true,
25357         /**
25358          * @event beforeclose
25359          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25360          * @param {Roo.TabPanelItem} this
25361          * @param {Object} e Set cancel to true on this object to cancel the close.
25362          */
25363         "beforeclose": true,
25364         /**
25365          * @event close
25366          * Fires when this tab is closed.
25367          * @param {Roo.TabPanelItem} this
25368          */
25369          "close": true,
25370         /**
25371          * @event deactivate
25372          * Fires when this tab is no longer the active tab.
25373          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25374          * @param {Roo.TabPanelItem} this
25375          */
25376          "deactivate" : true
25377     });
25378     this.hidden = false;
25379
25380     Roo.TabPanelItem.superclass.constructor.call(this);
25381 };
25382
25383 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25384     purgeListeners : function(){
25385        Roo.util.Observable.prototype.purgeListeners.call(this);
25386        this.el.removeAllListeners();
25387     },
25388     /**
25389      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25390      */
25391     show : function(){
25392         this.pnode.addClass("on");
25393         this.showAction();
25394         if(Roo.isOpera){
25395             this.tabPanel.stripWrap.repaint();
25396         }
25397         this.fireEvent("activate", this.tabPanel, this);
25398     },
25399
25400     /**
25401      * Returns true if this tab is the active tab.
25402      * @return {Boolean}
25403      */
25404     isActive : function(){
25405         return this.tabPanel.getActiveTab() == this;
25406     },
25407
25408     /**
25409      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25410      */
25411     hide : function(){
25412         this.pnode.removeClass("on");
25413         this.hideAction();
25414         this.fireEvent("deactivate", this.tabPanel, this);
25415     },
25416
25417     hideAction : function(){
25418         this.bodyEl.hide();
25419         this.bodyEl.setStyle("position", "absolute");
25420         this.bodyEl.setLeft("-20000px");
25421         this.bodyEl.setTop("-20000px");
25422     },
25423
25424     showAction : function(){
25425         this.bodyEl.setStyle("position", "relative");
25426         this.bodyEl.setTop("");
25427         this.bodyEl.setLeft("");
25428         this.bodyEl.show();
25429     },
25430
25431     /**
25432      * Set the tooltip for the tab.
25433      * @param {String} tooltip The tab's tooltip
25434      */
25435     setTooltip : function(text){
25436         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25437             this.textEl.dom.qtip = text;
25438             this.textEl.dom.removeAttribute('title');
25439         }else{
25440             this.textEl.dom.title = text;
25441         }
25442     },
25443
25444     onTabClick : function(e){
25445         e.preventDefault();
25446         this.tabPanel.activate(this.id);
25447     },
25448
25449     onTabMouseDown : function(e){
25450         e.preventDefault();
25451         this.tabPanel.activate(this.id);
25452     },
25453
25454     getWidth : function(){
25455         return this.inner.getWidth();
25456     },
25457
25458     setWidth : function(width){
25459         var iwidth = width - this.pnode.getPadding("lr");
25460         this.inner.setWidth(iwidth);
25461         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25462         this.pnode.setWidth(width);
25463     },
25464
25465     /**
25466      * Show or hide the tab
25467      * @param {Boolean} hidden True to hide or false to show.
25468      */
25469     setHidden : function(hidden){
25470         this.hidden = hidden;
25471         this.pnode.setStyle("display", hidden ? "none" : "");
25472     },
25473
25474     /**
25475      * Returns true if this tab is "hidden"
25476      * @return {Boolean}
25477      */
25478     isHidden : function(){
25479         return this.hidden;
25480     },
25481
25482     /**
25483      * Returns the text for this tab
25484      * @return {String}
25485      */
25486     getText : function(){
25487         return this.text;
25488     },
25489
25490     autoSize : function(){
25491         //this.el.beginMeasure();
25492         this.textEl.setWidth(1);
25493         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25494         //this.el.endMeasure();
25495     },
25496
25497     /**
25498      * Sets the text for the tab (Note: this also sets the tooltip text)
25499      * @param {String} text The tab's text and tooltip
25500      */
25501     setText : function(text){
25502         this.text = text;
25503         this.textEl.update(text);
25504         this.setTooltip(text);
25505         if(!this.tabPanel.resizeTabs){
25506             this.autoSize();
25507         }
25508     },
25509     /**
25510      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25511      */
25512     activate : function(){
25513         this.tabPanel.activate(this.id);
25514     },
25515
25516     /**
25517      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25518      */
25519     disable : function(){
25520         if(this.tabPanel.active != this){
25521             this.disabled = true;
25522             this.pnode.addClass("disabled");
25523         }
25524     },
25525
25526     /**
25527      * Enables this TabPanelItem if it was previously disabled.
25528      */
25529     enable : function(){
25530         this.disabled = false;
25531         this.pnode.removeClass("disabled");
25532     },
25533
25534     /**
25535      * Sets the content for this TabPanelItem.
25536      * @param {String} content The content
25537      * @param {Boolean} loadScripts true to look for and load scripts
25538      */
25539     setContent : function(content, loadScripts){
25540         this.bodyEl.update(content, loadScripts);
25541     },
25542
25543     /**
25544      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25545      * @return {Roo.UpdateManager} The UpdateManager
25546      */
25547     getUpdateManager : function(){
25548         return this.bodyEl.getUpdateManager();
25549     },
25550
25551     /**
25552      * Set a URL to be used to load the content for this TabPanelItem.
25553      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25554      * @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)
25555      * @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)
25556      * @return {Roo.UpdateManager} The UpdateManager
25557      */
25558     setUrl : function(url, params, loadOnce){
25559         if(this.refreshDelegate){
25560             this.un('activate', this.refreshDelegate);
25561         }
25562         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25563         this.on("activate", this.refreshDelegate);
25564         return this.bodyEl.getUpdateManager();
25565     },
25566
25567     /** @private */
25568     _handleRefresh : function(url, params, loadOnce){
25569         if(!loadOnce || !this.loaded){
25570             var updater = this.bodyEl.getUpdateManager();
25571             updater.update(url, params, this._setLoaded.createDelegate(this));
25572         }
25573     },
25574
25575     /**
25576      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25577      *   Will fail silently if the setUrl method has not been called.
25578      *   This does not activate the panel, just updates its content.
25579      */
25580     refresh : function(){
25581         if(this.refreshDelegate){
25582            this.loaded = false;
25583            this.refreshDelegate();
25584         }
25585     },
25586
25587     /** @private */
25588     _setLoaded : function(){
25589         this.loaded = true;
25590     },
25591
25592     /** @private */
25593     closeClick : function(e){
25594         var o = {};
25595         e.stopEvent();
25596         this.fireEvent("beforeclose", this, o);
25597         if(o.cancel !== true){
25598             this.tabPanel.removeTab(this.id);
25599         }
25600     },
25601     /**
25602      * The text displayed in the tooltip for the close icon.
25603      * @type String
25604      */
25605     closeText : "Close this tab"
25606 });
25607
25608 /** @private */
25609 Roo.TabPanel.prototype.createStrip = function(container){
25610     var strip = document.createElement("div");
25611     strip.className = "x-tabs-wrap";
25612     container.appendChild(strip);
25613     return strip;
25614 };
25615 /** @private */
25616 Roo.TabPanel.prototype.createStripList = function(strip){
25617     // div wrapper for retard IE
25618     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>';
25619     return strip.firstChild.firstChild.firstChild.firstChild;
25620 };
25621 /** @private */
25622 Roo.TabPanel.prototype.createBody = function(container){
25623     var body = document.createElement("div");
25624     Roo.id(body, "tab-body");
25625     Roo.fly(body).addClass("x-tabs-body");
25626     container.appendChild(body);
25627     return body;
25628 };
25629 /** @private */
25630 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25631     var body = Roo.getDom(id);
25632     if(!body){
25633         body = document.createElement("div");
25634         body.id = id;
25635     }
25636     Roo.fly(body).addClass("x-tabs-item-body");
25637     bodyEl.insertBefore(body, bodyEl.firstChild);
25638     return body;
25639 };
25640 /** @private */
25641 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25642     var td = document.createElement("td");
25643     stripEl.appendChild(td);
25644     if(closable){
25645         td.className = "x-tabs-closable";
25646         if(!this.closeTpl){
25647             this.closeTpl = new Roo.Template(
25648                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25649                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25650                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25651             );
25652         }
25653         var el = this.closeTpl.overwrite(td, {"text": text});
25654         var close = el.getElementsByTagName("div")[0];
25655         var inner = el.getElementsByTagName("em")[0];
25656         return {"el": el, "close": close, "inner": inner};
25657     } else {
25658         if(!this.tabTpl){
25659             this.tabTpl = new Roo.Template(
25660                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25661                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25662             );
25663         }
25664         var el = this.tabTpl.overwrite(td, {"text": text});
25665         var inner = el.getElementsByTagName("em")[0];
25666         return {"el": el, "inner": inner};
25667     }
25668 };/*
25669  * Based on:
25670  * Ext JS Library 1.1.1
25671  * Copyright(c) 2006-2007, Ext JS, LLC.
25672  *
25673  * Originally Released Under LGPL - original licence link has changed is not relivant.
25674  *
25675  * Fork - LGPL
25676  * <script type="text/javascript">
25677  */
25678
25679 /**
25680  * @class Roo.Button
25681  * @extends Roo.util.Observable
25682  * Simple Button class
25683  * @cfg {String} text The button text
25684  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25685  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25686  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25687  * @cfg {Object} scope The scope of the handler
25688  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25689  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25690  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25691  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25692  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25693  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25694    applies if enableToggle = true)
25695  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25696  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25697   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25698  * @constructor
25699  * Create a new button
25700  * @param {Object} config The config object
25701  */
25702 Roo.Button = function(renderTo, config)
25703 {
25704     if (!config) {
25705         config = renderTo;
25706         renderTo = config.renderTo || false;
25707     }
25708     
25709     Roo.apply(this, config);
25710     this.addEvents({
25711         /**
25712              * @event click
25713              * Fires when this button is clicked
25714              * @param {Button} this
25715              * @param {EventObject} e The click event
25716              */
25717             "click" : true,
25718         /**
25719              * @event toggle
25720              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25721              * @param {Button} this
25722              * @param {Boolean} pressed
25723              */
25724             "toggle" : true,
25725         /**
25726              * @event mouseover
25727              * Fires when the mouse hovers over the button
25728              * @param {Button} this
25729              * @param {Event} e The event object
25730              */
25731         'mouseover' : true,
25732         /**
25733              * @event mouseout
25734              * Fires when the mouse exits the button
25735              * @param {Button} this
25736              * @param {Event} e The event object
25737              */
25738         'mouseout': true,
25739          /**
25740              * @event render
25741              * Fires when the button is rendered
25742              * @param {Button} this
25743              */
25744         'render': true
25745     });
25746     if(this.menu){
25747         this.menu = Roo.menu.MenuMgr.get(this.menu);
25748     }
25749     if(renderTo){
25750         this.render(renderTo);
25751     }
25752     
25753     Roo.util.Observable.call(this);
25754 };
25755
25756 Roo.extend(Roo.Button, Roo.util.Observable, {
25757     /**
25758      * 
25759      */
25760     
25761     /**
25762      * Read-only. True if this button is hidden
25763      * @type Boolean
25764      */
25765     hidden : false,
25766     /**
25767      * Read-only. True if this button is disabled
25768      * @type Boolean
25769      */
25770     disabled : false,
25771     /**
25772      * Read-only. True if this button is pressed (only if enableToggle = true)
25773      * @type Boolean
25774      */
25775     pressed : false,
25776
25777     /**
25778      * @cfg {Number} tabIndex 
25779      * The DOM tabIndex for this button (defaults to undefined)
25780      */
25781     tabIndex : undefined,
25782
25783     /**
25784      * @cfg {Boolean} enableToggle
25785      * True to enable pressed/not pressed toggling (defaults to false)
25786      */
25787     enableToggle: false,
25788     /**
25789      * @cfg {Mixed} menu
25790      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25791      */
25792     menu : undefined,
25793     /**
25794      * @cfg {String} menuAlign
25795      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25796      */
25797     menuAlign : "tl-bl?",
25798
25799     /**
25800      * @cfg {String} iconCls
25801      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25802      */
25803     iconCls : undefined,
25804     /**
25805      * @cfg {String} type
25806      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25807      */
25808     type : 'button',
25809
25810     // private
25811     menuClassTarget: 'tr',
25812
25813     /**
25814      * @cfg {String} clickEvent
25815      * The type of event to map to the button's event handler (defaults to 'click')
25816      */
25817     clickEvent : 'click',
25818
25819     /**
25820      * @cfg {Boolean} handleMouseEvents
25821      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25822      */
25823     handleMouseEvents : true,
25824
25825     /**
25826      * @cfg {String} tooltipType
25827      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
25828      */
25829     tooltipType : 'qtip',
25830
25831     /**
25832      * @cfg {String} cls
25833      * A CSS class to apply to the button's main element.
25834      */
25835     
25836     /**
25837      * @cfg {Roo.Template} template (Optional)
25838      * An {@link Roo.Template} with which to create the Button's main element. This Template must
25839      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
25840      * require code modifications if required elements (e.g. a button) aren't present.
25841      */
25842
25843     // private
25844     render : function(renderTo){
25845         var btn;
25846         if(this.hideParent){
25847             this.parentEl = Roo.get(renderTo);
25848         }
25849         if(!this.dhconfig){
25850             if(!this.template){
25851                 if(!Roo.Button.buttonTemplate){
25852                     // hideous table template
25853                     Roo.Button.buttonTemplate = new Roo.Template(
25854                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
25855                         '<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>',
25856                         "</tr></tbody></table>");
25857                 }
25858                 this.template = Roo.Button.buttonTemplate;
25859             }
25860             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
25861             var btnEl = btn.child("button:first");
25862             btnEl.on('focus', this.onFocus, this);
25863             btnEl.on('blur', this.onBlur, this);
25864             if(this.cls){
25865                 btn.addClass(this.cls);
25866             }
25867             if(this.icon){
25868                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
25869             }
25870             if(this.iconCls){
25871                 btnEl.addClass(this.iconCls);
25872                 if(!this.cls){
25873                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
25874                 }
25875             }
25876             if(this.tabIndex !== undefined){
25877                 btnEl.dom.tabIndex = this.tabIndex;
25878             }
25879             if(this.tooltip){
25880                 if(typeof this.tooltip == 'object'){
25881                     Roo.QuickTips.tips(Roo.apply({
25882                           target: btnEl.id
25883                     }, this.tooltip));
25884                 } else {
25885                     btnEl.dom[this.tooltipType] = this.tooltip;
25886                 }
25887             }
25888         }else{
25889             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
25890         }
25891         this.el = btn;
25892         if(this.id){
25893             this.el.dom.id = this.el.id = this.id;
25894         }
25895         if(this.menu){
25896             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
25897             this.menu.on("show", this.onMenuShow, this);
25898             this.menu.on("hide", this.onMenuHide, this);
25899         }
25900         btn.addClass("x-btn");
25901         if(Roo.isIE && !Roo.isIE7){
25902             this.autoWidth.defer(1, this);
25903         }else{
25904             this.autoWidth();
25905         }
25906         if(this.handleMouseEvents){
25907             btn.on("mouseover", this.onMouseOver, this);
25908             btn.on("mouseout", this.onMouseOut, this);
25909             btn.on("mousedown", this.onMouseDown, this);
25910         }
25911         btn.on(this.clickEvent, this.onClick, this);
25912         //btn.on("mouseup", this.onMouseUp, this);
25913         if(this.hidden){
25914             this.hide();
25915         }
25916         if(this.disabled){
25917             this.disable();
25918         }
25919         Roo.ButtonToggleMgr.register(this);
25920         if(this.pressed){
25921             this.el.addClass("x-btn-pressed");
25922         }
25923         if(this.repeat){
25924             var repeater = new Roo.util.ClickRepeater(btn,
25925                 typeof this.repeat == "object" ? this.repeat : {}
25926             );
25927             repeater.on("click", this.onClick,  this);
25928         }
25929         this.fireEvent('render', this);
25930         
25931     },
25932     /**
25933      * Returns the button's underlying element
25934      * @return {Roo.Element} The element
25935      */
25936     getEl : function(){
25937         return this.el;  
25938     },
25939     
25940     /**
25941      * Destroys this Button and removes any listeners.
25942      */
25943     destroy : function(){
25944         Roo.ButtonToggleMgr.unregister(this);
25945         this.el.removeAllListeners();
25946         this.purgeListeners();
25947         this.el.remove();
25948     },
25949
25950     // private
25951     autoWidth : function(){
25952         if(this.el){
25953             this.el.setWidth("auto");
25954             if(Roo.isIE7 && Roo.isStrict){
25955                 var ib = this.el.child('button');
25956                 if(ib && ib.getWidth() > 20){
25957                     ib.clip();
25958                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
25959                 }
25960             }
25961             if(this.minWidth){
25962                 if(this.hidden){
25963                     this.el.beginMeasure();
25964                 }
25965                 if(this.el.getWidth() < this.minWidth){
25966                     this.el.setWidth(this.minWidth);
25967                 }
25968                 if(this.hidden){
25969                     this.el.endMeasure();
25970                 }
25971             }
25972         }
25973     },
25974
25975     /**
25976      * Assigns this button's click handler
25977      * @param {Function} handler The function to call when the button is clicked
25978      * @param {Object} scope (optional) Scope for the function passed in
25979      */
25980     setHandler : function(handler, scope){
25981         this.handler = handler;
25982         this.scope = scope;  
25983     },
25984     
25985     /**
25986      * Sets this button's text
25987      * @param {String} text The button text
25988      */
25989     setText : function(text){
25990         this.text = text;
25991         if(this.el){
25992             this.el.child("td.x-btn-center button.x-btn-text").update(text);
25993         }
25994         this.autoWidth();
25995     },
25996     
25997     /**
25998      * Gets the text for this button
25999      * @return {String} The button text
26000      */
26001     getText : function(){
26002         return this.text;  
26003     },
26004     
26005     /**
26006      * Show this button
26007      */
26008     show: function(){
26009         this.hidden = false;
26010         if(this.el){
26011             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26012         }
26013     },
26014     
26015     /**
26016      * Hide this button
26017      */
26018     hide: function(){
26019         this.hidden = true;
26020         if(this.el){
26021             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26022         }
26023     },
26024     
26025     /**
26026      * Convenience function for boolean show/hide
26027      * @param {Boolean} visible True to show, false to hide
26028      */
26029     setVisible: function(visible){
26030         if(visible) {
26031             this.show();
26032         }else{
26033             this.hide();
26034         }
26035     },
26036     
26037     /**
26038      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26039      * @param {Boolean} state (optional) Force a particular state
26040      */
26041     toggle : function(state){
26042         state = state === undefined ? !this.pressed : state;
26043         if(state != this.pressed){
26044             if(state){
26045                 this.el.addClass("x-btn-pressed");
26046                 this.pressed = true;
26047                 this.fireEvent("toggle", this, true);
26048             }else{
26049                 this.el.removeClass("x-btn-pressed");
26050                 this.pressed = false;
26051                 this.fireEvent("toggle", this, false);
26052             }
26053             if(this.toggleHandler){
26054                 this.toggleHandler.call(this.scope || this, this, state);
26055             }
26056         }
26057     },
26058     
26059     /**
26060      * Focus the button
26061      */
26062     focus : function(){
26063         this.el.child('button:first').focus();
26064     },
26065     
26066     /**
26067      * Disable this button
26068      */
26069     disable : function(){
26070         if(this.el){
26071             this.el.addClass("x-btn-disabled");
26072         }
26073         this.disabled = true;
26074     },
26075     
26076     /**
26077      * Enable this button
26078      */
26079     enable : function(){
26080         if(this.el){
26081             this.el.removeClass("x-btn-disabled");
26082         }
26083         this.disabled = false;
26084     },
26085
26086     /**
26087      * Convenience function for boolean enable/disable
26088      * @param {Boolean} enabled True to enable, false to disable
26089      */
26090     setDisabled : function(v){
26091         this[v !== true ? "enable" : "disable"]();
26092     },
26093
26094     // private
26095     onClick : function(e){
26096         if(e){
26097             e.preventDefault();
26098         }
26099         if(e.button != 0){
26100             return;
26101         }
26102         if(!this.disabled){
26103             if(this.enableToggle){
26104                 this.toggle();
26105             }
26106             if(this.menu && !this.menu.isVisible()){
26107                 this.menu.show(this.el, this.menuAlign);
26108             }
26109             this.fireEvent("click", this, e);
26110             if(this.handler){
26111                 this.el.removeClass("x-btn-over");
26112                 this.handler.call(this.scope || this, this, e);
26113             }
26114         }
26115     },
26116     // private
26117     onMouseOver : function(e){
26118         if(!this.disabled){
26119             this.el.addClass("x-btn-over");
26120             this.fireEvent('mouseover', this, e);
26121         }
26122     },
26123     // private
26124     onMouseOut : function(e){
26125         if(!e.within(this.el,  true)){
26126             this.el.removeClass("x-btn-over");
26127             this.fireEvent('mouseout', this, e);
26128         }
26129     },
26130     // private
26131     onFocus : function(e){
26132         if(!this.disabled){
26133             this.el.addClass("x-btn-focus");
26134         }
26135     },
26136     // private
26137     onBlur : function(e){
26138         this.el.removeClass("x-btn-focus");
26139     },
26140     // private
26141     onMouseDown : function(e){
26142         if(!this.disabled && e.button == 0){
26143             this.el.addClass("x-btn-click");
26144             Roo.get(document).on('mouseup', this.onMouseUp, this);
26145         }
26146     },
26147     // private
26148     onMouseUp : function(e){
26149         if(e.button == 0){
26150             this.el.removeClass("x-btn-click");
26151             Roo.get(document).un('mouseup', this.onMouseUp, this);
26152         }
26153     },
26154     // private
26155     onMenuShow : function(e){
26156         this.el.addClass("x-btn-menu-active");
26157     },
26158     // private
26159     onMenuHide : function(e){
26160         this.el.removeClass("x-btn-menu-active");
26161     }   
26162 });
26163
26164 // Private utility class used by Button
26165 Roo.ButtonToggleMgr = function(){
26166    var groups = {};
26167    
26168    function toggleGroup(btn, state){
26169        if(state){
26170            var g = groups[btn.toggleGroup];
26171            for(var i = 0, l = g.length; i < l; i++){
26172                if(g[i] != btn){
26173                    g[i].toggle(false);
26174                }
26175            }
26176        }
26177    }
26178    
26179    return {
26180        register : function(btn){
26181            if(!btn.toggleGroup){
26182                return;
26183            }
26184            var g = groups[btn.toggleGroup];
26185            if(!g){
26186                g = groups[btn.toggleGroup] = [];
26187            }
26188            g.push(btn);
26189            btn.on("toggle", toggleGroup);
26190        },
26191        
26192        unregister : function(btn){
26193            if(!btn.toggleGroup){
26194                return;
26195            }
26196            var g = groups[btn.toggleGroup];
26197            if(g){
26198                g.remove(btn);
26199                btn.un("toggle", toggleGroup);
26200            }
26201        }
26202    };
26203 }();/*
26204  * Based on:
26205  * Ext JS Library 1.1.1
26206  * Copyright(c) 2006-2007, Ext JS, LLC.
26207  *
26208  * Originally Released Under LGPL - original licence link has changed is not relivant.
26209  *
26210  * Fork - LGPL
26211  * <script type="text/javascript">
26212  */
26213  
26214 /**
26215  * @class Roo.SplitButton
26216  * @extends Roo.Button
26217  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26218  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26219  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26220  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26221  * @cfg {String} arrowTooltip The title attribute of the arrow
26222  * @constructor
26223  * Create a new menu button
26224  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26225  * @param {Object} config The config object
26226  */
26227 Roo.SplitButton = function(renderTo, config){
26228     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26229     /**
26230      * @event arrowclick
26231      * Fires when this button's arrow is clicked
26232      * @param {SplitButton} this
26233      * @param {EventObject} e The click event
26234      */
26235     this.addEvents({"arrowclick":true});
26236 };
26237
26238 Roo.extend(Roo.SplitButton, Roo.Button, {
26239     render : function(renderTo){
26240         // this is one sweet looking template!
26241         var tpl = new Roo.Template(
26242             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26243             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26244             '<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>',
26245             "</tbody></table></td><td>",
26246             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26247             '<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>',
26248             "</tbody></table></td></tr></table>"
26249         );
26250         var btn = tpl.append(renderTo, [this.text, this.type], true);
26251         var btnEl = btn.child("button");
26252         if(this.cls){
26253             btn.addClass(this.cls);
26254         }
26255         if(this.icon){
26256             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26257         }
26258         if(this.iconCls){
26259             btnEl.addClass(this.iconCls);
26260             if(!this.cls){
26261                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26262             }
26263         }
26264         this.el = btn;
26265         if(this.handleMouseEvents){
26266             btn.on("mouseover", this.onMouseOver, this);
26267             btn.on("mouseout", this.onMouseOut, this);
26268             btn.on("mousedown", this.onMouseDown, this);
26269             btn.on("mouseup", this.onMouseUp, this);
26270         }
26271         btn.on(this.clickEvent, this.onClick, this);
26272         if(this.tooltip){
26273             if(typeof this.tooltip == 'object'){
26274                 Roo.QuickTips.tips(Roo.apply({
26275                       target: btnEl.id
26276                 }, this.tooltip));
26277             } else {
26278                 btnEl.dom[this.tooltipType] = this.tooltip;
26279             }
26280         }
26281         if(this.arrowTooltip){
26282             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26283         }
26284         if(this.hidden){
26285             this.hide();
26286         }
26287         if(this.disabled){
26288             this.disable();
26289         }
26290         if(this.pressed){
26291             this.el.addClass("x-btn-pressed");
26292         }
26293         if(Roo.isIE && !Roo.isIE7){
26294             this.autoWidth.defer(1, this);
26295         }else{
26296             this.autoWidth();
26297         }
26298         if(this.menu){
26299             this.menu.on("show", this.onMenuShow, this);
26300             this.menu.on("hide", this.onMenuHide, this);
26301         }
26302         this.fireEvent('render', this);
26303     },
26304
26305     // private
26306     autoWidth : function(){
26307         if(this.el){
26308             var tbl = this.el.child("table:first");
26309             var tbl2 = this.el.child("table:last");
26310             this.el.setWidth("auto");
26311             tbl.setWidth("auto");
26312             if(Roo.isIE7 && Roo.isStrict){
26313                 var ib = this.el.child('button:first');
26314                 if(ib && ib.getWidth() > 20){
26315                     ib.clip();
26316                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26317                 }
26318             }
26319             if(this.minWidth){
26320                 if(this.hidden){
26321                     this.el.beginMeasure();
26322                 }
26323                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26324                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26325                 }
26326                 if(this.hidden){
26327                     this.el.endMeasure();
26328                 }
26329             }
26330             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26331         } 
26332     },
26333     /**
26334      * Sets this button's click handler
26335      * @param {Function} handler The function to call when the button is clicked
26336      * @param {Object} scope (optional) Scope for the function passed above
26337      */
26338     setHandler : function(handler, scope){
26339         this.handler = handler;
26340         this.scope = scope;  
26341     },
26342     
26343     /**
26344      * Sets this button's arrow click handler
26345      * @param {Function} handler The function to call when the arrow is clicked
26346      * @param {Object} scope (optional) Scope for the function passed above
26347      */
26348     setArrowHandler : function(handler, scope){
26349         this.arrowHandler = handler;
26350         this.scope = scope;  
26351     },
26352     
26353     /**
26354      * Focus the button
26355      */
26356     focus : function(){
26357         if(this.el){
26358             this.el.child("button:first").focus();
26359         }
26360     },
26361
26362     // private
26363     onClick : function(e){
26364         e.preventDefault();
26365         if(!this.disabled){
26366             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26367                 if(this.menu && !this.menu.isVisible()){
26368                     this.menu.show(this.el, this.menuAlign);
26369                 }
26370                 this.fireEvent("arrowclick", this, e);
26371                 if(this.arrowHandler){
26372                     this.arrowHandler.call(this.scope || this, this, e);
26373                 }
26374             }else{
26375                 this.fireEvent("click", this, e);
26376                 if(this.handler){
26377                     this.handler.call(this.scope || this, this, e);
26378                 }
26379             }
26380         }
26381     },
26382     // private
26383     onMouseDown : function(e){
26384         if(!this.disabled){
26385             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26386         }
26387     },
26388     // private
26389     onMouseUp : function(e){
26390         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26391     }   
26392 });
26393
26394
26395 // backwards compat
26396 Roo.MenuButton = Roo.SplitButton;/*
26397  * Based on:
26398  * Ext JS Library 1.1.1
26399  * Copyright(c) 2006-2007, Ext JS, LLC.
26400  *
26401  * Originally Released Under LGPL - original licence link has changed is not relivant.
26402  *
26403  * Fork - LGPL
26404  * <script type="text/javascript">
26405  */
26406
26407 /**
26408  * @class Roo.Toolbar
26409  * Basic Toolbar class.
26410  * @constructor
26411  * Creates a new Toolbar
26412  * @param {Object} config The config object
26413  */ 
26414 Roo.Toolbar = function(container, buttons, config)
26415 {
26416     /// old consturctor format still supported..
26417     if(container instanceof Array){ // omit the container for later rendering
26418         buttons = container;
26419         config = buttons;
26420         container = null;
26421     }
26422     if (typeof(container) == 'object' && container.xtype) {
26423         config = container;
26424         container = config.container;
26425         buttons = config.buttons; // not really - use items!!
26426     }
26427     var xitems = [];
26428     if (config && config.items) {
26429         xitems = config.items;
26430         delete config.items;
26431     }
26432     Roo.apply(this, config);
26433     this.buttons = buttons;
26434     
26435     if(container){
26436         this.render(container);
26437     }
26438     Roo.each(xitems, function(b) {
26439         this.add(b);
26440     }, this);
26441     
26442 };
26443
26444 Roo.Toolbar.prototype = {
26445     /**
26446      * @cfg {Roo.data.Store} items
26447      * array of button configs or elements to add
26448      */
26449     
26450     /**
26451      * @cfg {String/HTMLElement/Element} container
26452      * The id or element that will contain the toolbar
26453      */
26454     // private
26455     render : function(ct){
26456         this.el = Roo.get(ct);
26457         if(this.cls){
26458             this.el.addClass(this.cls);
26459         }
26460         // using a table allows for vertical alignment
26461         // 100% width is needed by Safari...
26462         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26463         this.tr = this.el.child("tr", true);
26464         var autoId = 0;
26465         this.items = new Roo.util.MixedCollection(false, function(o){
26466             return o.id || ("item" + (++autoId));
26467         });
26468         if(this.buttons){
26469             this.add.apply(this, this.buttons);
26470             delete this.buttons;
26471         }
26472     },
26473
26474     /**
26475      * Adds element(s) to the toolbar -- this function takes a variable number of 
26476      * arguments of mixed type and adds them to the toolbar.
26477      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26478      * <ul>
26479      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26480      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26481      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26482      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26483      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26484      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26485      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26486      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26487      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26488      * </ul>
26489      * @param {Mixed} arg2
26490      * @param {Mixed} etc.
26491      */
26492     add : function(){
26493         var a = arguments, l = a.length;
26494         for(var i = 0; i < l; i++){
26495             this._add(a[i]);
26496         }
26497     },
26498     // private..
26499     _add : function(el) {
26500         
26501         if (el.xtype) {
26502             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26503         }
26504         
26505         if (el.applyTo){ // some kind of form field
26506             return this.addField(el);
26507         } 
26508         if (el.render){ // some kind of Toolbar.Item
26509             return this.addItem(el);
26510         }
26511         if (typeof el == "string"){ // string
26512             if(el == "separator" || el == "-"){
26513                 return this.addSeparator();
26514             }
26515             if (el == " "){
26516                 return this.addSpacer();
26517             }
26518             if(el == "->"){
26519                 return this.addFill();
26520             }
26521             return this.addText(el);
26522             
26523         }
26524         if(el.tagName){ // element
26525             return this.addElement(el);
26526         }
26527         if(typeof el == "object"){ // must be button config?
26528             return this.addButton(el);
26529         }
26530         // and now what?!?!
26531         return false;
26532         
26533     },
26534     
26535     /**
26536      * Add an Xtype element
26537      * @param {Object} xtype Xtype Object
26538      * @return {Object} created Object
26539      */
26540     addxtype : function(e){
26541         return this.add(e);  
26542     },
26543     
26544     /**
26545      * Returns the Element for this toolbar.
26546      * @return {Roo.Element}
26547      */
26548     getEl : function(){
26549         return this.el;  
26550     },
26551     
26552     /**
26553      * Adds a separator
26554      * @return {Roo.Toolbar.Item} The separator item
26555      */
26556     addSeparator : function(){
26557         return this.addItem(new Roo.Toolbar.Separator());
26558     },
26559
26560     /**
26561      * Adds a spacer element
26562      * @return {Roo.Toolbar.Spacer} The spacer item
26563      */
26564     addSpacer : function(){
26565         return this.addItem(new Roo.Toolbar.Spacer());
26566     },
26567
26568     /**
26569      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26570      * @return {Roo.Toolbar.Fill} The fill item
26571      */
26572     addFill : function(){
26573         return this.addItem(new Roo.Toolbar.Fill());
26574     },
26575
26576     /**
26577      * Adds any standard HTML element to the toolbar
26578      * @param {String/HTMLElement/Element} el The element or id of the element to add
26579      * @return {Roo.Toolbar.Item} The element's item
26580      */
26581     addElement : function(el){
26582         return this.addItem(new Roo.Toolbar.Item(el));
26583     },
26584     /**
26585      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26586      * @type Roo.util.MixedCollection  
26587      */
26588     items : false,
26589      
26590     /**
26591      * Adds any Toolbar.Item or subclass
26592      * @param {Roo.Toolbar.Item} item
26593      * @return {Roo.Toolbar.Item} The item
26594      */
26595     addItem : function(item){
26596         var td = this.nextBlock();
26597         item.render(td);
26598         this.items.add(item);
26599         return item;
26600     },
26601     
26602     /**
26603      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26604      * @param {Object/Array} config A button config or array of configs
26605      * @return {Roo.Toolbar.Button/Array}
26606      */
26607     addButton : function(config){
26608         if(config instanceof Array){
26609             var buttons = [];
26610             for(var i = 0, len = config.length; i < len; i++) {
26611                 buttons.push(this.addButton(config[i]));
26612             }
26613             return buttons;
26614         }
26615         var b = config;
26616         if(!(config instanceof Roo.Toolbar.Button)){
26617             b = config.split ?
26618                 new Roo.Toolbar.SplitButton(config) :
26619                 new Roo.Toolbar.Button(config);
26620         }
26621         var td = this.nextBlock();
26622         b.render(td);
26623         this.items.add(b);
26624         return b;
26625     },
26626     
26627     /**
26628      * Adds text to the toolbar
26629      * @param {String} text The text to add
26630      * @return {Roo.Toolbar.Item} The element's item
26631      */
26632     addText : function(text){
26633         return this.addItem(new Roo.Toolbar.TextItem(text));
26634     },
26635     
26636     /**
26637      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26638      * @param {Number} index The index where the item is to be inserted
26639      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26640      * @return {Roo.Toolbar.Button/Item}
26641      */
26642     insertButton : function(index, item){
26643         if(item instanceof Array){
26644             var buttons = [];
26645             for(var i = 0, len = item.length; i < len; i++) {
26646                buttons.push(this.insertButton(index + i, item[i]));
26647             }
26648             return buttons;
26649         }
26650         if (!(item instanceof Roo.Toolbar.Button)){
26651            item = new Roo.Toolbar.Button(item);
26652         }
26653         var td = document.createElement("td");
26654         this.tr.insertBefore(td, this.tr.childNodes[index]);
26655         item.render(td);
26656         this.items.insert(index, item);
26657         return item;
26658     },
26659     
26660     /**
26661      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26662      * @param {Object} config
26663      * @return {Roo.Toolbar.Item} The element's item
26664      */
26665     addDom : function(config, returnEl){
26666         var td = this.nextBlock();
26667         Roo.DomHelper.overwrite(td, config);
26668         var ti = new Roo.Toolbar.Item(td.firstChild);
26669         ti.render(td);
26670         this.items.add(ti);
26671         return ti;
26672     },
26673
26674     /**
26675      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26676      * @type Roo.util.MixedCollection  
26677      */
26678     fields : false,
26679     
26680     /**
26681      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26682      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26683      * @param {Roo.form.Field} field
26684      * @return {Roo.ToolbarItem}
26685      */
26686      
26687       
26688     addField : function(field) {
26689         if (!this.fields) {
26690             var autoId = 0;
26691             this.fields = new Roo.util.MixedCollection(false, function(o){
26692                 return o.id || ("item" + (++autoId));
26693             });
26694
26695         }
26696         
26697         var td = this.nextBlock();
26698         field.render(td);
26699         var ti = new Roo.Toolbar.Item(td.firstChild);
26700         ti.render(td);
26701         this.items.add(ti);
26702         this.fields.add(field);
26703         return ti;
26704     },
26705     /**
26706      * Hide the toolbar
26707      * @method hide
26708      */
26709      
26710       
26711     hide : function()
26712     {
26713         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26714         this.el.child('div').hide();
26715     },
26716     /**
26717      * Show the toolbar
26718      * @method show
26719      */
26720     show : function()
26721     {
26722         this.el.child('div').show();
26723     },
26724       
26725     // private
26726     nextBlock : function(){
26727         var td = document.createElement("td");
26728         this.tr.appendChild(td);
26729         return td;
26730     },
26731
26732     // private
26733     destroy : function(){
26734         if(this.items){ // rendered?
26735             Roo.destroy.apply(Roo, this.items.items);
26736         }
26737         if(this.fields){ // rendered?
26738             Roo.destroy.apply(Roo, this.fields.items);
26739         }
26740         Roo.Element.uncache(this.el, this.tr);
26741     }
26742 };
26743
26744 /**
26745  * @class Roo.Toolbar.Item
26746  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26747  * @constructor
26748  * Creates a new Item
26749  * @param {HTMLElement} el 
26750  */
26751 Roo.Toolbar.Item = function(el){
26752     this.el = Roo.getDom(el);
26753     this.id = Roo.id(this.el);
26754     this.hidden = false;
26755 };
26756
26757 Roo.Toolbar.Item.prototype = {
26758     
26759     /**
26760      * Get this item's HTML Element
26761      * @return {HTMLElement}
26762      */
26763     getEl : function(){
26764        return this.el;  
26765     },
26766
26767     // private
26768     render : function(td){
26769         this.td = td;
26770         td.appendChild(this.el);
26771     },
26772     
26773     /**
26774      * Removes and destroys this item.
26775      */
26776     destroy : function(){
26777         this.td.parentNode.removeChild(this.td);
26778     },
26779     
26780     /**
26781      * Shows this item.
26782      */
26783     show: function(){
26784         this.hidden = false;
26785         this.td.style.display = "";
26786     },
26787     
26788     /**
26789      * Hides this item.
26790      */
26791     hide: function(){
26792         this.hidden = true;
26793         this.td.style.display = "none";
26794     },
26795     
26796     /**
26797      * Convenience function for boolean show/hide.
26798      * @param {Boolean} visible true to show/false to hide
26799      */
26800     setVisible: function(visible){
26801         if(visible) {
26802             this.show();
26803         }else{
26804             this.hide();
26805         }
26806     },
26807     
26808     /**
26809      * Try to focus this item.
26810      */
26811     focus : function(){
26812         Roo.fly(this.el).focus();
26813     },
26814     
26815     /**
26816      * Disables this item.
26817      */
26818     disable : function(){
26819         Roo.fly(this.td).addClass("x-item-disabled");
26820         this.disabled = true;
26821         this.el.disabled = true;
26822     },
26823     
26824     /**
26825      * Enables this item.
26826      */
26827     enable : function(){
26828         Roo.fly(this.td).removeClass("x-item-disabled");
26829         this.disabled = false;
26830         this.el.disabled = false;
26831     }
26832 };
26833
26834
26835 /**
26836  * @class Roo.Toolbar.Separator
26837  * @extends Roo.Toolbar.Item
26838  * A simple toolbar separator class
26839  * @constructor
26840  * Creates a new Separator
26841  */
26842 Roo.Toolbar.Separator = function(){
26843     var s = document.createElement("span");
26844     s.className = "ytb-sep";
26845     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
26846 };
26847 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
26848     enable:Roo.emptyFn,
26849     disable:Roo.emptyFn,
26850     focus:Roo.emptyFn
26851 });
26852
26853 /**
26854  * @class Roo.Toolbar.Spacer
26855  * @extends Roo.Toolbar.Item
26856  * A simple element that adds extra horizontal space to a toolbar.
26857  * @constructor
26858  * Creates a new Spacer
26859  */
26860 Roo.Toolbar.Spacer = function(){
26861     var s = document.createElement("div");
26862     s.className = "ytb-spacer";
26863     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
26864 };
26865 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
26866     enable:Roo.emptyFn,
26867     disable:Roo.emptyFn,
26868     focus:Roo.emptyFn
26869 });
26870
26871 /**
26872  * @class Roo.Toolbar.Fill
26873  * @extends Roo.Toolbar.Spacer
26874  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
26875  * @constructor
26876  * Creates a new Spacer
26877  */
26878 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
26879     // private
26880     render : function(td){
26881         td.style.width = '100%';
26882         Roo.Toolbar.Fill.superclass.render.call(this, td);
26883     }
26884 });
26885
26886 /**
26887  * @class Roo.Toolbar.TextItem
26888  * @extends Roo.Toolbar.Item
26889  * A simple class that renders text directly into a toolbar.
26890  * @constructor
26891  * Creates a new TextItem
26892  * @param {String} text
26893  */
26894 Roo.Toolbar.TextItem = function(text){
26895     if (typeof(text) == 'object') {
26896         text = text.text;
26897     }
26898     var s = document.createElement("span");
26899     s.className = "ytb-text";
26900     s.innerHTML = text;
26901     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
26902 };
26903 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
26904     enable:Roo.emptyFn,
26905     disable:Roo.emptyFn,
26906     focus:Roo.emptyFn
26907 });
26908
26909 /**
26910  * @class Roo.Toolbar.Button
26911  * @extends Roo.Button
26912  * A button that renders into a toolbar.
26913  * @constructor
26914  * Creates a new Button
26915  * @param {Object} config A standard {@link Roo.Button} config object
26916  */
26917 Roo.Toolbar.Button = function(config){
26918     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
26919 };
26920 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
26921     render : function(td){
26922         this.td = td;
26923         Roo.Toolbar.Button.superclass.render.call(this, td);
26924     },
26925     
26926     /**
26927      * Removes and destroys this button
26928      */
26929     destroy : function(){
26930         Roo.Toolbar.Button.superclass.destroy.call(this);
26931         this.td.parentNode.removeChild(this.td);
26932     },
26933     
26934     /**
26935      * Shows this button
26936      */
26937     show: function(){
26938         this.hidden = false;
26939         this.td.style.display = "";
26940     },
26941     
26942     /**
26943      * Hides this button
26944      */
26945     hide: function(){
26946         this.hidden = true;
26947         this.td.style.display = "none";
26948     },
26949
26950     /**
26951      * Disables this item
26952      */
26953     disable : function(){
26954         Roo.fly(this.td).addClass("x-item-disabled");
26955         this.disabled = true;
26956     },
26957
26958     /**
26959      * Enables this item
26960      */
26961     enable : function(){
26962         Roo.fly(this.td).removeClass("x-item-disabled");
26963         this.disabled = false;
26964     }
26965 });
26966 // backwards compat
26967 Roo.ToolbarButton = Roo.Toolbar.Button;
26968
26969 /**
26970  * @class Roo.Toolbar.SplitButton
26971  * @extends Roo.SplitButton
26972  * A menu button that renders into a toolbar.
26973  * @constructor
26974  * Creates a new SplitButton
26975  * @param {Object} config A standard {@link Roo.SplitButton} config object
26976  */
26977 Roo.Toolbar.SplitButton = function(config){
26978     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
26979 };
26980 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
26981     render : function(td){
26982         this.td = td;
26983         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
26984     },
26985     
26986     /**
26987      * Removes and destroys this button
26988      */
26989     destroy : function(){
26990         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
26991         this.td.parentNode.removeChild(this.td);
26992     },
26993     
26994     /**
26995      * Shows this button
26996      */
26997     show: function(){
26998         this.hidden = false;
26999         this.td.style.display = "";
27000     },
27001     
27002     /**
27003      * Hides this button
27004      */
27005     hide: function(){
27006         this.hidden = true;
27007         this.td.style.display = "none";
27008     }
27009 });
27010
27011 // backwards compat
27012 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27013  * Based on:
27014  * Ext JS Library 1.1.1
27015  * Copyright(c) 2006-2007, Ext JS, LLC.
27016  *
27017  * Originally Released Under LGPL - original licence link has changed is not relivant.
27018  *
27019  * Fork - LGPL
27020  * <script type="text/javascript">
27021  */
27022  
27023 /**
27024  * @class Roo.PagingToolbar
27025  * @extends Roo.Toolbar
27026  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27027  * @constructor
27028  * Create a new PagingToolbar
27029  * @param {Object} config The config object
27030  */
27031 Roo.PagingToolbar = function(el, ds, config)
27032 {
27033     // old args format still supported... - xtype is prefered..
27034     if (typeof(el) == 'object' && el.xtype) {
27035         // created from xtype...
27036         config = el;
27037         ds = el.dataSource;
27038         el = config.container;
27039     }
27040     
27041     
27042     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27043     this.ds = ds;
27044     this.cursor = 0;
27045     this.renderButtons(this.el);
27046     this.bind(ds);
27047 };
27048
27049 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27050     /**
27051      * @cfg {Roo.data.Store} dataSource
27052      * The underlying data store providing the paged data
27053      */
27054     /**
27055      * @cfg {String/HTMLElement/Element} container
27056      * container The id or element that will contain the toolbar
27057      */
27058     /**
27059      * @cfg {Boolean} displayInfo
27060      * True to display the displayMsg (defaults to false)
27061      */
27062     /**
27063      * @cfg {Number} pageSize
27064      * The number of records to display per page (defaults to 20)
27065      */
27066     pageSize: 20,
27067     /**
27068      * @cfg {String} displayMsg
27069      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27070      */
27071     displayMsg : 'Displaying {0} - {1} of {2}',
27072     /**
27073      * @cfg {String} emptyMsg
27074      * The message to display when no records are found (defaults to "No data to display")
27075      */
27076     emptyMsg : 'No data to display',
27077     /**
27078      * Customizable piece of the default paging text (defaults to "Page")
27079      * @type String
27080      */
27081     beforePageText : "Page",
27082     /**
27083      * Customizable piece of the default paging text (defaults to "of %0")
27084      * @type String
27085      */
27086     afterPageText : "of {0}",
27087     /**
27088      * Customizable piece of the default paging text (defaults to "First Page")
27089      * @type String
27090      */
27091     firstText : "First Page",
27092     /**
27093      * Customizable piece of the default paging text (defaults to "Previous Page")
27094      * @type String
27095      */
27096     prevText : "Previous Page",
27097     /**
27098      * Customizable piece of the default paging text (defaults to "Next Page")
27099      * @type String
27100      */
27101     nextText : "Next Page",
27102     /**
27103      * Customizable piece of the default paging text (defaults to "Last Page")
27104      * @type String
27105      */
27106     lastText : "Last Page",
27107     /**
27108      * Customizable piece of the default paging text (defaults to "Refresh")
27109      * @type String
27110      */
27111     refreshText : "Refresh",
27112
27113     // private
27114     renderButtons : function(el){
27115         Roo.PagingToolbar.superclass.render.call(this, el);
27116         this.first = this.addButton({
27117             tooltip: this.firstText,
27118             cls: "x-btn-icon x-grid-page-first",
27119             disabled: true,
27120             handler: this.onClick.createDelegate(this, ["first"])
27121         });
27122         this.prev = this.addButton({
27123             tooltip: this.prevText,
27124             cls: "x-btn-icon x-grid-page-prev",
27125             disabled: true,
27126             handler: this.onClick.createDelegate(this, ["prev"])
27127         });
27128         this.addSeparator();
27129         this.add(this.beforePageText);
27130         this.field = Roo.get(this.addDom({
27131            tag: "input",
27132            type: "text",
27133            size: "3",
27134            value: "1",
27135            cls: "x-grid-page-number"
27136         }).el);
27137         this.field.on("keydown", this.onPagingKeydown, this);
27138         this.field.on("focus", function(){this.dom.select();});
27139         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27140         this.field.setHeight(18);
27141         this.addSeparator();
27142         this.next = this.addButton({
27143             tooltip: this.nextText,
27144             cls: "x-btn-icon x-grid-page-next",
27145             disabled: true,
27146             handler: this.onClick.createDelegate(this, ["next"])
27147         });
27148         this.last = this.addButton({
27149             tooltip: this.lastText,
27150             cls: "x-btn-icon x-grid-page-last",
27151             disabled: true,
27152             handler: this.onClick.createDelegate(this, ["last"])
27153         });
27154         this.addSeparator();
27155         this.loading = this.addButton({
27156             tooltip: this.refreshText,
27157             cls: "x-btn-icon x-grid-loading",
27158             handler: this.onClick.createDelegate(this, ["refresh"])
27159         });
27160
27161         if(this.displayInfo){
27162             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27163         }
27164     },
27165
27166     // private
27167     updateInfo : function(){
27168         if(this.displayEl){
27169             var count = this.ds.getCount();
27170             var msg = count == 0 ?
27171                 this.emptyMsg :
27172                 String.format(
27173                     this.displayMsg,
27174                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27175                 );
27176             this.displayEl.update(msg);
27177         }
27178     },
27179
27180     // private
27181     onLoad : function(ds, r, o){
27182        this.cursor = o.params ? o.params.start : 0;
27183        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27184
27185        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27186        this.field.dom.value = ap;
27187        this.first.setDisabled(ap == 1);
27188        this.prev.setDisabled(ap == 1);
27189        this.next.setDisabled(ap == ps);
27190        this.last.setDisabled(ap == ps);
27191        this.loading.enable();
27192        this.updateInfo();
27193     },
27194
27195     // private
27196     getPageData : function(){
27197         var total = this.ds.getTotalCount();
27198         return {
27199             total : total,
27200             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27201             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27202         };
27203     },
27204
27205     // private
27206     onLoadError : function(){
27207         this.loading.enable();
27208     },
27209
27210     // private
27211     onPagingKeydown : function(e){
27212         var k = e.getKey();
27213         var d = this.getPageData();
27214         if(k == e.RETURN){
27215             var v = this.field.dom.value, pageNum;
27216             if(!v || isNaN(pageNum = parseInt(v, 10))){
27217                 this.field.dom.value = d.activePage;
27218                 return;
27219             }
27220             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27221             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27222             e.stopEvent();
27223         }
27224         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))
27225         {
27226           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27227           this.field.dom.value = pageNum;
27228           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27229           e.stopEvent();
27230         }
27231         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27232         {
27233           var v = this.field.dom.value, pageNum; 
27234           var increment = (e.shiftKey) ? 10 : 1;
27235           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27236             increment *= -1;
27237           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27238             this.field.dom.value = d.activePage;
27239             return;
27240           }
27241           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27242           {
27243             this.field.dom.value = parseInt(v, 10) + increment;
27244             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27245             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27246           }
27247           e.stopEvent();
27248         }
27249     },
27250
27251     // private
27252     beforeLoad : function(){
27253         if(this.loading){
27254             this.loading.disable();
27255         }
27256     },
27257
27258     // private
27259     onClick : function(which){
27260         var ds = this.ds;
27261         switch(which){
27262             case "first":
27263                 ds.load({params:{start: 0, limit: this.pageSize}});
27264             break;
27265             case "prev":
27266                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27267             break;
27268             case "next":
27269                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27270             break;
27271             case "last":
27272                 var total = ds.getTotalCount();
27273                 var extra = total % this.pageSize;
27274                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27275                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27276             break;
27277             case "refresh":
27278                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27279             break;
27280         }
27281     },
27282
27283     /**
27284      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27285      * @param {Roo.data.Store} store The data store to unbind
27286      */
27287     unbind : function(ds){
27288         ds.un("beforeload", this.beforeLoad, this);
27289         ds.un("load", this.onLoad, this);
27290         ds.un("loadexception", this.onLoadError, this);
27291         ds.un("remove", this.updateInfo, this);
27292         ds.un("add", this.updateInfo, this);
27293         this.ds = undefined;
27294     },
27295
27296     /**
27297      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27298      * @param {Roo.data.Store} store The data store to bind
27299      */
27300     bind : function(ds){
27301         ds.on("beforeload", this.beforeLoad, this);
27302         ds.on("load", this.onLoad, this);
27303         ds.on("loadexception", this.onLoadError, this);
27304         ds.on("remove", this.updateInfo, this);
27305         ds.on("add", this.updateInfo, this);
27306         this.ds = ds;
27307     }
27308 });/*
27309  * Based on:
27310  * Ext JS Library 1.1.1
27311  * Copyright(c) 2006-2007, Ext JS, LLC.
27312  *
27313  * Originally Released Under LGPL - original licence link has changed is not relivant.
27314  *
27315  * Fork - LGPL
27316  * <script type="text/javascript">
27317  */
27318
27319 /**
27320  * @class Roo.Resizable
27321  * @extends Roo.util.Observable
27322  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27323  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27324  * 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
27325  * the element will be wrapped for you automatically.</p>
27326  * <p>Here is the list of valid resize handles:</p>
27327  * <pre>
27328 Value   Description
27329 ------  -------------------
27330  'n'     north
27331  's'     south
27332  'e'     east
27333  'w'     west
27334  'nw'    northwest
27335  'sw'    southwest
27336  'se'    southeast
27337  'ne'    northeast
27338  'all'   all
27339 </pre>
27340  * <p>Here's an example showing the creation of a typical Resizable:</p>
27341  * <pre><code>
27342 var resizer = new Roo.Resizable("element-id", {
27343     handles: 'all',
27344     minWidth: 200,
27345     minHeight: 100,
27346     maxWidth: 500,
27347     maxHeight: 400,
27348     pinned: true
27349 });
27350 resizer.on("resize", myHandler);
27351 </code></pre>
27352  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27353  * resizer.east.setDisplayed(false);</p>
27354  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27355  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27356  * resize operation's new size (defaults to [0, 0])
27357  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27358  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27359  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27360  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27361  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27362  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27363  * @cfg {Number} width The width of the element in pixels (defaults to null)
27364  * @cfg {Number} height The height of the element in pixels (defaults to null)
27365  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27366  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27367  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27368  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27369  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27370  * in favor of the handles config option (defaults to false)
27371  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27372  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27373  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27374  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27375  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27376  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27377  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27378  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27379  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27380  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27381  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27382  * @constructor
27383  * Create a new resizable component
27384  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27385  * @param {Object} config configuration options
27386   */
27387 Roo.Resizable = function(el, config){
27388     this.el = Roo.get(el);
27389
27390     if(config && config.wrap){
27391         config.resizeChild = this.el;
27392         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27393         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27394         this.el.setStyle("overflow", "hidden");
27395         this.el.setPositioning(config.resizeChild.getPositioning());
27396         config.resizeChild.clearPositioning();
27397         if(!config.width || !config.height){
27398             var csize = config.resizeChild.getSize();
27399             this.el.setSize(csize.width, csize.height);
27400         }
27401         if(config.pinned && !config.adjustments){
27402             config.adjustments = "auto";
27403         }
27404     }
27405
27406     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27407     this.proxy.unselectable();
27408     this.proxy.enableDisplayMode('block');
27409
27410     Roo.apply(this, config);
27411
27412     if(this.pinned){
27413         this.disableTrackOver = true;
27414         this.el.addClass("x-resizable-pinned");
27415     }
27416     // if the element isn't positioned, make it relative
27417     var position = this.el.getStyle("position");
27418     if(position != "absolute" && position != "fixed"){
27419         this.el.setStyle("position", "relative");
27420     }
27421     if(!this.handles){ // no handles passed, must be legacy style
27422         this.handles = 's,e,se';
27423         if(this.multiDirectional){
27424             this.handles += ',n,w';
27425         }
27426     }
27427     if(this.handles == "all"){
27428         this.handles = "n s e w ne nw se sw";
27429     }
27430     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27431     var ps = Roo.Resizable.positions;
27432     for(var i = 0, len = hs.length; i < len; i++){
27433         if(hs[i] && ps[hs[i]]){
27434             var pos = ps[hs[i]];
27435             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27436         }
27437     }
27438     // legacy
27439     this.corner = this.southeast;
27440
27441     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1){
27442         this.updateBox = true;
27443     }
27444
27445     this.activeHandle = null;
27446
27447     if(this.resizeChild){
27448         if(typeof this.resizeChild == "boolean"){
27449             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27450         }else{
27451             this.resizeChild = Roo.get(this.resizeChild, true);
27452         }
27453     }
27454
27455     if(this.adjustments == "auto"){
27456         var rc = this.resizeChild;
27457         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27458         if(rc && (hw || hn)){
27459             rc.position("relative");
27460             rc.setLeft(hw ? hw.el.getWidth() : 0);
27461             rc.setTop(hn ? hn.el.getHeight() : 0);
27462         }
27463         this.adjustments = [
27464             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27465             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27466         ];
27467     }
27468
27469     if(this.draggable){
27470         this.dd = this.dynamic ?
27471             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27472         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27473     }
27474
27475     // public events
27476     this.addEvents({
27477         /**
27478          * @event beforeresize
27479          * Fired before resize is allowed. Set enabled to false to cancel resize.
27480          * @param {Roo.Resizable} this
27481          * @param {Roo.EventObject} e The mousedown event
27482          */
27483         "beforeresize" : true,
27484         /**
27485          * @event resize
27486          * Fired after a resize.
27487          * @param {Roo.Resizable} this
27488          * @param {Number} width The new width
27489          * @param {Number} height The new height
27490          * @param {Roo.EventObject} e The mouseup event
27491          */
27492         "resize" : true
27493     });
27494
27495     if(this.width !== null && this.height !== null){
27496         this.resizeTo(this.width, this.height);
27497     }else{
27498         this.updateChildSize();
27499     }
27500     if(Roo.isIE){
27501         this.el.dom.style.zoom = 1;
27502     }
27503     Roo.Resizable.superclass.constructor.call(this);
27504 };
27505
27506 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27507         resizeChild : false,
27508         adjustments : [0, 0],
27509         minWidth : 5,
27510         minHeight : 5,
27511         maxWidth : 10000,
27512         maxHeight : 10000,
27513         enabled : true,
27514         animate : false,
27515         duration : .35,
27516         dynamic : false,
27517         handles : false,
27518         multiDirectional : false,
27519         disableTrackOver : false,
27520         easing : 'easeOutStrong',
27521         widthIncrement : 0,
27522         heightIncrement : 0,
27523         pinned : false,
27524         width : null,
27525         height : null,
27526         preserveRatio : false,
27527         transparent: false,
27528         minX: 0,
27529         minY: 0,
27530         draggable: false,
27531
27532         /**
27533          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27534          */
27535         constrainTo: undefined,
27536         /**
27537          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27538          */
27539         resizeRegion: undefined,
27540
27541
27542     /**
27543      * Perform a manual resize
27544      * @param {Number} width
27545      * @param {Number} height
27546      */
27547     resizeTo : function(width, height){
27548         this.el.setSize(width, height);
27549         this.updateChildSize();
27550         this.fireEvent("resize", this, width, height, null);
27551     },
27552
27553     // private
27554     startSizing : function(e, handle){
27555         this.fireEvent("beforeresize", this, e);
27556         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27557
27558             if(!this.overlay){
27559                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27560                 this.overlay.unselectable();
27561                 this.overlay.enableDisplayMode("block");
27562                 this.overlay.on("mousemove", this.onMouseMove, this);
27563                 this.overlay.on("mouseup", this.onMouseUp, this);
27564             }
27565             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27566
27567             this.resizing = true;
27568             this.startBox = this.el.getBox();
27569             this.startPoint = e.getXY();
27570             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27571                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27572
27573             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27574             this.overlay.show();
27575
27576             if(this.constrainTo) {
27577                 var ct = Roo.get(this.constrainTo);
27578                 this.resizeRegion = ct.getRegion().adjust(
27579                     ct.getFrameWidth('t'),
27580                     ct.getFrameWidth('l'),
27581                     -ct.getFrameWidth('b'),
27582                     -ct.getFrameWidth('r')
27583                 );
27584             }
27585
27586             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27587             this.proxy.show();
27588             this.proxy.setBox(this.startBox);
27589             if(!this.dynamic){
27590                 this.proxy.setStyle('visibility', 'visible');
27591             }
27592         }
27593     },
27594
27595     // private
27596     onMouseDown : function(handle, e){
27597         if(this.enabled){
27598             e.stopEvent();
27599             this.activeHandle = handle;
27600             this.startSizing(e, handle);
27601         }
27602     },
27603
27604     // private
27605     onMouseUp : function(e){
27606         var size = this.resizeElement();
27607         this.resizing = false;
27608         this.handleOut();
27609         this.overlay.hide();
27610         this.proxy.hide();
27611         this.fireEvent("resize", this, size.width, size.height, e);
27612     },
27613
27614     // private
27615     updateChildSize : function(){
27616         if(this.resizeChild){
27617             var el = this.el;
27618             var child = this.resizeChild;
27619             var adj = this.adjustments;
27620             if(el.dom.offsetWidth){
27621                 var b = el.getSize(true);
27622                 child.setSize(b.width+adj[0], b.height+adj[1]);
27623             }
27624             // Second call here for IE
27625             // The first call enables instant resizing and
27626             // the second call corrects scroll bars if they
27627             // exist
27628             if(Roo.isIE){
27629                 setTimeout(function(){
27630                     if(el.dom.offsetWidth){
27631                         var b = el.getSize(true);
27632                         child.setSize(b.width+adj[0], b.height+adj[1]);
27633                     }
27634                 }, 10);
27635             }
27636         }
27637     },
27638
27639     // private
27640     snap : function(value, inc, min){
27641         if(!inc || !value) return value;
27642         var newValue = value;
27643         var m = value % inc;
27644         if(m > 0){
27645             if(m > (inc/2)){
27646                 newValue = value + (inc-m);
27647             }else{
27648                 newValue = value - m;
27649             }
27650         }
27651         return Math.max(min, newValue);
27652     },
27653
27654     // private
27655     resizeElement : function(){
27656         var box = this.proxy.getBox();
27657         if(this.updateBox){
27658             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27659         }else{
27660             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27661         }
27662         this.updateChildSize();
27663         if(!this.dynamic){
27664             this.proxy.hide();
27665         }
27666         return box;
27667     },
27668
27669     // private
27670     constrain : function(v, diff, m, mx){
27671         if(v - diff < m){
27672             diff = v - m;
27673         }else if(v - diff > mx){
27674             diff = mx - v;
27675         }
27676         return diff;
27677     },
27678
27679     // private
27680     onMouseMove : function(e){
27681         if(this.enabled){
27682             try{// try catch so if something goes wrong the user doesn't get hung
27683
27684             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27685                 return;
27686             }
27687
27688             //var curXY = this.startPoint;
27689             var curSize = this.curSize || this.startBox;
27690             var x = this.startBox.x, y = this.startBox.y;
27691             var ox = x, oy = y;
27692             var w = curSize.width, h = curSize.height;
27693             var ow = w, oh = h;
27694             var mw = this.minWidth, mh = this.minHeight;
27695             var mxw = this.maxWidth, mxh = this.maxHeight;
27696             var wi = this.widthIncrement;
27697             var hi = this.heightIncrement;
27698
27699             var eventXY = e.getXY();
27700             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27701             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27702
27703             var pos = this.activeHandle.position;
27704
27705             switch(pos){
27706                 case "east":
27707                     w += diffX;
27708                     w = Math.min(Math.max(mw, w), mxw);
27709                     break;
27710                 case "south":
27711                     h += diffY;
27712                     h = Math.min(Math.max(mh, h), mxh);
27713                     break;
27714                 case "southeast":
27715                     w += diffX;
27716                     h += diffY;
27717                     w = Math.min(Math.max(mw, w), mxw);
27718                     h = Math.min(Math.max(mh, h), mxh);
27719                     break;
27720                 case "north":
27721                     diffY = this.constrain(h, diffY, mh, mxh);
27722                     y += diffY;
27723                     h -= diffY;
27724                     break;
27725                 case "west":
27726                     diffX = this.constrain(w, diffX, mw, mxw);
27727                     x += diffX;
27728                     w -= diffX;
27729                     break;
27730                 case "northeast":
27731                     w += diffX;
27732                     w = Math.min(Math.max(mw, w), mxw);
27733                     diffY = this.constrain(h, diffY, mh, mxh);
27734                     y += diffY;
27735                     h -= diffY;
27736                     break;
27737                 case "northwest":
27738                     diffX = this.constrain(w, diffX, mw, mxw);
27739                     diffY = this.constrain(h, diffY, mh, mxh);
27740                     y += diffY;
27741                     h -= diffY;
27742                     x += diffX;
27743                     w -= diffX;
27744                     break;
27745                case "southwest":
27746                     diffX = this.constrain(w, diffX, mw, mxw);
27747                     h += diffY;
27748                     h = Math.min(Math.max(mh, h), mxh);
27749                     x += diffX;
27750                     w -= diffX;
27751                     break;
27752             }
27753
27754             var sw = this.snap(w, wi, mw);
27755             var sh = this.snap(h, hi, mh);
27756             if(sw != w || sh != h){
27757                 switch(pos){
27758                     case "northeast":
27759                         y -= sh - h;
27760                     break;
27761                     case "north":
27762                         y -= sh - h;
27763                         break;
27764                     case "southwest":
27765                         x -= sw - w;
27766                     break;
27767                     case "west":
27768                         x -= sw - w;
27769                         break;
27770                     case "northwest":
27771                         x -= sw - w;
27772                         y -= sh - h;
27773                     break;
27774                 }
27775                 w = sw;
27776                 h = sh;
27777             }
27778
27779             if(this.preserveRatio){
27780                 switch(pos){
27781                     case "southeast":
27782                     case "east":
27783                         h = oh * (w/ow);
27784                         h = Math.min(Math.max(mh, h), mxh);
27785                         w = ow * (h/oh);
27786                        break;
27787                     case "south":
27788                         w = ow * (h/oh);
27789                         w = Math.min(Math.max(mw, w), mxw);
27790                         h = oh * (w/ow);
27791                         break;
27792                     case "northeast":
27793                         w = ow * (h/oh);
27794                         w = Math.min(Math.max(mw, w), mxw);
27795                         h = oh * (w/ow);
27796                     break;
27797                     case "north":
27798                         var tw = w;
27799                         w = ow * (h/oh);
27800                         w = Math.min(Math.max(mw, w), mxw);
27801                         h = oh * (w/ow);
27802                         x += (tw - w) / 2;
27803                         break;
27804                     case "southwest":
27805                         h = oh * (w/ow);
27806                         h = Math.min(Math.max(mh, h), mxh);
27807                         var tw = w;
27808                         w = ow * (h/oh);
27809                         x += tw - w;
27810                         break;
27811                     case "west":
27812                         var th = h;
27813                         h = oh * (w/ow);
27814                         h = Math.min(Math.max(mh, h), mxh);
27815                         y += (th - h) / 2;
27816                         var tw = w;
27817                         w = ow * (h/oh);
27818                         x += tw - w;
27819                        break;
27820                     case "northwest":
27821                         var tw = w;
27822                         var th = h;
27823                         h = oh * (w/ow);
27824                         h = Math.min(Math.max(mh, h), mxh);
27825                         w = ow * (h/oh);
27826                         y += th - h;
27827                          x += tw - w;
27828                        break;
27829
27830                 }
27831             }
27832             this.proxy.setBounds(x, y, w, h);
27833             if(this.dynamic){
27834                 this.resizeElement();
27835             }
27836             }catch(e){}
27837         }
27838     },
27839
27840     // private
27841     handleOver : function(){
27842         if(this.enabled){
27843             this.el.addClass("x-resizable-over");
27844         }
27845     },
27846
27847     // private
27848     handleOut : function(){
27849         if(!this.resizing){
27850             this.el.removeClass("x-resizable-over");
27851         }
27852     },
27853
27854     /**
27855      * Returns the element this component is bound to.
27856      * @return {Roo.Element}
27857      */
27858     getEl : function(){
27859         return this.el;
27860     },
27861
27862     /**
27863      * Returns the resizeChild element (or null).
27864      * @return {Roo.Element}
27865      */
27866     getResizeChild : function(){
27867         return this.resizeChild;
27868     },
27869
27870     /**
27871      * Destroys this resizable. If the element was wrapped and
27872      * removeEl is not true then the element remains.
27873      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
27874      */
27875     destroy : function(removeEl){
27876         this.proxy.remove();
27877         if(this.overlay){
27878             this.overlay.removeAllListeners();
27879             this.overlay.remove();
27880         }
27881         var ps = Roo.Resizable.positions;
27882         for(var k in ps){
27883             if(typeof ps[k] != "function" && this[ps[k]]){
27884                 var h = this[ps[k]];
27885                 h.el.removeAllListeners();
27886                 h.el.remove();
27887             }
27888         }
27889         if(removeEl){
27890             this.el.update("");
27891             this.el.remove();
27892         }
27893     }
27894 });
27895
27896 // private
27897 // hash to map config positions to true positions
27898 Roo.Resizable.positions = {
27899     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast"
27900 };
27901
27902 // private
27903 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
27904     if(!this.tpl){
27905         // only initialize the template if resizable is used
27906         var tpl = Roo.DomHelper.createTemplate(
27907             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
27908         );
27909         tpl.compile();
27910         Roo.Resizable.Handle.prototype.tpl = tpl;
27911     }
27912     this.position = pos;
27913     this.rz = rz;
27914     this.el = this.tpl.append(rz.el.dom, [this.position], true);
27915     this.el.unselectable();
27916     if(transparent){
27917         this.el.setOpacity(0);
27918     }
27919     this.el.on("mousedown", this.onMouseDown, this);
27920     if(!disableTrackOver){
27921         this.el.on("mouseover", this.onMouseOver, this);
27922         this.el.on("mouseout", this.onMouseOut, this);
27923     }
27924 };
27925
27926 // private
27927 Roo.Resizable.Handle.prototype = {
27928     afterResize : function(rz){
27929         // do nothing
27930     },
27931     // private
27932     onMouseDown : function(e){
27933         this.rz.onMouseDown(this, e);
27934     },
27935     // private
27936     onMouseOver : function(e){
27937         this.rz.handleOver(this, e);
27938     },
27939     // private
27940     onMouseOut : function(e){
27941         this.rz.handleOut(this, e);
27942     }
27943 };/*
27944  * Based on:
27945  * Ext JS Library 1.1.1
27946  * Copyright(c) 2006-2007, Ext JS, LLC.
27947  *
27948  * Originally Released Under LGPL - original licence link has changed is not relivant.
27949  *
27950  * Fork - LGPL
27951  * <script type="text/javascript">
27952  */
27953
27954 /**
27955  * @class Roo.Editor
27956  * @extends Roo.Component
27957  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
27958  * @constructor
27959  * Create a new Editor
27960  * @param {Roo.form.Field} field The Field object (or descendant)
27961  * @param {Object} config The config object
27962  */
27963 Roo.Editor = function(field, config){
27964     Roo.Editor.superclass.constructor.call(this, config);
27965     this.field = field;
27966     this.addEvents({
27967         /**
27968              * @event beforestartedit
27969              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
27970              * false from the handler of this event.
27971              * @param {Editor} this
27972              * @param {Roo.Element} boundEl The underlying element bound to this editor
27973              * @param {Mixed} value The field value being set
27974              */
27975         "beforestartedit" : true,
27976         /**
27977              * @event startedit
27978              * Fires when this editor is displayed
27979              * @param {Roo.Element} boundEl The underlying element bound to this editor
27980              * @param {Mixed} value The starting field value
27981              */
27982         "startedit" : true,
27983         /**
27984              * @event beforecomplete
27985              * Fires after a change has been made to the field, but before the change is reflected in the underlying
27986              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
27987              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
27988              * event will not fire since no edit actually occurred.
27989              * @param {Editor} this
27990              * @param {Mixed} value The current field value
27991              * @param {Mixed} startValue The original field value
27992              */
27993         "beforecomplete" : true,
27994         /**
27995              * @event complete
27996              * Fires after editing is complete and any changed value has been written to the underlying field.
27997              * @param {Editor} this
27998              * @param {Mixed} value The current field value
27999              * @param {Mixed} startValue The original field value
28000              */
28001         "complete" : true,
28002         /**
28003          * @event specialkey
28004          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28005          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28006          * @param {Roo.form.Field} this
28007          * @param {Roo.EventObject} e The event object
28008          */
28009         "specialkey" : true
28010     });
28011 };
28012
28013 Roo.extend(Roo.Editor, Roo.Component, {
28014     /**
28015      * @cfg {Boolean/String} autosize
28016      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28017      * or "height" to adopt the height only (defaults to false)
28018      */
28019     /**
28020      * @cfg {Boolean} revertInvalid
28021      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28022      * validation fails (defaults to true)
28023      */
28024     /**
28025      * @cfg {Boolean} ignoreNoChange
28026      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28027      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28028      * will never be ignored.
28029      */
28030     /**
28031      * @cfg {Boolean} hideEl
28032      * False to keep the bound element visible while the editor is displayed (defaults to true)
28033      */
28034     /**
28035      * @cfg {Mixed} value
28036      * The data value of the underlying field (defaults to "")
28037      */
28038     value : "",
28039     /**
28040      * @cfg {String} alignment
28041      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28042      */
28043     alignment: "c-c?",
28044     /**
28045      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28046      * for bottom-right shadow (defaults to "frame")
28047      */
28048     shadow : "frame",
28049     /**
28050      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28051      */
28052     constrain : false,
28053     /**
28054      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28055      */
28056     completeOnEnter : false,
28057     /**
28058      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28059      */
28060     cancelOnEsc : false,
28061     /**
28062      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28063      */
28064     updateEl : false,
28065
28066     // private
28067     onRender : function(ct, position){
28068         this.el = new Roo.Layer({
28069             shadow: this.shadow,
28070             cls: "x-editor",
28071             parentEl : ct,
28072             shim : this.shim,
28073             shadowOffset:4,
28074             id: this.id,
28075             constrain: this.constrain
28076         });
28077         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28078         if(this.field.msgTarget != 'title'){
28079             this.field.msgTarget = 'qtip';
28080         }
28081         this.field.render(this.el);
28082         if(Roo.isGecko){
28083             this.field.el.dom.setAttribute('autocomplete', 'off');
28084         }
28085         this.field.on("specialkey", this.onSpecialKey, this);
28086         if(this.swallowKeys){
28087             this.field.el.swallowEvent(['keydown','keypress']);
28088         }
28089         this.field.show();
28090         this.field.on("blur", this.onBlur, this);
28091         if(this.field.grow){
28092             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28093         }
28094     },
28095
28096     onSpecialKey : function(field, e){
28097         if(this.completeOnEnter && e.getKey() == e.ENTER){
28098             e.stopEvent();
28099             this.completeEdit();
28100         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28101             this.cancelEdit();
28102         }else{
28103             this.fireEvent('specialkey', field, e);
28104         }
28105     },
28106
28107     /**
28108      * Starts the editing process and shows the editor.
28109      * @param {String/HTMLElement/Element} el The element to edit
28110      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28111       * to the innerHTML of el.
28112      */
28113     startEdit : function(el, value){
28114         if(this.editing){
28115             this.completeEdit();
28116         }
28117         this.boundEl = Roo.get(el);
28118         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28119         if(!this.rendered){
28120             this.render(this.parentEl || document.body);
28121         }
28122         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28123             return;
28124         }
28125         this.startValue = v;
28126         this.field.setValue(v);
28127         if(this.autoSize){
28128             var sz = this.boundEl.getSize();
28129             switch(this.autoSize){
28130                 case "width":
28131                 this.setSize(sz.width,  "");
28132                 break;
28133                 case "height":
28134                 this.setSize("",  sz.height);
28135                 break;
28136                 default:
28137                 this.setSize(sz.width,  sz.height);
28138             }
28139         }
28140         this.el.alignTo(this.boundEl, this.alignment);
28141         this.editing = true;
28142         if(Roo.QuickTips){
28143             Roo.QuickTips.disable();
28144         }
28145         this.show();
28146     },
28147
28148     /**
28149      * Sets the height and width of this editor.
28150      * @param {Number} width The new width
28151      * @param {Number} height The new height
28152      */
28153     setSize : function(w, h){
28154         this.field.setSize(w, h);
28155         if(this.el){
28156             this.el.sync();
28157         }
28158     },
28159
28160     /**
28161      * Realigns the editor to the bound field based on the current alignment config value.
28162      */
28163     realign : function(){
28164         this.el.alignTo(this.boundEl, this.alignment);
28165     },
28166
28167     /**
28168      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28169      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28170      */
28171     completeEdit : function(remainVisible){
28172         if(!this.editing){
28173             return;
28174         }
28175         var v = this.getValue();
28176         if(this.revertInvalid !== false && !this.field.isValid()){
28177             v = this.startValue;
28178             this.cancelEdit(true);
28179         }
28180         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28181             this.editing = false;
28182             this.hide();
28183             return;
28184         }
28185         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28186             this.editing = false;
28187             if(this.updateEl && this.boundEl){
28188                 this.boundEl.update(v);
28189             }
28190             if(remainVisible !== true){
28191                 this.hide();
28192             }
28193             this.fireEvent("complete", this, v, this.startValue);
28194         }
28195     },
28196
28197     // private
28198     onShow : function(){
28199         this.el.show();
28200         if(this.hideEl !== false){
28201             this.boundEl.hide();
28202         }
28203         this.field.show();
28204         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28205             this.fixIEFocus = true;
28206             this.deferredFocus.defer(50, this);
28207         }else{
28208             this.field.focus();
28209         }
28210         this.fireEvent("startedit", this.boundEl, this.startValue);
28211     },
28212
28213     deferredFocus : function(){
28214         if(this.editing){
28215             this.field.focus();
28216         }
28217     },
28218
28219     /**
28220      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28221      * reverted to the original starting value.
28222      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28223      * cancel (defaults to false)
28224      */
28225     cancelEdit : function(remainVisible){
28226         if(this.editing){
28227             this.setValue(this.startValue);
28228             if(remainVisible !== true){
28229                 this.hide();
28230             }
28231         }
28232     },
28233
28234     // private
28235     onBlur : function(){
28236         if(this.allowBlur !== true && this.editing){
28237             this.completeEdit();
28238         }
28239     },
28240
28241     // private
28242     onHide : function(){
28243         if(this.editing){
28244             this.completeEdit();
28245             return;
28246         }
28247         this.field.blur();
28248         if(this.field.collapse){
28249             this.field.collapse();
28250         }
28251         this.el.hide();
28252         if(this.hideEl !== false){
28253             this.boundEl.show();
28254         }
28255         if(Roo.QuickTips){
28256             Roo.QuickTips.enable();
28257         }
28258     },
28259
28260     /**
28261      * Sets the data value of the editor
28262      * @param {Mixed} value Any valid value supported by the underlying field
28263      */
28264     setValue : function(v){
28265         this.field.setValue(v);
28266     },
28267
28268     /**
28269      * Gets the data value of the editor
28270      * @return {Mixed} The data value
28271      */
28272     getValue : function(){
28273         return this.field.getValue();
28274     }
28275 });/*
28276  * Based on:
28277  * Ext JS Library 1.1.1
28278  * Copyright(c) 2006-2007, Ext JS, LLC.
28279  *
28280  * Originally Released Under LGPL - original licence link has changed is not relivant.
28281  *
28282  * Fork - LGPL
28283  * <script type="text/javascript">
28284  */
28285  
28286 /**
28287  * @class Roo.BasicDialog
28288  * @extends Roo.util.Observable
28289  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28290  * <pre><code>
28291 var dlg = new Roo.BasicDialog("my-dlg", {
28292     height: 200,
28293     width: 300,
28294     minHeight: 100,
28295     minWidth: 150,
28296     modal: true,
28297     proxyDrag: true,
28298     shadow: true
28299 });
28300 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28301 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28302 dlg.addButton('Cancel', dlg.hide, dlg);
28303 dlg.show();
28304 </code></pre>
28305   <b>A Dialog should always be a direct child of the body element.</b>
28306  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28307  * @cfg {String} title Default text to display in the title bar (defaults to null)
28308  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28309  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28310  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28311  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28312  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28313  * (defaults to null with no animation)
28314  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28315  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28316  * property for valid values (defaults to 'all')
28317  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28318  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28319  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28320  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28321  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28322  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28323  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28324  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28325  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28326  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28327  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28328  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28329  * draggable = true (defaults to false)
28330  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28331  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28332  * shadow (defaults to false)
28333  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28334  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28335  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28336  * @cfg {Array} buttons Array of buttons
28337  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28338  * @constructor
28339  * Create a new BasicDialog.
28340  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28341  * @param {Object} config Configuration options
28342  */
28343 Roo.BasicDialog = function(el, config){
28344     this.el = Roo.get(el);
28345     var dh = Roo.DomHelper;
28346     if(!this.el && config && config.autoCreate){
28347         if(typeof config.autoCreate == "object"){
28348             if(!config.autoCreate.id){
28349                 config.autoCreate.id = el;
28350             }
28351             this.el = dh.append(document.body,
28352                         config.autoCreate, true);
28353         }else{
28354             this.el = dh.append(document.body,
28355                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28356         }
28357     }
28358     el = this.el;
28359     el.setDisplayed(true);
28360     el.hide = this.hideAction;
28361     this.id = el.id;
28362     el.addClass("x-dlg");
28363
28364     Roo.apply(this, config);
28365
28366     this.proxy = el.createProxy("x-dlg-proxy");
28367     this.proxy.hide = this.hideAction;
28368     this.proxy.setOpacity(.5);
28369     this.proxy.hide();
28370
28371     if(config.width){
28372         el.setWidth(config.width);
28373     }
28374     if(config.height){
28375         el.setHeight(config.height);
28376     }
28377     this.size = el.getSize();
28378     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28379         this.xy = [config.x,config.y];
28380     }else{
28381         this.xy = el.getCenterXY(true);
28382     }
28383     /** The header element @type Roo.Element */
28384     this.header = el.child("> .x-dlg-hd");
28385     /** The body element @type Roo.Element */
28386     this.body = el.child("> .x-dlg-bd");
28387     /** The footer element @type Roo.Element */
28388     this.footer = el.child("> .x-dlg-ft");
28389
28390     if(!this.header){
28391         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28392     }
28393     if(!this.body){
28394         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28395     }
28396
28397     this.header.unselectable();
28398     if(this.title){
28399         this.header.update(this.title);
28400     }
28401     // this element allows the dialog to be focused for keyboard event
28402     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28403     this.focusEl.swallowEvent("click", true);
28404
28405     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28406
28407     // wrap the body and footer for special rendering
28408     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28409     if(this.footer){
28410         this.bwrap.dom.appendChild(this.footer.dom);
28411     }
28412
28413     this.bg = this.el.createChild({
28414         tag: "div", cls:"x-dlg-bg",
28415         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28416     });
28417     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28418
28419
28420     if(this.autoScroll !== false && !this.autoTabs){
28421         this.body.setStyle("overflow", "auto");
28422     }
28423
28424     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28425
28426     if(this.closable !== false){
28427         this.el.addClass("x-dlg-closable");
28428         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28429         this.close.on("click", this.closeClick, this);
28430         this.close.addClassOnOver("x-dlg-close-over");
28431     }
28432     if(this.collapsible !== false){
28433         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28434         this.collapseBtn.on("click", this.collapseClick, this);
28435         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28436         this.header.on("dblclick", this.collapseClick, this);
28437     }
28438     if(this.resizable !== false){
28439         this.el.addClass("x-dlg-resizable");
28440         this.resizer = new Roo.Resizable(el, {
28441             minWidth: this.minWidth || 80,
28442             minHeight:this.minHeight || 80,
28443             handles: this.resizeHandles || "all",
28444             pinned: true
28445         });
28446         this.resizer.on("beforeresize", this.beforeResize, this);
28447         this.resizer.on("resize", this.onResize, this);
28448     }
28449     if(this.draggable !== false){
28450         el.addClass("x-dlg-draggable");
28451         if (!this.proxyDrag) {
28452             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28453         }
28454         else {
28455             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28456         }
28457         dd.setHandleElId(this.header.id);
28458         dd.endDrag = this.endMove.createDelegate(this);
28459         dd.startDrag = this.startMove.createDelegate(this);
28460         dd.onDrag = this.onDrag.createDelegate(this);
28461         dd.scroll = false;
28462         this.dd = dd;
28463     }
28464     if(this.modal){
28465         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28466         this.mask.enableDisplayMode("block");
28467         this.mask.hide();
28468         this.el.addClass("x-dlg-modal");
28469     }
28470     if(this.shadow){
28471         this.shadow = new Roo.Shadow({
28472             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28473             offset : this.shadowOffset
28474         });
28475     }else{
28476         this.shadowOffset = 0;
28477     }
28478     if(Roo.useShims && this.shim !== false){
28479         this.shim = this.el.createShim();
28480         this.shim.hide = this.hideAction;
28481         this.shim.hide();
28482     }else{
28483         this.shim = false;
28484     }
28485     if(this.autoTabs){
28486         this.initTabs();
28487     }
28488     if (this.buttons) { 
28489         var bts= this.buttons;
28490         this.buttons = [];
28491         Roo.each(bts, function(b) {
28492             this.addButton(b);
28493         }, this);
28494     }
28495     
28496     
28497     this.addEvents({
28498         /**
28499          * @event keydown
28500          * Fires when a key is pressed
28501          * @param {Roo.BasicDialog} this
28502          * @param {Roo.EventObject} e
28503          */
28504         "keydown" : true,
28505         /**
28506          * @event move
28507          * Fires when this dialog is moved by the user.
28508          * @param {Roo.BasicDialog} this
28509          * @param {Number} x The new page X
28510          * @param {Number} y The new page Y
28511          */
28512         "move" : true,
28513         /**
28514          * @event resize
28515          * Fires when this dialog is resized by the user.
28516          * @param {Roo.BasicDialog} this
28517          * @param {Number} width The new width
28518          * @param {Number} height The new height
28519          */
28520         "resize" : true,
28521         /**
28522          * @event beforehide
28523          * Fires before this dialog is hidden.
28524          * @param {Roo.BasicDialog} this
28525          */
28526         "beforehide" : true,
28527         /**
28528          * @event hide
28529          * Fires when this dialog is hidden.
28530          * @param {Roo.BasicDialog} this
28531          */
28532         "hide" : true,
28533         /**
28534          * @event beforeshow
28535          * Fires before this dialog is shown.
28536          * @param {Roo.BasicDialog} this
28537          */
28538         "beforeshow" : true,
28539         /**
28540          * @event show
28541          * Fires when this dialog is shown.
28542          * @param {Roo.BasicDialog} this
28543          */
28544         "show" : true
28545     });
28546     el.on("keydown", this.onKeyDown, this);
28547     el.on("mousedown", this.toFront, this);
28548     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28549     this.el.hide();
28550     Roo.DialogManager.register(this);
28551     Roo.BasicDialog.superclass.constructor.call(this);
28552 };
28553
28554 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28555     shadowOffset: Roo.isIE ? 6 : 5,
28556     minHeight: 80,
28557     minWidth: 200,
28558     minButtonWidth: 75,
28559     defaultButton: null,
28560     buttonAlign: "right",
28561     tabTag: 'div',
28562     firstShow: true,
28563
28564     /**
28565      * Sets the dialog title text
28566      * @param {String} text The title text to display
28567      * @return {Roo.BasicDialog} this
28568      */
28569     setTitle : function(text){
28570         this.header.update(text);
28571         return this;
28572     },
28573
28574     // private
28575     closeClick : function(){
28576         this.hide();
28577     },
28578
28579     // private
28580     collapseClick : function(){
28581         this[this.collapsed ? "expand" : "collapse"]();
28582     },
28583
28584     /**
28585      * Collapses the dialog to its minimized state (only the title bar is visible).
28586      * Equivalent to the user clicking the collapse dialog button.
28587      */
28588     collapse : function(){
28589         if(!this.collapsed){
28590             this.collapsed = true;
28591             this.el.addClass("x-dlg-collapsed");
28592             this.restoreHeight = this.el.getHeight();
28593             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28594         }
28595     },
28596
28597     /**
28598      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28599      * clicking the expand dialog button.
28600      */
28601     expand : function(){
28602         if(this.collapsed){
28603             this.collapsed = false;
28604             this.el.removeClass("x-dlg-collapsed");
28605             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28606         }
28607     },
28608
28609     /**
28610      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28611      * @return {Roo.TabPanel} The tabs component
28612      */
28613     initTabs : function(){
28614         var tabs = this.getTabs();
28615         while(tabs.getTab(0)){
28616             tabs.removeTab(0);
28617         }
28618         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28619             var dom = el.dom;
28620             tabs.addTab(Roo.id(dom), dom.title);
28621             dom.title = "";
28622         });
28623         tabs.activate(0);
28624         return tabs;
28625     },
28626
28627     // private
28628     beforeResize : function(){
28629         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28630     },
28631
28632     // private
28633     onResize : function(){
28634         this.refreshSize();
28635         this.syncBodyHeight();
28636         this.adjustAssets();
28637         this.focus();
28638         this.fireEvent("resize", this, this.size.width, this.size.height);
28639     },
28640
28641     // private
28642     onKeyDown : function(e){
28643         if(this.isVisible()){
28644             this.fireEvent("keydown", this, e);
28645         }
28646     },
28647
28648     /**
28649      * Resizes the dialog.
28650      * @param {Number} width
28651      * @param {Number} height
28652      * @return {Roo.BasicDialog} this
28653      */
28654     resizeTo : function(width, height){
28655         this.el.setSize(width, height);
28656         this.size = {width: width, height: height};
28657         this.syncBodyHeight();
28658         if(this.fixedcenter){
28659             this.center();
28660         }
28661         if(this.isVisible()){
28662             this.constrainXY();
28663             this.adjustAssets();
28664         }
28665         this.fireEvent("resize", this, width, height);
28666         return this;
28667     },
28668
28669
28670     /**
28671      * Resizes the dialog to fit the specified content size.
28672      * @param {Number} width
28673      * @param {Number} height
28674      * @return {Roo.BasicDialog} this
28675      */
28676     setContentSize : function(w, h){
28677         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28678         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28679         //if(!this.el.isBorderBox()){
28680             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28681             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28682         //}
28683         if(this.tabs){
28684             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28685             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28686         }
28687         this.resizeTo(w, h);
28688         return this;
28689     },
28690
28691     /**
28692      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28693      * executed in response to a particular key being pressed while the dialog is active.
28694      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28695      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28696      * @param {Function} fn The function to call
28697      * @param {Object} scope (optional) The scope of the function
28698      * @return {Roo.BasicDialog} this
28699      */
28700     addKeyListener : function(key, fn, scope){
28701         var keyCode, shift, ctrl, alt;
28702         if(typeof key == "object" && !(key instanceof Array)){
28703             keyCode = key["key"];
28704             shift = key["shift"];
28705             ctrl = key["ctrl"];
28706             alt = key["alt"];
28707         }else{
28708             keyCode = key;
28709         }
28710         var handler = function(dlg, e){
28711             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28712                 var k = e.getKey();
28713                 if(keyCode instanceof Array){
28714                     for(var i = 0, len = keyCode.length; i < len; i++){
28715                         if(keyCode[i] == k){
28716                           fn.call(scope || window, dlg, k, e);
28717                           return;
28718                         }
28719                     }
28720                 }else{
28721                     if(k == keyCode){
28722                         fn.call(scope || window, dlg, k, e);
28723                     }
28724                 }
28725             }
28726         };
28727         this.on("keydown", handler);
28728         return this;
28729     },
28730
28731     /**
28732      * Returns the TabPanel component (creates it if it doesn't exist).
28733      * Note: If you wish to simply check for the existence of tabs without creating them,
28734      * check for a null 'tabs' property.
28735      * @return {Roo.TabPanel} The tabs component
28736      */
28737     getTabs : function(){
28738         if(!this.tabs){
28739             this.el.addClass("x-dlg-auto-tabs");
28740             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28741             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28742         }
28743         return this.tabs;
28744     },
28745
28746     /**
28747      * Adds a button to the footer section of the dialog.
28748      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28749      * object or a valid Roo.DomHelper element config
28750      * @param {Function} handler The function called when the button is clicked
28751      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28752      * @return {Roo.Button} The new button
28753      */
28754     addButton : function(config, handler, scope){
28755         var dh = Roo.DomHelper;
28756         if(!this.footer){
28757             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28758         }
28759         if(!this.btnContainer){
28760             var tb = this.footer.createChild({
28761
28762                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28763                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28764             }, null, true);
28765             this.btnContainer = tb.firstChild.firstChild.firstChild;
28766         }
28767         var bconfig = {
28768             handler: handler,
28769             scope: scope,
28770             minWidth: this.minButtonWidth,
28771             hideParent:true
28772         };
28773         if(typeof config == "string"){
28774             bconfig.text = config;
28775         }else{
28776             if(config.tag){
28777                 bconfig.dhconfig = config;
28778             }else{
28779                 Roo.apply(bconfig, config);
28780             }
28781         }
28782         var fc = false;
28783         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28784             bconfig.position = Math.max(0, bconfig.position);
28785             fc = this.btnContainer.childNodes[bconfig.position];
28786         }
28787          
28788         var btn = new Roo.Button(
28789             fc ? 
28790                 this.btnContainer.insertBefore(document.createElement("td"),fc)
28791                 : this.btnContainer.appendChild(document.createElement("td")),
28792             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
28793             bconfig
28794         );
28795         this.syncBodyHeight();
28796         if(!this.buttons){
28797             /**
28798              * Array of all the buttons that have been added to this dialog via addButton
28799              * @type Array
28800              */
28801             this.buttons = [];
28802         }
28803         this.buttons.push(btn);
28804         return btn;
28805     },
28806
28807     /**
28808      * Sets the default button to be focused when the dialog is displayed.
28809      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
28810      * @return {Roo.BasicDialog} this
28811      */
28812     setDefaultButton : function(btn){
28813         this.defaultButton = btn;
28814         return this;
28815     },
28816
28817     // private
28818     getHeaderFooterHeight : function(safe){
28819         var height = 0;
28820         if(this.header){
28821            height += this.header.getHeight();
28822         }
28823         if(this.footer){
28824            var fm = this.footer.getMargins();
28825             height += (this.footer.getHeight()+fm.top+fm.bottom);
28826         }
28827         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
28828         height += this.centerBg.getPadding("tb");
28829         return height;
28830     },
28831
28832     // private
28833     syncBodyHeight : function(){
28834         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
28835         var height = this.size.height - this.getHeaderFooterHeight(false);
28836         bd.setHeight(height-bd.getMargins("tb"));
28837         var hh = this.header.getHeight();
28838         var h = this.size.height-hh;
28839         cb.setHeight(h);
28840         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
28841         bw.setHeight(h-cb.getPadding("tb"));
28842         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
28843         bd.setWidth(bw.getWidth(true));
28844         if(this.tabs){
28845             this.tabs.syncHeight();
28846             if(Roo.isIE){
28847                 this.tabs.el.repaint();
28848             }
28849         }
28850     },
28851
28852     /**
28853      * Restores the previous state of the dialog if Roo.state is configured.
28854      * @return {Roo.BasicDialog} this
28855      */
28856     restoreState : function(){
28857         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
28858         if(box && box.width){
28859             this.xy = [box.x, box.y];
28860             this.resizeTo(box.width, box.height);
28861         }
28862         return this;
28863     },
28864
28865     // private
28866     beforeShow : function(){
28867         this.expand();
28868         if(this.fixedcenter){
28869             this.xy = this.el.getCenterXY(true);
28870         }
28871         if(this.modal){
28872             Roo.get(document.body).addClass("x-body-masked");
28873             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
28874             this.mask.show();
28875         }
28876         this.constrainXY();
28877     },
28878
28879     // private
28880     animShow : function(){
28881         var b = Roo.get(this.animateTarget, true).getBox();
28882         this.proxy.setSize(b.width, b.height);
28883         this.proxy.setLocation(b.x, b.y);
28884         this.proxy.show();
28885         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
28886                     true, .35, this.showEl.createDelegate(this));
28887     },
28888
28889     /**
28890      * Shows the dialog.
28891      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
28892      * @return {Roo.BasicDialog} this
28893      */
28894     show : function(animateTarget){
28895         if (this.fireEvent("beforeshow", this) === false){
28896             return;
28897         }
28898         if(this.syncHeightBeforeShow){
28899             this.syncBodyHeight();
28900         }else if(this.firstShow){
28901             this.firstShow = false;
28902             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
28903         }
28904         this.animateTarget = animateTarget || this.animateTarget;
28905         if(!this.el.isVisible()){
28906             this.beforeShow();
28907             if(this.animateTarget){
28908                 this.animShow();
28909             }else{
28910                 this.showEl();
28911             }
28912         }
28913         return this;
28914     },
28915
28916     // private
28917     showEl : function(){
28918         this.proxy.hide();
28919         this.el.setXY(this.xy);
28920         this.el.show();
28921         this.adjustAssets(true);
28922         this.toFront();
28923         this.focus();
28924         // IE peekaboo bug - fix found by Dave Fenwick
28925         if(Roo.isIE){
28926             this.el.repaint();
28927         }
28928         this.fireEvent("show", this);
28929     },
28930
28931     /**
28932      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
28933      * dialog itself will receive focus.
28934      */
28935     focus : function(){
28936         if(this.defaultButton){
28937             this.defaultButton.focus();
28938         }else{
28939             this.focusEl.focus();
28940         }
28941     },
28942
28943     // private
28944     constrainXY : function(){
28945         if(this.constraintoviewport !== false){
28946             if(!this.viewSize){
28947                 if(this.container){
28948                     var s = this.container.getSize();
28949                     this.viewSize = [s.width, s.height];
28950                 }else{
28951                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
28952                 }
28953             }
28954             var s = Roo.get(this.container||document).getScroll();
28955
28956             var x = this.xy[0], y = this.xy[1];
28957             var w = this.size.width, h = this.size.height;
28958             var vw = this.viewSize[0], vh = this.viewSize[1];
28959             // only move it if it needs it
28960             var moved = false;
28961             // first validate right/bottom
28962             if(x + w > vw+s.left){
28963                 x = vw - w;
28964                 moved = true;
28965             }
28966             if(y + h > vh+s.top){
28967                 y = vh - h;
28968                 moved = true;
28969             }
28970             // then make sure top/left isn't negative
28971             if(x < s.left){
28972                 x = s.left;
28973                 moved = true;
28974             }
28975             if(y < s.top){
28976                 y = s.top;
28977                 moved = true;
28978             }
28979             if(moved){
28980                 // cache xy
28981                 this.xy = [x, y];
28982                 if(this.isVisible()){
28983                     this.el.setLocation(x, y);
28984                     this.adjustAssets();
28985                 }
28986             }
28987         }
28988     },
28989
28990     // private
28991     onDrag : function(){
28992         if(!this.proxyDrag){
28993             this.xy = this.el.getXY();
28994             this.adjustAssets();
28995         }
28996     },
28997
28998     // private
28999     adjustAssets : function(doShow){
29000         var x = this.xy[0], y = this.xy[1];
29001         var w = this.size.width, h = this.size.height;
29002         if(doShow === true){
29003             if(this.shadow){
29004                 this.shadow.show(this.el);
29005             }
29006             if(this.shim){
29007                 this.shim.show();
29008             }
29009         }
29010         if(this.shadow && this.shadow.isVisible()){
29011             this.shadow.show(this.el);
29012         }
29013         if(this.shim && this.shim.isVisible()){
29014             this.shim.setBounds(x, y, w, h);
29015         }
29016     },
29017
29018     // private
29019     adjustViewport : function(w, h){
29020         if(!w || !h){
29021             w = Roo.lib.Dom.getViewWidth();
29022             h = Roo.lib.Dom.getViewHeight();
29023         }
29024         // cache the size
29025         this.viewSize = [w, h];
29026         if(this.modal && this.mask.isVisible()){
29027             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29028             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29029         }
29030         if(this.isVisible()){
29031             this.constrainXY();
29032         }
29033     },
29034
29035     /**
29036      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29037      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29038      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29039      */
29040     destroy : function(removeEl){
29041         if(this.isVisible()){
29042             this.animateTarget = null;
29043             this.hide();
29044         }
29045         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29046         if(this.tabs){
29047             this.tabs.destroy(removeEl);
29048         }
29049         Roo.destroy(
29050              this.shim,
29051              this.proxy,
29052              this.resizer,
29053              this.close,
29054              this.mask
29055         );
29056         if(this.dd){
29057             this.dd.unreg();
29058         }
29059         if(this.buttons){
29060            for(var i = 0, len = this.buttons.length; i < len; i++){
29061                this.buttons[i].destroy();
29062            }
29063         }
29064         this.el.removeAllListeners();
29065         if(removeEl === true){
29066             this.el.update("");
29067             this.el.remove();
29068         }
29069         Roo.DialogManager.unregister(this);
29070     },
29071
29072     // private
29073     startMove : function(){
29074         if(this.proxyDrag){
29075             this.proxy.show();
29076         }
29077         if(this.constraintoviewport !== false){
29078             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29079         }
29080     },
29081
29082     // private
29083     endMove : function(){
29084         if(!this.proxyDrag){
29085             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29086         }else{
29087             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29088             this.proxy.hide();
29089         }
29090         this.refreshSize();
29091         this.adjustAssets();
29092         this.focus();
29093         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29094     },
29095
29096     /**
29097      * Brings this dialog to the front of any other visible dialogs
29098      * @return {Roo.BasicDialog} this
29099      */
29100     toFront : function(){
29101         Roo.DialogManager.bringToFront(this);
29102         return this;
29103     },
29104
29105     /**
29106      * Sends this dialog to the back (under) of any other visible dialogs
29107      * @return {Roo.BasicDialog} this
29108      */
29109     toBack : function(){
29110         Roo.DialogManager.sendToBack(this);
29111         return this;
29112     },
29113
29114     /**
29115      * Centers this dialog in the viewport
29116      * @return {Roo.BasicDialog} this
29117      */
29118     center : function(){
29119         var xy = this.el.getCenterXY(true);
29120         this.moveTo(xy[0], xy[1]);
29121         return this;
29122     },
29123
29124     /**
29125      * Moves the dialog's top-left corner to the specified point
29126      * @param {Number} x
29127      * @param {Number} y
29128      * @return {Roo.BasicDialog} this
29129      */
29130     moveTo : function(x, y){
29131         this.xy = [x,y];
29132         if(this.isVisible()){
29133             this.el.setXY(this.xy);
29134             this.adjustAssets();
29135         }
29136         return this;
29137     },
29138
29139     /**
29140      * Aligns the dialog to the specified element
29141      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29142      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29143      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29144      * @return {Roo.BasicDialog} this
29145      */
29146     alignTo : function(element, position, offsets){
29147         this.xy = this.el.getAlignToXY(element, position, offsets);
29148         if(this.isVisible()){
29149             this.el.setXY(this.xy);
29150             this.adjustAssets();
29151         }
29152         return this;
29153     },
29154
29155     /**
29156      * Anchors an element to another element and realigns it when the window is resized.
29157      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29158      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29159      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29160      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29161      * is a number, it is used as the buffer delay (defaults to 50ms).
29162      * @return {Roo.BasicDialog} this
29163      */
29164     anchorTo : function(el, alignment, offsets, monitorScroll){
29165         var action = function(){
29166             this.alignTo(el, alignment, offsets);
29167         };
29168         Roo.EventManager.onWindowResize(action, this);
29169         var tm = typeof monitorScroll;
29170         if(tm != 'undefined'){
29171             Roo.EventManager.on(window, 'scroll', action, this,
29172                 {buffer: tm == 'number' ? monitorScroll : 50});
29173         }
29174         action.call(this);
29175         return this;
29176     },
29177
29178     /**
29179      * Returns true if the dialog is visible
29180      * @return {Boolean}
29181      */
29182     isVisible : function(){
29183         return this.el.isVisible();
29184     },
29185
29186     // private
29187     animHide : function(callback){
29188         var b = Roo.get(this.animateTarget).getBox();
29189         this.proxy.show();
29190         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29191         this.el.hide();
29192         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29193                     this.hideEl.createDelegate(this, [callback]));
29194     },
29195
29196     /**
29197      * Hides the dialog.
29198      * @param {Function} callback (optional) Function to call when the dialog is hidden
29199      * @return {Roo.BasicDialog} this
29200      */
29201     hide : function(callback){
29202         if (this.fireEvent("beforehide", this) === false){
29203             return;
29204         }
29205         if(this.shadow){
29206             this.shadow.hide();
29207         }
29208         if(this.shim) {
29209           this.shim.hide();
29210         }
29211         if(this.animateTarget){
29212            this.animHide(callback);
29213         }else{
29214             this.el.hide();
29215             this.hideEl(callback);
29216         }
29217         return this;
29218     },
29219
29220     // private
29221     hideEl : function(callback){
29222         this.proxy.hide();
29223         if(this.modal){
29224             this.mask.hide();
29225             Roo.get(document.body).removeClass("x-body-masked");
29226         }
29227         this.fireEvent("hide", this);
29228         if(typeof callback == "function"){
29229             callback();
29230         }
29231     },
29232
29233     // private
29234     hideAction : function(){
29235         this.setLeft("-10000px");
29236         this.setTop("-10000px");
29237         this.setStyle("visibility", "hidden");
29238     },
29239
29240     // private
29241     refreshSize : function(){
29242         this.size = this.el.getSize();
29243         this.xy = this.el.getXY();
29244         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29245     },
29246
29247     // private
29248     // z-index is managed by the DialogManager and may be overwritten at any time
29249     setZIndex : function(index){
29250         if(this.modal){
29251             this.mask.setStyle("z-index", index);
29252         }
29253         if(this.shim){
29254             this.shim.setStyle("z-index", ++index);
29255         }
29256         if(this.shadow){
29257             this.shadow.setZIndex(++index);
29258         }
29259         this.el.setStyle("z-index", ++index);
29260         if(this.proxy){
29261             this.proxy.setStyle("z-index", ++index);
29262         }
29263         if(this.resizer){
29264             this.resizer.proxy.setStyle("z-index", ++index);
29265         }
29266
29267         this.lastZIndex = index;
29268     },
29269
29270     /**
29271      * Returns the element for this dialog
29272      * @return {Roo.Element} The underlying dialog Element
29273      */
29274     getEl : function(){
29275         return this.el;
29276     }
29277 });
29278
29279 /**
29280  * @class Roo.DialogManager
29281  * Provides global access to BasicDialogs that have been created and
29282  * support for z-indexing (layering) multiple open dialogs.
29283  */
29284 Roo.DialogManager = function(){
29285     var list = {};
29286     var accessList = [];
29287     var front = null;
29288
29289     // private
29290     var sortDialogs = function(d1, d2){
29291         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29292     };
29293
29294     // private
29295     var orderDialogs = function(){
29296         accessList.sort(sortDialogs);
29297         var seed = Roo.DialogManager.zseed;
29298         for(var i = 0, len = accessList.length; i < len; i++){
29299             var dlg = accessList[i];
29300             if(dlg){
29301                 dlg.setZIndex(seed + (i*10));
29302             }
29303         }
29304     };
29305
29306     return {
29307         /**
29308          * The starting z-index for BasicDialogs (defaults to 9000)
29309          * @type Number The z-index value
29310          */
29311         zseed : 9000,
29312
29313         // private
29314         register : function(dlg){
29315             list[dlg.id] = dlg;
29316             accessList.push(dlg);
29317         },
29318
29319         // private
29320         unregister : function(dlg){
29321             delete list[dlg.id];
29322             var i=0;
29323             var len=0;
29324             if(!accessList.indexOf){
29325                 for(  i = 0, len = accessList.length; i < len; i++){
29326                     if(accessList[i] == dlg){
29327                         accessList.splice(i, 1);
29328                         return;
29329                     }
29330                 }
29331             }else{
29332                  i = accessList.indexOf(dlg);
29333                 if(i != -1){
29334                     accessList.splice(i, 1);
29335                 }
29336             }
29337         },
29338
29339         /**
29340          * Gets a registered dialog by id
29341          * @param {String/Object} id The id of the dialog or a dialog
29342          * @return {Roo.BasicDialog} this
29343          */
29344         get : function(id){
29345             return typeof id == "object" ? id : list[id];
29346         },
29347
29348         /**
29349          * Brings the specified dialog to the front
29350          * @param {String/Object} dlg The id of the dialog or a dialog
29351          * @return {Roo.BasicDialog} this
29352          */
29353         bringToFront : function(dlg){
29354             dlg = this.get(dlg);
29355             if(dlg != front){
29356                 front = dlg;
29357                 dlg._lastAccess = new Date().getTime();
29358                 orderDialogs();
29359             }
29360             return dlg;
29361         },
29362
29363         /**
29364          * Sends the specified dialog to the back
29365          * @param {String/Object} dlg The id of the dialog or a dialog
29366          * @return {Roo.BasicDialog} this
29367          */
29368         sendToBack : function(dlg){
29369             dlg = this.get(dlg);
29370             dlg._lastAccess = -(new Date().getTime());
29371             orderDialogs();
29372             return dlg;
29373         },
29374
29375         /**
29376          * Hides all dialogs
29377          */
29378         hideAll : function(){
29379             for(var id in list){
29380                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29381                     list[id].hide();
29382                 }
29383             }
29384         }
29385     };
29386 }();
29387
29388 /**
29389  * @class Roo.LayoutDialog
29390  * @extends Roo.BasicDialog
29391  * Dialog which provides adjustments for working with a layout in a Dialog.
29392  * Add your necessary layout config options to the dialog's config.<br>
29393  * Example usage (including a nested layout):
29394  * <pre><code>
29395 if(!dialog){
29396     dialog = new Roo.LayoutDialog("download-dlg", {
29397         modal: true,
29398         width:600,
29399         height:450,
29400         shadow:true,
29401         minWidth:500,
29402         minHeight:350,
29403         autoTabs:true,
29404         proxyDrag:true,
29405         // layout config merges with the dialog config
29406         center:{
29407             tabPosition: "top",
29408             alwaysShowTabs: true
29409         }
29410     });
29411     dialog.addKeyListener(27, dialog.hide, dialog);
29412     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29413     dialog.addButton("Build It!", this.getDownload, this);
29414
29415     // we can even add nested layouts
29416     var innerLayout = new Roo.BorderLayout("dl-inner", {
29417         east: {
29418             initialSize: 200,
29419             autoScroll:true,
29420             split:true
29421         },
29422         center: {
29423             autoScroll:true
29424         }
29425     });
29426     innerLayout.beginUpdate();
29427     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29428     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29429     innerLayout.endUpdate(true);
29430
29431     var layout = dialog.getLayout();
29432     layout.beginUpdate();
29433     layout.add("center", new Roo.ContentPanel("standard-panel",
29434                         {title: "Download the Source", fitToFrame:true}));
29435     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29436                {title: "Build your own roo.js"}));
29437     layout.getRegion("center").showPanel(sp);
29438     layout.endUpdate();
29439 }
29440 </code></pre>
29441     * @constructor
29442     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29443     * @param {Object} config configuration options
29444   */
29445 Roo.LayoutDialog = function(el, cfg){
29446     
29447     var config=  cfg;
29448     if (typeof(cfg) == 'undefined') {
29449         config = Roo.apply({}, el);
29450         el = Roo.get( document.documentElement || document.body).createChild();
29451         //config.autoCreate = true;
29452     }
29453     
29454     
29455     config.autoTabs = false;
29456     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29457     this.body.setStyle({overflow:"hidden", position:"relative"});
29458     this.layout = new Roo.BorderLayout(this.body.dom, config);
29459     this.layout.monitorWindowResize = false;
29460     this.el.addClass("x-dlg-auto-layout");
29461     // fix case when center region overwrites center function
29462     this.center = Roo.BasicDialog.prototype.center;
29463     this.on("show", this.layout.layout, this.layout, true);
29464     if (config.items) {
29465         var xitems = config.items;
29466         delete config.items;
29467         Roo.each(xitems, this.addxtype, this);
29468     }
29469     
29470     
29471 };
29472 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29473     /**
29474      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29475      * @deprecated
29476      */
29477     endUpdate : function(){
29478         this.layout.endUpdate();
29479     },
29480
29481     /**
29482      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29483      *  @deprecated
29484      */
29485     beginUpdate : function(){
29486         this.layout.beginUpdate();
29487     },
29488
29489     /**
29490      * Get the BorderLayout for this dialog
29491      * @return {Roo.BorderLayout}
29492      */
29493     getLayout : function(){
29494         return this.layout;
29495     },
29496
29497     showEl : function(){
29498         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29499         if(Roo.isIE7){
29500             this.layout.layout();
29501         }
29502     },
29503
29504     // private
29505     // Use the syncHeightBeforeShow config option to control this automatically
29506     syncBodyHeight : function(){
29507         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29508         if(this.layout){this.layout.layout();}
29509     },
29510     
29511       /**
29512      * Add an xtype element (actually adds to the layout.)
29513      * @return {Object} xdata xtype object data.
29514      */
29515     
29516     addxtype : function(c) {
29517         return this.layout.addxtype(c);
29518     }
29519 });/*
29520  * Based on:
29521  * Ext JS Library 1.1.1
29522  * Copyright(c) 2006-2007, Ext JS, LLC.
29523  *
29524  * Originally Released Under LGPL - original licence link has changed is not relivant.
29525  *
29526  * Fork - LGPL
29527  * <script type="text/javascript">
29528  */
29529  
29530 /**
29531  * @class Roo.MessageBox
29532  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29533  * Example usage:
29534  *<pre><code>
29535 // Basic alert:
29536 Roo.Msg.alert('Status', 'Changes saved successfully.');
29537
29538 // Prompt for user data:
29539 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29540     if (btn == 'ok'){
29541         // process text value...
29542     }
29543 });
29544
29545 // Show a dialog using config options:
29546 Roo.Msg.show({
29547    title:'Save Changes?',
29548    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29549    buttons: Roo.Msg.YESNOCANCEL,
29550    fn: processResult,
29551    animEl: 'elId'
29552 });
29553 </code></pre>
29554  * @singleton
29555  */
29556 Roo.MessageBox = function(){
29557     var dlg, opt, mask, waitTimer;
29558     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29559     var buttons, activeTextEl, bwidth;
29560
29561     // private
29562     var handleButton = function(button){
29563         dlg.hide();
29564         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29565     };
29566
29567     // private
29568     var handleHide = function(){
29569         if(opt && opt.cls){
29570             dlg.el.removeClass(opt.cls);
29571         }
29572         if(waitTimer){
29573             Roo.TaskMgr.stop(waitTimer);
29574             waitTimer = null;
29575         }
29576     };
29577
29578     // private
29579     var updateButtons = function(b){
29580         var width = 0;
29581         if(!b){
29582             buttons["ok"].hide();
29583             buttons["cancel"].hide();
29584             buttons["yes"].hide();
29585             buttons["no"].hide();
29586             dlg.footer.dom.style.display = 'none';
29587             return width;
29588         }
29589         dlg.footer.dom.style.display = '';
29590         for(var k in buttons){
29591             if(typeof buttons[k] != "function"){
29592                 if(b[k]){
29593                     buttons[k].show();
29594                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29595                     width += buttons[k].el.getWidth()+15;
29596                 }else{
29597                     buttons[k].hide();
29598                 }
29599             }
29600         }
29601         return width;
29602     };
29603
29604     // private
29605     var handleEsc = function(d, k, e){
29606         if(opt && opt.closable !== false){
29607             dlg.hide();
29608         }
29609         if(e){
29610             e.stopEvent();
29611         }
29612     };
29613
29614     return {
29615         /**
29616          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29617          * @return {Roo.BasicDialog} The BasicDialog element
29618          */
29619         getDialog : function(){
29620            if(!dlg){
29621                 dlg = new Roo.BasicDialog("x-msg-box", {
29622                     autoCreate : true,
29623                     shadow: true,
29624                     draggable: true,
29625                     resizable:false,
29626                     constraintoviewport:false,
29627                     fixedcenter:true,
29628                     collapsible : false,
29629                     shim:true,
29630                     modal: true,
29631                     width:400, height:100,
29632                     buttonAlign:"center",
29633                     closeClick : function(){
29634                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29635                             handleButton("no");
29636                         }else{
29637                             handleButton("cancel");
29638                         }
29639                     }
29640                 });
29641                 dlg.on("hide", handleHide);
29642                 mask = dlg.mask;
29643                 dlg.addKeyListener(27, handleEsc);
29644                 buttons = {};
29645                 var bt = this.buttonText;
29646                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29647                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29648                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29649                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29650                 bodyEl = dlg.body.createChild({
29651
29652                     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>'
29653                 });
29654                 msgEl = bodyEl.dom.firstChild;
29655                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29656                 textboxEl.enableDisplayMode();
29657                 textboxEl.addKeyListener([10,13], function(){
29658                     if(dlg.isVisible() && opt && opt.buttons){
29659                         if(opt.buttons.ok){
29660                             handleButton("ok");
29661                         }else if(opt.buttons.yes){
29662                             handleButton("yes");
29663                         }
29664                     }
29665                 });
29666                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29667                 textareaEl.enableDisplayMode();
29668                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29669                 progressEl.enableDisplayMode();
29670                 var pf = progressEl.dom.firstChild;
29671                 if (pf) {
29672                     pp = Roo.get(pf.firstChild);
29673                     pp.setHeight(pf.offsetHeight);
29674                 }
29675                 
29676             }
29677             return dlg;
29678         },
29679
29680         /**
29681          * Updates the message box body text
29682          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29683          * the XHTML-compliant non-breaking space character '&amp;#160;')
29684          * @return {Roo.MessageBox} This message box
29685          */
29686         updateText : function(text){
29687             if(!dlg.isVisible() && !opt.width){
29688                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29689             }
29690             msgEl.innerHTML = text || '&#160;';
29691             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29692                         Math.max(opt.minWidth || this.minWidth, bwidth));
29693             if(opt.prompt){
29694                 activeTextEl.setWidth(w);
29695             }
29696             if(dlg.isVisible()){
29697                 dlg.fixedcenter = false;
29698             }
29699             dlg.setContentSize(w, bodyEl.getHeight());
29700             if(dlg.isVisible()){
29701                 dlg.fixedcenter = true;
29702             }
29703             return this;
29704         },
29705
29706         /**
29707          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29708          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29709          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29710          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29711          * @return {Roo.MessageBox} This message box
29712          */
29713         updateProgress : function(value, text){
29714             if(text){
29715                 this.updateText(text);
29716             }
29717             if (pp) { // weird bug on my firefox - for some reason this is not defined
29718                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29719             }
29720             return this;
29721         },        
29722
29723         /**
29724          * Returns true if the message box is currently displayed
29725          * @return {Boolean} True if the message box is visible, else false
29726          */
29727         isVisible : function(){
29728             return dlg && dlg.isVisible();  
29729         },
29730
29731         /**
29732          * Hides the message box if it is displayed
29733          */
29734         hide : function(){
29735             if(this.isVisible()){
29736                 dlg.hide();
29737             }  
29738         },
29739
29740         /**
29741          * Displays a new message box, or reinitializes an existing message box, based on the config options
29742          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29743          * The following config object properties are supported:
29744          * <pre>
29745 Property    Type             Description
29746 ----------  ---------------  ------------------------------------------------------------------------------------
29747 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29748                                    closes (defaults to undefined)
29749 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29750                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29751 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29752                                    progress and wait dialogs will ignore this property and always hide the
29753                                    close button as they can only be closed programmatically.
29754 cls               String           A custom CSS class to apply to the message box element
29755 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29756                                    displayed (defaults to 75)
29757 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29758                                    function will be btn (the name of the button that was clicked, if applicable,
29759                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29760                                    Progress and wait dialogs will ignore this option since they do not respond to
29761                                    user actions and can only be closed programmatically, so any required function
29762                                    should be called by the same code after it closes the dialog.
29763 icon              String           A CSS class that provides a background image to be used as an icon for
29764                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29765 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29766 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29767 modal             Boolean          False to allow user interaction with the page while the message box is
29768                                    displayed (defaults to true)
29769 msg               String           A string that will replace the existing message box body text (defaults
29770                                    to the XHTML-compliant non-breaking space character '&#160;')
29771 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29772 progress          Boolean          True to display a progress bar (defaults to false)
29773 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29774 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29775 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29776 title             String           The title text
29777 value             String           The string value to set into the active textbox element if displayed
29778 wait              Boolean          True to display a progress bar (defaults to false)
29779 width             Number           The width of the dialog in pixels
29780 </pre>
29781          *
29782          * Example usage:
29783          * <pre><code>
29784 Roo.Msg.show({
29785    title: 'Address',
29786    msg: 'Please enter your address:',
29787    width: 300,
29788    buttons: Roo.MessageBox.OKCANCEL,
29789    multiline: true,
29790    fn: saveAddress,
29791    animEl: 'addAddressBtn'
29792 });
29793 </code></pre>
29794          * @param {Object} config Configuration options
29795          * @return {Roo.MessageBox} This message box
29796          */
29797         show : function(options){
29798             if(this.isVisible()){
29799                 this.hide();
29800             }
29801             var d = this.getDialog();
29802             opt = options;
29803             d.setTitle(opt.title || "&#160;");
29804             d.close.setDisplayed(opt.closable !== false);
29805             activeTextEl = textboxEl;
29806             opt.prompt = opt.prompt || (opt.multiline ? true : false);
29807             if(opt.prompt){
29808                 if(opt.multiline){
29809                     textboxEl.hide();
29810                     textareaEl.show();
29811                     textareaEl.setHeight(typeof opt.multiline == "number" ?
29812                         opt.multiline : this.defaultTextHeight);
29813                     activeTextEl = textareaEl;
29814                 }else{
29815                     textboxEl.show();
29816                     textareaEl.hide();
29817                 }
29818             }else{
29819                 textboxEl.hide();
29820                 textareaEl.hide();
29821             }
29822             progressEl.setDisplayed(opt.progress === true);
29823             this.updateProgress(0);
29824             activeTextEl.dom.value = opt.value || "";
29825             if(opt.prompt){
29826                 dlg.setDefaultButton(activeTextEl);
29827             }else{
29828                 var bs = opt.buttons;
29829                 var db = null;
29830                 if(bs && bs.ok){
29831                     db = buttons["ok"];
29832                 }else if(bs && bs.yes){
29833                     db = buttons["yes"];
29834                 }
29835                 dlg.setDefaultButton(db);
29836             }
29837             bwidth = updateButtons(opt.buttons);
29838             this.updateText(opt.msg);
29839             if(opt.cls){
29840                 d.el.addClass(opt.cls);
29841             }
29842             d.proxyDrag = opt.proxyDrag === true;
29843             d.modal = opt.modal !== false;
29844             d.mask = opt.modal !== false ? mask : false;
29845             if(!d.isVisible()){
29846                 // force it to the end of the z-index stack so it gets a cursor in FF
29847                 document.body.appendChild(dlg.el.dom);
29848                 d.animateTarget = null;
29849                 d.show(options.animEl);
29850             }
29851             return this;
29852         },
29853
29854         /**
29855          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
29856          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
29857          * and closing the message box when the process is complete.
29858          * @param {String} title The title bar text
29859          * @param {String} msg The message box body text
29860          * @return {Roo.MessageBox} This message box
29861          */
29862         progress : function(title, msg){
29863             this.show({
29864                 title : title,
29865                 msg : msg,
29866                 buttons: false,
29867                 progress:true,
29868                 closable:false,
29869                 minWidth: this.minProgressWidth,
29870                 modal : true
29871             });
29872             return this;
29873         },
29874
29875         /**
29876          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
29877          * If a callback function is passed it will be called after the user clicks the button, and the
29878          * id of the button that was clicked will be passed as the only parameter to the callback
29879          * (could also be the top-right close button).
29880          * @param {String} title The title bar text
29881          * @param {String} msg The message box body text
29882          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29883          * @param {Object} scope (optional) The scope of the callback function
29884          * @return {Roo.MessageBox} This message box
29885          */
29886         alert : function(title, msg, fn, scope){
29887             this.show({
29888                 title : title,
29889                 msg : msg,
29890                 buttons: this.OK,
29891                 fn: fn,
29892                 scope : scope,
29893                 modal : true
29894             });
29895             return this;
29896         },
29897
29898         /**
29899          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
29900          * interaction while waiting for a long-running process to complete that does not have defined intervals.
29901          * You are responsible for closing the message box when the process is complete.
29902          * @param {String} msg The message box body text
29903          * @param {String} title (optional) The title bar text
29904          * @return {Roo.MessageBox} This message box
29905          */
29906         wait : function(msg, title){
29907             this.show({
29908                 title : title,
29909                 msg : msg,
29910                 buttons: false,
29911                 closable:false,
29912                 progress:true,
29913                 modal:true,
29914                 width:300,
29915                 wait:true
29916             });
29917             waitTimer = Roo.TaskMgr.start({
29918                 run: function(i){
29919                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
29920                 },
29921                 interval: 1000
29922             });
29923             return this;
29924         },
29925
29926         /**
29927          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
29928          * If a callback function is passed it will be called after the user clicks either button, and the id of the
29929          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
29930          * @param {String} title The title bar text
29931          * @param {String} msg The message box body text
29932          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29933          * @param {Object} scope (optional) The scope of the callback function
29934          * @return {Roo.MessageBox} This message box
29935          */
29936         confirm : function(title, msg, fn, scope){
29937             this.show({
29938                 title : title,
29939                 msg : msg,
29940                 buttons: this.YESNO,
29941                 fn: fn,
29942                 scope : scope,
29943                 modal : true
29944             });
29945             return this;
29946         },
29947
29948         /**
29949          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
29950          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
29951          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
29952          * (could also be the top-right close button) and the text that was entered will be passed as the two
29953          * parameters to the callback.
29954          * @param {String} title The title bar text
29955          * @param {String} msg The message box body text
29956          * @param {Function} fn (optional) The callback function invoked after the message box is closed
29957          * @param {Object} scope (optional) The scope of the callback function
29958          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
29959          * property, or the height in pixels to create the textbox (defaults to false / single-line)
29960          * @return {Roo.MessageBox} This message box
29961          */
29962         prompt : function(title, msg, fn, scope, multiline){
29963             this.show({
29964                 title : title,
29965                 msg : msg,
29966                 buttons: this.OKCANCEL,
29967                 fn: fn,
29968                 minWidth:250,
29969                 scope : scope,
29970                 prompt:true,
29971                 multiline: multiline,
29972                 modal : true
29973             });
29974             return this;
29975         },
29976
29977         /**
29978          * Button config that displays a single OK button
29979          * @type Object
29980          */
29981         OK : {ok:true},
29982         /**
29983          * Button config that displays Yes and No buttons
29984          * @type Object
29985          */
29986         YESNO : {yes:true, no:true},
29987         /**
29988          * Button config that displays OK and Cancel buttons
29989          * @type Object
29990          */
29991         OKCANCEL : {ok:true, cancel:true},
29992         /**
29993          * Button config that displays Yes, No and Cancel buttons
29994          * @type Object
29995          */
29996         YESNOCANCEL : {yes:true, no:true, cancel:true},
29997
29998         /**
29999          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30000          * @type Number
30001          */
30002         defaultTextHeight : 75,
30003         /**
30004          * The maximum width in pixels of the message box (defaults to 600)
30005          * @type Number
30006          */
30007         maxWidth : 600,
30008         /**
30009          * The minimum width in pixels of the message box (defaults to 100)
30010          * @type Number
30011          */
30012         minWidth : 100,
30013         /**
30014          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30015          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30016          * @type Number
30017          */
30018         minProgressWidth : 250,
30019         /**
30020          * An object containing the default button text strings that can be overriden for localized language support.
30021          * Supported properties are: ok, cancel, yes and no.
30022          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30023          * @type Object
30024          */
30025         buttonText : {
30026             ok : "OK",
30027             cancel : "Cancel",
30028             yes : "Yes",
30029             no : "No"
30030         }
30031     };
30032 }();
30033
30034 /**
30035  * Shorthand for {@link Roo.MessageBox}
30036  */
30037 Roo.Msg = Roo.MessageBox;/*
30038  * Based on:
30039  * Ext JS Library 1.1.1
30040  * Copyright(c) 2006-2007, Ext JS, LLC.
30041  *
30042  * Originally Released Under LGPL - original licence link has changed is not relivant.
30043  *
30044  * Fork - LGPL
30045  * <script type="text/javascript">
30046  */
30047 /**
30048  * @class Roo.QuickTips
30049  * Provides attractive and customizable tooltips for any element.
30050  * @singleton
30051  */
30052 Roo.QuickTips = function(){
30053     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30054     var ce, bd, xy, dd;
30055     var visible = false, disabled = true, inited = false;
30056     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30057     
30058     var onOver = function(e){
30059         if(disabled){
30060             return;
30061         }
30062         var t = e.getTarget();
30063         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30064             return;
30065         }
30066         if(ce && t == ce.el){
30067             clearTimeout(hideProc);
30068             return;
30069         }
30070         if(t && tagEls[t.id]){
30071             tagEls[t.id].el = t;
30072             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30073             return;
30074         }
30075         var ttp, et = Roo.fly(t);
30076         var ns = cfg.namespace;
30077         if(tm.interceptTitles && t.title){
30078             ttp = t.title;
30079             t.qtip = ttp;
30080             t.removeAttribute("title");
30081             e.preventDefault();
30082         }else{
30083             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30084         }
30085         if(ttp){
30086             showProc = show.defer(tm.showDelay, tm, [{
30087                 el: t, 
30088                 text: ttp, 
30089                 width: et.getAttributeNS(ns, cfg.width),
30090                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30091                 title: et.getAttributeNS(ns, cfg.title),
30092                     cls: et.getAttributeNS(ns, cfg.cls)
30093             }]);
30094         }
30095     };
30096     
30097     var onOut = function(e){
30098         clearTimeout(showProc);
30099         var t = e.getTarget();
30100         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30101             hideProc = setTimeout(hide, tm.hideDelay);
30102         }
30103     };
30104     
30105     var onMove = function(e){
30106         if(disabled){
30107             return;
30108         }
30109         xy = e.getXY();
30110         xy[1] += 18;
30111         if(tm.trackMouse && ce){
30112             el.setXY(xy);
30113         }
30114     };
30115     
30116     var onDown = function(e){
30117         clearTimeout(showProc);
30118         clearTimeout(hideProc);
30119         if(!e.within(el)){
30120             if(tm.hideOnClick){
30121                 hide();
30122                 tm.disable();
30123                 tm.enable.defer(100, tm);
30124             }
30125         }
30126     };
30127     
30128     var getPad = function(){
30129         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30130     };
30131
30132     var show = function(o){
30133         if(disabled){
30134             return;
30135         }
30136         clearTimeout(dismissProc);
30137         ce = o;
30138         if(removeCls){ // in case manually hidden
30139             el.removeClass(removeCls);
30140             removeCls = null;
30141         }
30142         if(ce.cls){
30143             el.addClass(ce.cls);
30144             removeCls = ce.cls;
30145         }
30146         if(ce.title){
30147             tipTitle.update(ce.title);
30148             tipTitle.show();
30149         }else{
30150             tipTitle.update('');
30151             tipTitle.hide();
30152         }
30153         el.dom.style.width  = tm.maxWidth+'px';
30154         //tipBody.dom.style.width = '';
30155         tipBodyText.update(o.text);
30156         var p = getPad(), w = ce.width;
30157         if(!w){
30158             var td = tipBodyText.dom;
30159             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30160             if(aw > tm.maxWidth){
30161                 w = tm.maxWidth;
30162             }else if(aw < tm.minWidth){
30163                 w = tm.minWidth;
30164             }else{
30165                 w = aw;
30166             }
30167         }
30168         //tipBody.setWidth(w);
30169         el.setWidth(parseInt(w, 10) + p);
30170         if(ce.autoHide === false){
30171             close.setDisplayed(true);
30172             if(dd){
30173                 dd.unlock();
30174             }
30175         }else{
30176             close.setDisplayed(false);
30177             if(dd){
30178                 dd.lock();
30179             }
30180         }
30181         if(xy){
30182             el.avoidY = xy[1]-18;
30183             el.setXY(xy);
30184         }
30185         if(tm.animate){
30186             el.setOpacity(.1);
30187             el.setStyle("visibility", "visible");
30188             el.fadeIn({callback: afterShow});
30189         }else{
30190             afterShow();
30191         }
30192     };
30193     
30194     var afterShow = function(){
30195         if(ce){
30196             el.show();
30197             esc.enable();
30198             if(tm.autoDismiss && ce.autoHide !== false){
30199                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30200             }
30201         }
30202     };
30203     
30204     var hide = function(noanim){
30205         clearTimeout(dismissProc);
30206         clearTimeout(hideProc);
30207         ce = null;
30208         if(el.isVisible()){
30209             esc.disable();
30210             if(noanim !== true && tm.animate){
30211                 el.fadeOut({callback: afterHide});
30212             }else{
30213                 afterHide();
30214             } 
30215         }
30216     };
30217     
30218     var afterHide = function(){
30219         el.hide();
30220         if(removeCls){
30221             el.removeClass(removeCls);
30222             removeCls = null;
30223         }
30224     };
30225     
30226     return {
30227         /**
30228         * @cfg {Number} minWidth
30229         * The minimum width of the quick tip (defaults to 40)
30230         */
30231        minWidth : 40,
30232         /**
30233         * @cfg {Number} maxWidth
30234         * The maximum width of the quick tip (defaults to 300)
30235         */
30236        maxWidth : 300,
30237         /**
30238         * @cfg {Boolean} interceptTitles
30239         * True to automatically use the element's DOM title value if available (defaults to false)
30240         */
30241        interceptTitles : false,
30242         /**
30243         * @cfg {Boolean} trackMouse
30244         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30245         */
30246        trackMouse : false,
30247         /**
30248         * @cfg {Boolean} hideOnClick
30249         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30250         */
30251        hideOnClick : true,
30252         /**
30253         * @cfg {Number} showDelay
30254         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30255         */
30256        showDelay : 500,
30257         /**
30258         * @cfg {Number} hideDelay
30259         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30260         */
30261        hideDelay : 200,
30262         /**
30263         * @cfg {Boolean} autoHide
30264         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30265         * Used in conjunction with hideDelay.
30266         */
30267        autoHide : true,
30268         /**
30269         * @cfg {Boolean}
30270         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30271         * (defaults to true).  Used in conjunction with autoDismissDelay.
30272         */
30273        autoDismiss : true,
30274         /**
30275         * @cfg {Number}
30276         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30277         */
30278        autoDismissDelay : 5000,
30279        /**
30280         * @cfg {Boolean} animate
30281         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30282         */
30283        animate : false,
30284
30285        /**
30286         * @cfg {String} title
30287         * Title text to display (defaults to '').  This can be any valid HTML markup.
30288         */
30289         title: '',
30290        /**
30291         * @cfg {String} text
30292         * Body text to display (defaults to '').  This can be any valid HTML markup.
30293         */
30294         text : '',
30295        /**
30296         * @cfg {String} cls
30297         * A CSS class to apply to the base quick tip element (defaults to '').
30298         */
30299         cls : '',
30300        /**
30301         * @cfg {Number} width
30302         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30303         * minWidth or maxWidth.
30304         */
30305         width : null,
30306
30307     /**
30308      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30309      * or display QuickTips in a page.
30310      */
30311        init : function(){
30312           tm = Roo.QuickTips;
30313           cfg = tm.tagConfig;
30314           if(!inited){
30315               if(!Roo.isReady){ // allow calling of init() before onReady
30316                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30317                   return;
30318               }
30319               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30320               el.fxDefaults = {stopFx: true};
30321               // maximum custom styling
30322               //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>');
30323               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>');              
30324               tipTitle = el.child('h3');
30325               tipTitle.enableDisplayMode("block");
30326               tipBody = el.child('div.x-tip-bd');
30327               tipBodyText = el.child('div.x-tip-bd-inner');
30328               //bdLeft = el.child('div.x-tip-bd-left');
30329               //bdRight = el.child('div.x-tip-bd-right');
30330               close = el.child('div.x-tip-close');
30331               close.enableDisplayMode("block");
30332               close.on("click", hide);
30333               var d = Roo.get(document);
30334               d.on("mousedown", onDown);
30335               d.on("mouseover", onOver);
30336               d.on("mouseout", onOut);
30337               d.on("mousemove", onMove);
30338               esc = d.addKeyListener(27, hide);
30339               esc.disable();
30340               if(Roo.dd.DD){
30341                   dd = el.initDD("default", null, {
30342                       onDrag : function(){
30343                           el.sync();  
30344                       }
30345                   });
30346                   dd.setHandleElId(tipTitle.id);
30347                   dd.lock();
30348               }
30349               inited = true;
30350           }
30351           this.enable(); 
30352        },
30353
30354     /**
30355      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30356      * are supported:
30357      * <pre>
30358 Property    Type                   Description
30359 ----------  ---------------------  ------------------------------------------------------------------------
30360 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30361      * </ul>
30362      * @param {Object} config The config object
30363      */
30364        register : function(config){
30365            var cs = config instanceof Array ? config : arguments;
30366            for(var i = 0, len = cs.length; i < len; i++) {
30367                var c = cs[i];
30368                var target = c.target;
30369                if(target){
30370                    if(target instanceof Array){
30371                        for(var j = 0, jlen = target.length; j < jlen; j++){
30372                            tagEls[target[j]] = c;
30373                        }
30374                    }else{
30375                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30376                    }
30377                }
30378            }
30379        },
30380
30381     /**
30382      * Removes this quick tip from its element and destroys it.
30383      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30384      */
30385        unregister : function(el){
30386            delete tagEls[Roo.id(el)];
30387        },
30388
30389     /**
30390      * Enable this quick tip.
30391      */
30392        enable : function(){
30393            if(inited && disabled){
30394                locks.pop();
30395                if(locks.length < 1){
30396                    disabled = false;
30397                }
30398            }
30399        },
30400
30401     /**
30402      * Disable this quick tip.
30403      */
30404        disable : function(){
30405           disabled = true;
30406           clearTimeout(showProc);
30407           clearTimeout(hideProc);
30408           clearTimeout(dismissProc);
30409           if(ce){
30410               hide(true);
30411           }
30412           locks.push(1);
30413        },
30414
30415     /**
30416      * Returns true if the quick tip is enabled, else false.
30417      */
30418        isEnabled : function(){
30419             return !disabled;
30420        },
30421
30422         // private
30423        tagConfig : {
30424            namespace : "ext",
30425            attribute : "qtip",
30426            width : "width",
30427            target : "target",
30428            title : "qtitle",
30429            hide : "hide",
30430            cls : "qclass"
30431        }
30432    };
30433 }();
30434
30435 // backwards compat
30436 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30437  * Based on:
30438  * Ext JS Library 1.1.1
30439  * Copyright(c) 2006-2007, Ext JS, LLC.
30440  *
30441  * Originally Released Under LGPL - original licence link has changed is not relivant.
30442  *
30443  * Fork - LGPL
30444  * <script type="text/javascript">
30445  */
30446  
30447
30448 /**
30449  * @class Roo.tree.TreePanel
30450  * @extends Roo.data.Tree
30451
30452  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30453  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30454  * @cfg {Boolean} enableDD true to enable drag and drop
30455  * @cfg {Boolean} enableDrag true to enable just drag
30456  * @cfg {Boolean} enableDrop true to enable just drop
30457  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30458  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30459  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30460  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30461  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30462  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30463  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30464  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30465  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30466  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30467  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30468  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30469  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30470  * @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>
30471  * @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>
30472  * 
30473  * @constructor
30474  * @param {String/HTMLElement/Element} el The container element
30475  * @param {Object} config
30476  */
30477 Roo.tree.TreePanel = function(el, config){
30478     var root = false;
30479     var loader = false;
30480     if (config.root) {
30481         root = config.root;
30482         delete config.root;
30483     }
30484     if (config.loader) {
30485         loader = config.loader;
30486         delete config.loader;
30487     }
30488     
30489     Roo.apply(this, config);
30490     Roo.tree.TreePanel.superclass.constructor.call(this);
30491     this.el = Roo.get(el);
30492     this.el.addClass('x-tree');
30493     //console.log(root);
30494     if (root) {
30495         this.setRootNode( Roo.factory(root, Roo.tree));
30496     }
30497     if (loader) {
30498         this.loader = Roo.factory(loader, Roo.tree);
30499     }
30500    /**
30501     * Read-only. The id of the container element becomes this TreePanel's id.
30502     */
30503    this.id = this.el.id;
30504    this.addEvents({
30505         /**
30506         * @event beforeload
30507         * Fires before a node is loaded, return false to cancel
30508         * @param {Node} node The node being loaded
30509         */
30510         "beforeload" : true,
30511         /**
30512         * @event load
30513         * Fires when a node is loaded
30514         * @param {Node} node The node that was loaded
30515         */
30516         "load" : true,
30517         /**
30518         * @event textchange
30519         * Fires when the text for a node is changed
30520         * @param {Node} node The node
30521         * @param {String} text The new text
30522         * @param {String} oldText The old text
30523         */
30524         "textchange" : true,
30525         /**
30526         * @event beforeexpand
30527         * Fires before a node is expanded, return false to cancel.
30528         * @param {Node} node The node
30529         * @param {Boolean} deep
30530         * @param {Boolean} anim
30531         */
30532         "beforeexpand" : true,
30533         /**
30534         * @event beforecollapse
30535         * Fires before a node is collapsed, return false to cancel.
30536         * @param {Node} node The node
30537         * @param {Boolean} deep
30538         * @param {Boolean} anim
30539         */
30540         "beforecollapse" : true,
30541         /**
30542         * @event expand
30543         * Fires when a node is expanded
30544         * @param {Node} node The node
30545         */
30546         "expand" : true,
30547         /**
30548         * @event disabledchange
30549         * Fires when the disabled status of a node changes
30550         * @param {Node} node The node
30551         * @param {Boolean} disabled
30552         */
30553         "disabledchange" : true,
30554         /**
30555         * @event collapse
30556         * Fires when a node is collapsed
30557         * @param {Node} node The node
30558         */
30559         "collapse" : true,
30560         /**
30561         * @event beforeclick
30562         * Fires before click processing on a node. Return false to cancel the default action.
30563         * @param {Node} node The node
30564         * @param {Roo.EventObject} e The event object
30565         */
30566         "beforeclick":true,
30567         /**
30568         * @event checkchange
30569         * Fires when a node with a checkbox's checked property changes
30570         * @param {Node} this This node
30571         * @param {Boolean} checked
30572         */
30573         "checkchange":true,
30574         /**
30575         * @event click
30576         * Fires when a node is clicked
30577         * @param {Node} node The node
30578         * @param {Roo.EventObject} e The event object
30579         */
30580         "click":true,
30581         /**
30582         * @event dblclick
30583         * Fires when a node is double clicked
30584         * @param {Node} node The node
30585         * @param {Roo.EventObject} e The event object
30586         */
30587         "dblclick":true,
30588         /**
30589         * @event contextmenu
30590         * Fires when a node is right clicked
30591         * @param {Node} node The node
30592         * @param {Roo.EventObject} e The event object
30593         */
30594         "contextmenu":true,
30595         /**
30596         * @event beforechildrenrendered
30597         * Fires right before the child nodes for a node are rendered
30598         * @param {Node} node The node
30599         */
30600         "beforechildrenrendered":true,
30601        /**
30602              * @event startdrag
30603              * Fires when a node starts being dragged
30604              * @param {Roo.tree.TreePanel} this
30605              * @param {Roo.tree.TreeNode} node
30606              * @param {event} e The raw browser event
30607              */ 
30608             "startdrag" : true,
30609             /**
30610              * @event enddrag
30611              * Fires when a drag operation is complete
30612              * @param {Roo.tree.TreePanel} this
30613              * @param {Roo.tree.TreeNode} node
30614              * @param {event} e The raw browser event
30615              */
30616             "enddrag" : true,
30617             /**
30618              * @event dragdrop
30619              * Fires when a dragged node is dropped on a valid DD target
30620              * @param {Roo.tree.TreePanel} this
30621              * @param {Roo.tree.TreeNode} node
30622              * @param {DD} dd The dd it was dropped on
30623              * @param {event} e The raw browser event
30624              */
30625             "dragdrop" : true,
30626             /**
30627              * @event beforenodedrop
30628              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30629              * passed to handlers has the following properties:<br />
30630              * <ul style="padding:5px;padding-left:16px;">
30631              * <li>tree - The TreePanel</li>
30632              * <li>target - The node being targeted for the drop</li>
30633              * <li>data - The drag data from the drag source</li>
30634              * <li>point - The point of the drop - append, above or below</li>
30635              * <li>source - The drag source</li>
30636              * <li>rawEvent - Raw mouse event</li>
30637              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30638              * to be inserted by setting them on this object.</li>
30639              * <li>cancel - Set this to true to cancel the drop.</li>
30640              * </ul>
30641              * @param {Object} dropEvent
30642              */
30643             "beforenodedrop" : true,
30644             /**
30645              * @event nodedrop
30646              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30647              * passed to handlers has the following properties:<br />
30648              * <ul style="padding:5px;padding-left:16px;">
30649              * <li>tree - The TreePanel</li>
30650              * <li>target - The node being targeted for the drop</li>
30651              * <li>data - The drag data from the drag source</li>
30652              * <li>point - The point of the drop - append, above or below</li>
30653              * <li>source - The drag source</li>
30654              * <li>rawEvent - Raw mouse event</li>
30655              * <li>dropNode - Dropped node(s).</li>
30656              * </ul>
30657              * @param {Object} dropEvent
30658              */
30659             "nodedrop" : true,
30660              /**
30661              * @event nodedragover
30662              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30663              * passed to handlers has the following properties:<br />
30664              * <ul style="padding:5px;padding-left:16px;">
30665              * <li>tree - The TreePanel</li>
30666              * <li>target - The node being targeted for the drop</li>
30667              * <li>data - The drag data from the drag source</li>
30668              * <li>point - The point of the drop - append, above or below</li>
30669              * <li>source - The drag source</li>
30670              * <li>rawEvent - Raw mouse event</li>
30671              * <li>dropNode - Drop node(s) provided by the source.</li>
30672              * <li>cancel - Set this to true to signal drop not allowed.</li>
30673              * </ul>
30674              * @param {Object} dragOverEvent
30675              */
30676             "nodedragover" : true
30677         
30678    });
30679    if(this.singleExpand){
30680        this.on("beforeexpand", this.restrictExpand, this);
30681    }
30682 };
30683 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30684     rootVisible : true,
30685     animate: Roo.enableFx,
30686     lines : true,
30687     enableDD : false,
30688     hlDrop : Roo.enableFx,
30689   
30690     renderer: false,
30691     
30692     rendererTip: false,
30693     // private
30694     restrictExpand : function(node){
30695         var p = node.parentNode;
30696         if(p){
30697             if(p.expandedChild && p.expandedChild.parentNode == p){
30698                 p.expandedChild.collapse();
30699             }
30700             p.expandedChild = node;
30701         }
30702     },
30703
30704     // private override
30705     setRootNode : function(node){
30706         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30707         if(!this.rootVisible){
30708             node.ui = new Roo.tree.RootTreeNodeUI(node);
30709         }
30710         return node;
30711     },
30712
30713     /**
30714      * Returns the container element for this TreePanel
30715      */
30716     getEl : function(){
30717         return this.el;
30718     },
30719
30720     /**
30721      * Returns the default TreeLoader for this TreePanel
30722      */
30723     getLoader : function(){
30724         return this.loader;
30725     },
30726
30727     /**
30728      * Expand all nodes
30729      */
30730     expandAll : function(){
30731         this.root.expand(true);
30732     },
30733
30734     /**
30735      * Collapse all nodes
30736      */
30737     collapseAll : function(){
30738         this.root.collapse(true);
30739     },
30740
30741     /**
30742      * Returns the selection model used by this TreePanel
30743      */
30744     getSelectionModel : function(){
30745         if(!this.selModel){
30746             this.selModel = new Roo.tree.DefaultSelectionModel();
30747         }
30748         return this.selModel;
30749     },
30750
30751     /**
30752      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30753      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30754      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30755      * @return {Array}
30756      */
30757     getChecked : function(a, startNode){
30758         startNode = startNode || this.root;
30759         var r = [];
30760         var f = function(){
30761             if(this.attributes.checked){
30762                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30763             }
30764         }
30765         startNode.cascade(f);
30766         return r;
30767     },
30768
30769     /**
30770      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30771      * @param {String} path
30772      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30773      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30774      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30775      */
30776     expandPath : function(path, attr, callback){
30777         attr = attr || "id";
30778         var keys = path.split(this.pathSeparator);
30779         var curNode = this.root;
30780         if(curNode.attributes[attr] != keys[1]){ // invalid root
30781             if(callback){
30782                 callback(false, null);
30783             }
30784             return;
30785         }
30786         var index = 1;
30787         var f = function(){
30788             if(++index == keys.length){
30789                 if(callback){
30790                     callback(true, curNode);
30791                 }
30792                 return;
30793             }
30794             var c = curNode.findChild(attr, keys[index]);
30795             if(!c){
30796                 if(callback){
30797                     callback(false, curNode);
30798                 }
30799                 return;
30800             }
30801             curNode = c;
30802             c.expand(false, false, f);
30803         };
30804         curNode.expand(false, false, f);
30805     },
30806
30807     /**
30808      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30809      * @param {String} path
30810      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30811      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
30812      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
30813      */
30814     selectPath : function(path, attr, callback){
30815         attr = attr || "id";
30816         var keys = path.split(this.pathSeparator);
30817         var v = keys.pop();
30818         if(keys.length > 0){
30819             var f = function(success, node){
30820                 if(success && node){
30821                     var n = node.findChild(attr, v);
30822                     if(n){
30823                         n.select();
30824                         if(callback){
30825                             callback(true, n);
30826                         }
30827                     }else if(callback){
30828                         callback(false, n);
30829                     }
30830                 }else{
30831                     if(callback){
30832                         callback(false, n);
30833                     }
30834                 }
30835             };
30836             this.expandPath(keys.join(this.pathSeparator), attr, f);
30837         }else{
30838             this.root.select();
30839             if(callback){
30840                 callback(true, this.root);
30841             }
30842         }
30843     },
30844
30845     getTreeEl : function(){
30846         return this.el;
30847     },
30848
30849     /**
30850      * Trigger rendering of this TreePanel
30851      */
30852     render : function(){
30853         if (this.innerCt) {
30854             return this; // stop it rendering more than once!!
30855         }
30856         
30857         this.innerCt = this.el.createChild({tag:"ul",
30858                cls:"x-tree-root-ct " +
30859                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
30860
30861         if(this.containerScroll){
30862             Roo.dd.ScrollManager.register(this.el);
30863         }
30864         if((this.enableDD || this.enableDrop) && !this.dropZone){
30865            /**
30866             * The dropZone used by this tree if drop is enabled
30867             * @type Roo.tree.TreeDropZone
30868             */
30869              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
30870                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
30871            });
30872         }
30873         if((this.enableDD || this.enableDrag) && !this.dragZone){
30874            /**
30875             * The dragZone used by this tree if drag is enabled
30876             * @type Roo.tree.TreeDragZone
30877             */
30878             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
30879                ddGroup: this.ddGroup || "TreeDD",
30880                scroll: this.ddScroll
30881            });
30882         }
30883         this.getSelectionModel().init(this);
30884         if (!this.root) {
30885             console.log("ROOT not set in tree");
30886             return;
30887         }
30888         this.root.render();
30889         if(!this.rootVisible){
30890             this.root.renderChildren();
30891         }
30892         return this;
30893     }
30894 });/*
30895  * Based on:
30896  * Ext JS Library 1.1.1
30897  * Copyright(c) 2006-2007, Ext JS, LLC.
30898  *
30899  * Originally Released Under LGPL - original licence link has changed is not relivant.
30900  *
30901  * Fork - LGPL
30902  * <script type="text/javascript">
30903  */
30904  
30905
30906 /**
30907  * @class Roo.tree.DefaultSelectionModel
30908  * @extends Roo.util.Observable
30909  * The default single selection for a TreePanel.
30910  */
30911 Roo.tree.DefaultSelectionModel = function(){
30912    this.selNode = null;
30913    
30914    this.addEvents({
30915        /**
30916         * @event selectionchange
30917         * Fires when the selected node changes
30918         * @param {DefaultSelectionModel} this
30919         * @param {TreeNode} node the new selection
30920         */
30921        "selectionchange" : true,
30922
30923        /**
30924         * @event beforeselect
30925         * Fires before the selected node changes, return false to cancel the change
30926         * @param {DefaultSelectionModel} this
30927         * @param {TreeNode} node the new selection
30928         * @param {TreeNode} node the old selection
30929         */
30930        "beforeselect" : true
30931    });
30932 };
30933
30934 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
30935     init : function(tree){
30936         this.tree = tree;
30937         tree.getTreeEl().on("keydown", this.onKeyDown, this);
30938         tree.on("click", this.onNodeClick, this);
30939     },
30940     
30941     onNodeClick : function(node, e){
30942         if (e.ctrlKey && this.selNode == node)  {
30943             this.unselect(node);
30944             return;
30945         }
30946         this.select(node);
30947     },
30948     
30949     /**
30950      * Select a node.
30951      * @param {TreeNode} node The node to select
30952      * @return {TreeNode} The selected node
30953      */
30954     select : function(node){
30955         var last = this.selNode;
30956         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
30957             if(last){
30958                 last.ui.onSelectedChange(false);
30959             }
30960             this.selNode = node;
30961             node.ui.onSelectedChange(true);
30962             this.fireEvent("selectionchange", this, node, last);
30963         }
30964         return node;
30965     },
30966     
30967     /**
30968      * Deselect a node.
30969      * @param {TreeNode} node The node to unselect
30970      */
30971     unselect : function(node){
30972         if(this.selNode == node){
30973             this.clearSelections();
30974         }    
30975     },
30976     
30977     /**
30978      * Clear all selections
30979      */
30980     clearSelections : function(){
30981         var n = this.selNode;
30982         if(n){
30983             n.ui.onSelectedChange(false);
30984             this.selNode = null;
30985             this.fireEvent("selectionchange", this, null);
30986         }
30987         return n;
30988     },
30989     
30990     /**
30991      * Get the selected node
30992      * @return {TreeNode} The selected node
30993      */
30994     getSelectedNode : function(){
30995         return this.selNode;    
30996     },
30997     
30998     /**
30999      * Returns true if the node is selected
31000      * @param {TreeNode} node The node to check
31001      * @return {Boolean}
31002      */
31003     isSelected : function(node){
31004         return this.selNode == node;  
31005     },
31006
31007     /**
31008      * Selects the node above the selected node in the tree, intelligently walking the nodes
31009      * @return TreeNode The new selection
31010      */
31011     selectPrevious : function(){
31012         var s = this.selNode || this.lastSelNode;
31013         if(!s){
31014             return null;
31015         }
31016         var ps = s.previousSibling;
31017         if(ps){
31018             if(!ps.isExpanded() || ps.childNodes.length < 1){
31019                 return this.select(ps);
31020             } else{
31021                 var lc = ps.lastChild;
31022                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31023                     lc = lc.lastChild;
31024                 }
31025                 return this.select(lc);
31026             }
31027         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31028             return this.select(s.parentNode);
31029         }
31030         return null;
31031     },
31032
31033     /**
31034      * Selects the node above the selected node in the tree, intelligently walking the nodes
31035      * @return TreeNode The new selection
31036      */
31037     selectNext : function(){
31038         var s = this.selNode || this.lastSelNode;
31039         if(!s){
31040             return null;
31041         }
31042         if(s.firstChild && s.isExpanded()){
31043              return this.select(s.firstChild);
31044          }else if(s.nextSibling){
31045              return this.select(s.nextSibling);
31046          }else if(s.parentNode){
31047             var newS = null;
31048             s.parentNode.bubble(function(){
31049                 if(this.nextSibling){
31050                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31051                     return false;
31052                 }
31053             });
31054             return newS;
31055          }
31056         return null;
31057     },
31058
31059     onKeyDown : function(e){
31060         var s = this.selNode || this.lastSelNode;
31061         // undesirable, but required
31062         var sm = this;
31063         if(!s){
31064             return;
31065         }
31066         var k = e.getKey();
31067         switch(k){
31068              case e.DOWN:
31069                  e.stopEvent();
31070                  this.selectNext();
31071              break;
31072              case e.UP:
31073                  e.stopEvent();
31074                  this.selectPrevious();
31075              break;
31076              case e.RIGHT:
31077                  e.preventDefault();
31078                  if(s.hasChildNodes()){
31079                      if(!s.isExpanded()){
31080                          s.expand();
31081                      }else if(s.firstChild){
31082                          this.select(s.firstChild, e);
31083                      }
31084                  }
31085              break;
31086              case e.LEFT:
31087                  e.preventDefault();
31088                  if(s.hasChildNodes() && s.isExpanded()){
31089                      s.collapse();
31090                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31091                      this.select(s.parentNode, e);
31092                  }
31093              break;
31094         };
31095     }
31096 });
31097
31098 /**
31099  * @class Roo.tree.MultiSelectionModel
31100  * @extends Roo.util.Observable
31101  * Multi selection for a TreePanel.
31102  */
31103 Roo.tree.MultiSelectionModel = function(){
31104    this.selNodes = [];
31105    this.selMap = {};
31106    this.addEvents({
31107        /**
31108         * @event selectionchange
31109         * Fires when the selected nodes change
31110         * @param {MultiSelectionModel} this
31111         * @param {Array} nodes Array of the selected nodes
31112         */
31113        "selectionchange" : true
31114    });
31115 };
31116
31117 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31118     init : function(tree){
31119         this.tree = tree;
31120         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31121         tree.on("click", this.onNodeClick, this);
31122     },
31123     
31124     onNodeClick : function(node, e){
31125         this.select(node, e, e.ctrlKey);
31126     },
31127     
31128     /**
31129      * Select a node.
31130      * @param {TreeNode} node The node to select
31131      * @param {EventObject} e (optional) An event associated with the selection
31132      * @param {Boolean} keepExisting True to retain existing selections
31133      * @return {TreeNode} The selected node
31134      */
31135     select : function(node, e, keepExisting){
31136         if(keepExisting !== true){
31137             this.clearSelections(true);
31138         }
31139         if(this.isSelected(node)){
31140             this.lastSelNode = node;
31141             return node;
31142         }
31143         this.selNodes.push(node);
31144         this.selMap[node.id] = node;
31145         this.lastSelNode = node;
31146         node.ui.onSelectedChange(true);
31147         this.fireEvent("selectionchange", this, this.selNodes);
31148         return node;
31149     },
31150     
31151     /**
31152      * Deselect a node.
31153      * @param {TreeNode} node The node to unselect
31154      */
31155     unselect : function(node){
31156         if(this.selMap[node.id]){
31157             node.ui.onSelectedChange(false);
31158             var sn = this.selNodes;
31159             var index = -1;
31160             if(sn.indexOf){
31161                 index = sn.indexOf(node);
31162             }else{
31163                 for(var i = 0, len = sn.length; i < len; i++){
31164                     if(sn[i] == node){
31165                         index = i;
31166                         break;
31167                     }
31168                 }
31169             }
31170             if(index != -1){
31171                 this.selNodes.splice(index, 1);
31172             }
31173             delete this.selMap[node.id];
31174             this.fireEvent("selectionchange", this, this.selNodes);
31175         }
31176     },
31177     
31178     /**
31179      * Clear all selections
31180      */
31181     clearSelections : function(suppressEvent){
31182         var sn = this.selNodes;
31183         if(sn.length > 0){
31184             for(var i = 0, len = sn.length; i < len; i++){
31185                 sn[i].ui.onSelectedChange(false);
31186             }
31187             this.selNodes = [];
31188             this.selMap = {};
31189             if(suppressEvent !== true){
31190                 this.fireEvent("selectionchange", this, this.selNodes);
31191             }
31192         }
31193     },
31194     
31195     /**
31196      * Returns true if the node is selected
31197      * @param {TreeNode} node The node to check
31198      * @return {Boolean}
31199      */
31200     isSelected : function(node){
31201         return this.selMap[node.id] ? true : false;  
31202     },
31203     
31204     /**
31205      * Returns an array of the selected nodes
31206      * @return {Array}
31207      */
31208     getSelectedNodes : function(){
31209         return this.selNodes;    
31210     },
31211
31212     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31213
31214     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31215
31216     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31217 });/*
31218  * Based on:
31219  * Ext JS Library 1.1.1
31220  * Copyright(c) 2006-2007, Ext JS, LLC.
31221  *
31222  * Originally Released Under LGPL - original licence link has changed is not relivant.
31223  *
31224  * Fork - LGPL
31225  * <script type="text/javascript">
31226  */
31227  
31228 /**
31229  * @class Roo.tree.TreeNode
31230  * @extends Roo.data.Node
31231  * @cfg {String} text The text for this node
31232  * @cfg {Boolean} expanded true to start the node expanded
31233  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31234  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31235  * @cfg {Boolean} disabled true to start the node disabled
31236  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31237  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31238  * @cfg {String} cls A css class to be added to the node
31239  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31240  * @cfg {String} href URL of the link used for the node (defaults to #)
31241  * @cfg {String} hrefTarget target frame for the link
31242  * @cfg {String} qtip An Ext QuickTip for the node
31243  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31244  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31245  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31246  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31247  * (defaults to undefined with no checkbox rendered)
31248  * @constructor
31249  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31250  */
31251 Roo.tree.TreeNode = function(attributes){
31252     attributes = attributes || {};
31253     if(typeof attributes == "string"){
31254         attributes = {text: attributes};
31255     }
31256     this.childrenRendered = false;
31257     this.rendered = false;
31258     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31259     this.expanded = attributes.expanded === true;
31260     this.isTarget = attributes.isTarget !== false;
31261     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31262     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31263
31264     /**
31265      * Read-only. The text for this node. To change it use setText().
31266      * @type String
31267      */
31268     this.text = attributes.text;
31269     /**
31270      * True if this node is disabled.
31271      * @type Boolean
31272      */
31273     this.disabled = attributes.disabled === true;
31274
31275     this.addEvents({
31276         /**
31277         * @event textchange
31278         * Fires when the text for this node is changed
31279         * @param {Node} this This node
31280         * @param {String} text The new text
31281         * @param {String} oldText The old text
31282         */
31283         "textchange" : true,
31284         /**
31285         * @event beforeexpand
31286         * Fires before this node is expanded, return false to cancel.
31287         * @param {Node} this This node
31288         * @param {Boolean} deep
31289         * @param {Boolean} anim
31290         */
31291         "beforeexpand" : true,
31292         /**
31293         * @event beforecollapse
31294         * Fires before this node is collapsed, return false to cancel.
31295         * @param {Node} this This node
31296         * @param {Boolean} deep
31297         * @param {Boolean} anim
31298         */
31299         "beforecollapse" : true,
31300         /**
31301         * @event expand
31302         * Fires when this node is expanded
31303         * @param {Node} this This node
31304         */
31305         "expand" : true,
31306         /**
31307         * @event disabledchange
31308         * Fires when the disabled status of this node changes
31309         * @param {Node} this This node
31310         * @param {Boolean} disabled
31311         */
31312         "disabledchange" : true,
31313         /**
31314         * @event collapse
31315         * Fires when this node is collapsed
31316         * @param {Node} this This node
31317         */
31318         "collapse" : true,
31319         /**
31320         * @event beforeclick
31321         * Fires before click processing. Return false to cancel the default action.
31322         * @param {Node} this This node
31323         * @param {Roo.EventObject} e The event object
31324         */
31325         "beforeclick":true,
31326         /**
31327         * @event checkchange
31328         * Fires when a node with a checkbox's checked property changes
31329         * @param {Node} this This node
31330         * @param {Boolean} checked
31331         */
31332         "checkchange":true,
31333         /**
31334         * @event click
31335         * Fires when this node is clicked
31336         * @param {Node} this This node
31337         * @param {Roo.EventObject} e The event object
31338         */
31339         "click":true,
31340         /**
31341         * @event dblclick
31342         * Fires when this node is double clicked
31343         * @param {Node} this This node
31344         * @param {Roo.EventObject} e The event object
31345         */
31346         "dblclick":true,
31347         /**
31348         * @event contextmenu
31349         * Fires when this node is right clicked
31350         * @param {Node} this This node
31351         * @param {Roo.EventObject} e The event object
31352         */
31353         "contextmenu":true,
31354         /**
31355         * @event beforechildrenrendered
31356         * Fires right before the child nodes for this node are rendered
31357         * @param {Node} this This node
31358         */
31359         "beforechildrenrendered":true
31360     });
31361
31362     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31363
31364     /**
31365      * Read-only. The UI for this node
31366      * @type TreeNodeUI
31367      */
31368     this.ui = new uiClass(this);
31369 };
31370 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31371     preventHScroll: true,
31372     /**
31373      * Returns true if this node is expanded
31374      * @return {Boolean}
31375      */
31376     isExpanded : function(){
31377         return this.expanded;
31378     },
31379
31380     /**
31381      * Returns the UI object for this node
31382      * @return {TreeNodeUI}
31383      */
31384     getUI : function(){
31385         return this.ui;
31386     },
31387
31388     // private override
31389     setFirstChild : function(node){
31390         var of = this.firstChild;
31391         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31392         if(this.childrenRendered && of && node != of){
31393             of.renderIndent(true, true);
31394         }
31395         if(this.rendered){
31396             this.renderIndent(true, true);
31397         }
31398     },
31399
31400     // private override
31401     setLastChild : function(node){
31402         var ol = this.lastChild;
31403         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31404         if(this.childrenRendered && ol && node != ol){
31405             ol.renderIndent(true, true);
31406         }
31407         if(this.rendered){
31408             this.renderIndent(true, true);
31409         }
31410     },
31411
31412     // these methods are overridden to provide lazy rendering support
31413     // private override
31414     appendChild : function(){
31415         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31416         if(node && this.childrenRendered){
31417             node.render();
31418         }
31419         this.ui.updateExpandIcon();
31420         return node;
31421     },
31422
31423     // private override
31424     removeChild : function(node){
31425         this.ownerTree.getSelectionModel().unselect(node);
31426         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31427         // if it's been rendered remove dom node
31428         if(this.childrenRendered){
31429             node.ui.remove();
31430         }
31431         if(this.childNodes.length < 1){
31432             this.collapse(false, false);
31433         }else{
31434             this.ui.updateExpandIcon();
31435         }
31436         if(!this.firstChild) {
31437             this.childrenRendered = false;
31438         }
31439         return node;
31440     },
31441
31442     // private override
31443     insertBefore : function(node, refNode){
31444         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31445         if(newNode && refNode && this.childrenRendered){
31446             node.render();
31447         }
31448         this.ui.updateExpandIcon();
31449         return newNode;
31450     },
31451
31452     /**
31453      * Sets the text for this node
31454      * @param {String} text
31455      */
31456     setText : function(text){
31457         var oldText = this.text;
31458         this.text = text;
31459         this.attributes.text = text;
31460         if(this.rendered){ // event without subscribing
31461             this.ui.onTextChange(this, text, oldText);
31462         }
31463         this.fireEvent("textchange", this, text, oldText);
31464     },
31465
31466     /**
31467      * Triggers selection of this node
31468      */
31469     select : function(){
31470         this.getOwnerTree().getSelectionModel().select(this);
31471     },
31472
31473     /**
31474      * Triggers deselection of this node
31475      */
31476     unselect : function(){
31477         this.getOwnerTree().getSelectionModel().unselect(this);
31478     },
31479
31480     /**
31481      * Returns true if this node is selected
31482      * @return {Boolean}
31483      */
31484     isSelected : function(){
31485         return this.getOwnerTree().getSelectionModel().isSelected(this);
31486     },
31487
31488     /**
31489      * Expand this node.
31490      * @param {Boolean} deep (optional) True to expand all children as well
31491      * @param {Boolean} anim (optional) false to cancel the default animation
31492      * @param {Function} callback (optional) A callback to be called when
31493      * expanding this node completes (does not wait for deep expand to complete).
31494      * Called with 1 parameter, this node.
31495      */
31496     expand : function(deep, anim, callback){
31497         if(!this.expanded){
31498             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31499                 return;
31500             }
31501             if(!this.childrenRendered){
31502                 this.renderChildren();
31503             }
31504             this.expanded = true;
31505             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31506                 this.ui.animExpand(function(){
31507                     this.fireEvent("expand", this);
31508                     if(typeof callback == "function"){
31509                         callback(this);
31510                     }
31511                     if(deep === true){
31512                         this.expandChildNodes(true);
31513                     }
31514                 }.createDelegate(this));
31515                 return;
31516             }else{
31517                 this.ui.expand();
31518                 this.fireEvent("expand", this);
31519                 if(typeof callback == "function"){
31520                     callback(this);
31521                 }
31522             }
31523         }else{
31524            if(typeof callback == "function"){
31525                callback(this);
31526            }
31527         }
31528         if(deep === true){
31529             this.expandChildNodes(true);
31530         }
31531     },
31532
31533     isHiddenRoot : function(){
31534         return this.isRoot && !this.getOwnerTree().rootVisible;
31535     },
31536
31537     /**
31538      * Collapse this node.
31539      * @param {Boolean} deep (optional) True to collapse all children as well
31540      * @param {Boolean} anim (optional) false to cancel the default animation
31541      */
31542     collapse : function(deep, anim){
31543         if(this.expanded && !this.isHiddenRoot()){
31544             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31545                 return;
31546             }
31547             this.expanded = false;
31548             if((this.getOwnerTree().animate && anim !== false) || anim){
31549                 this.ui.animCollapse(function(){
31550                     this.fireEvent("collapse", this);
31551                     if(deep === true){
31552                         this.collapseChildNodes(true);
31553                     }
31554                 }.createDelegate(this));
31555                 return;
31556             }else{
31557                 this.ui.collapse();
31558                 this.fireEvent("collapse", this);
31559             }
31560         }
31561         if(deep === true){
31562             var cs = this.childNodes;
31563             for(var i = 0, len = cs.length; i < len; i++) {
31564                 cs[i].collapse(true, false);
31565             }
31566         }
31567     },
31568
31569     // private
31570     delayedExpand : function(delay){
31571         if(!this.expandProcId){
31572             this.expandProcId = this.expand.defer(delay, this);
31573         }
31574     },
31575
31576     // private
31577     cancelExpand : function(){
31578         if(this.expandProcId){
31579             clearTimeout(this.expandProcId);
31580         }
31581         this.expandProcId = false;
31582     },
31583
31584     /**
31585      * Toggles expanded/collapsed state of the node
31586      */
31587     toggle : function(){
31588         if(this.expanded){
31589             this.collapse();
31590         }else{
31591             this.expand();
31592         }
31593     },
31594
31595     /**
31596      * Ensures all parent nodes are expanded
31597      */
31598     ensureVisible : function(callback){
31599         var tree = this.getOwnerTree();
31600         tree.expandPath(this.parentNode.getPath(), false, function(){
31601             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31602             Roo.callback(callback);
31603         }.createDelegate(this));
31604     },
31605
31606     /**
31607      * Expand all child nodes
31608      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31609      */
31610     expandChildNodes : function(deep){
31611         var cs = this.childNodes;
31612         for(var i = 0, len = cs.length; i < len; i++) {
31613                 cs[i].expand(deep);
31614         }
31615     },
31616
31617     /**
31618      * Collapse all child nodes
31619      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31620      */
31621     collapseChildNodes : function(deep){
31622         var cs = this.childNodes;
31623         for(var i = 0, len = cs.length; i < len; i++) {
31624                 cs[i].collapse(deep);
31625         }
31626     },
31627
31628     /**
31629      * Disables this node
31630      */
31631     disable : function(){
31632         this.disabled = true;
31633         this.unselect();
31634         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31635             this.ui.onDisableChange(this, true);
31636         }
31637         this.fireEvent("disabledchange", this, true);
31638     },
31639
31640     /**
31641      * Enables this node
31642      */
31643     enable : function(){
31644         this.disabled = false;
31645         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31646             this.ui.onDisableChange(this, false);
31647         }
31648         this.fireEvent("disabledchange", this, false);
31649     },
31650
31651     // private
31652     renderChildren : function(suppressEvent){
31653         if(suppressEvent !== false){
31654             this.fireEvent("beforechildrenrendered", this);
31655         }
31656         var cs = this.childNodes;
31657         for(var i = 0, len = cs.length; i < len; i++){
31658             cs[i].render(true);
31659         }
31660         this.childrenRendered = true;
31661     },
31662
31663     // private
31664     sort : function(fn, scope){
31665         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31666         if(this.childrenRendered){
31667             var cs = this.childNodes;
31668             for(var i = 0, len = cs.length; i < len; i++){
31669                 cs[i].render(true);
31670             }
31671         }
31672     },
31673
31674     // private
31675     render : function(bulkRender){
31676         this.ui.render(bulkRender);
31677         if(!this.rendered){
31678             this.rendered = true;
31679             if(this.expanded){
31680                 this.expanded = false;
31681                 this.expand(false, false);
31682             }
31683         }
31684     },
31685
31686     // private
31687     renderIndent : function(deep, refresh){
31688         if(refresh){
31689             this.ui.childIndent = null;
31690         }
31691         this.ui.renderIndent();
31692         if(deep === true && this.childrenRendered){
31693             var cs = this.childNodes;
31694             for(var i = 0, len = cs.length; i < len; i++){
31695                 cs[i].renderIndent(true, refresh);
31696             }
31697         }
31698     }
31699 });/*
31700  * Based on:
31701  * Ext JS Library 1.1.1
31702  * Copyright(c) 2006-2007, Ext JS, LLC.
31703  *
31704  * Originally Released Under LGPL - original licence link has changed is not relivant.
31705  *
31706  * Fork - LGPL
31707  * <script type="text/javascript">
31708  */
31709  
31710 /**
31711  * @class Roo.tree.AsyncTreeNode
31712  * @extends Roo.tree.TreeNode
31713  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31714  * @constructor
31715  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31716  */
31717  Roo.tree.AsyncTreeNode = function(config){
31718     this.loaded = false;
31719     this.loading = false;
31720     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31721     /**
31722     * @event beforeload
31723     * Fires before this node is loaded, return false to cancel
31724     * @param {Node} this This node
31725     */
31726     this.addEvents({'beforeload':true, 'load': true});
31727     /**
31728     * @event load
31729     * Fires when this node is loaded
31730     * @param {Node} this This node
31731     */
31732     /**
31733      * The loader used by this node (defaults to using the tree's defined loader)
31734      * @type TreeLoader
31735      * @property loader
31736      */
31737 };
31738 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31739     expand : function(deep, anim, callback){
31740         if(this.loading){ // if an async load is already running, waiting til it's done
31741             var timer;
31742             var f = function(){
31743                 if(!this.loading){ // done loading
31744                     clearInterval(timer);
31745                     this.expand(deep, anim, callback);
31746                 }
31747             }.createDelegate(this);
31748             timer = setInterval(f, 200);
31749             return;
31750         }
31751         if(!this.loaded){
31752             if(this.fireEvent("beforeload", this) === false){
31753                 return;
31754             }
31755             this.loading = true;
31756             this.ui.beforeLoad(this);
31757             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31758             if(loader){
31759                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31760                 return;
31761             }
31762         }
31763         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31764     },
31765     
31766     /**
31767      * Returns true if this node is currently loading
31768      * @return {Boolean}
31769      */
31770     isLoading : function(){
31771         return this.loading;  
31772     },
31773     
31774     loadComplete : function(deep, anim, callback){
31775         this.loading = false;
31776         this.loaded = true;
31777         this.ui.afterLoad(this);
31778         this.fireEvent("load", this);
31779         this.expand(deep, anim, callback);
31780     },
31781     
31782     /**
31783      * Returns true if this node has been loaded
31784      * @return {Boolean}
31785      */
31786     isLoaded : function(){
31787         return this.loaded;
31788     },
31789     
31790     hasChildNodes : function(){
31791         if(!this.isLeaf() && !this.loaded){
31792             return true;
31793         }else{
31794             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
31795         }
31796     },
31797
31798     /**
31799      * Trigger a reload for this node
31800      * @param {Function} callback
31801      */
31802     reload : function(callback){
31803         this.collapse(false, false);
31804         while(this.firstChild){
31805             this.removeChild(this.firstChild);
31806         }
31807         this.childrenRendered = false;
31808         this.loaded = false;
31809         if(this.isHiddenRoot()){
31810             this.expanded = false;
31811         }
31812         this.expand(false, false, callback);
31813     }
31814 });/*
31815  * Based on:
31816  * Ext JS Library 1.1.1
31817  * Copyright(c) 2006-2007, Ext JS, LLC.
31818  *
31819  * Originally Released Under LGPL - original licence link has changed is not relivant.
31820  *
31821  * Fork - LGPL
31822  * <script type="text/javascript">
31823  */
31824  
31825 /**
31826  * @class Roo.tree.TreeNodeUI
31827  * @constructor
31828  * @param {Object} node The node to render
31829  * The TreeNode UI implementation is separate from the
31830  * tree implementation. Unless you are customizing the tree UI,
31831  * you should never have to use this directly.
31832  */
31833 Roo.tree.TreeNodeUI = function(node){
31834     this.node = node;
31835     this.rendered = false;
31836     this.animating = false;
31837     this.emptyIcon = Roo.BLANK_IMAGE_URL;
31838 };
31839
31840 Roo.tree.TreeNodeUI.prototype = {
31841     removeChild : function(node){
31842         if(this.rendered){
31843             this.ctNode.removeChild(node.ui.getEl());
31844         }
31845     },
31846
31847     beforeLoad : function(){
31848          this.addClass("x-tree-node-loading");
31849     },
31850
31851     afterLoad : function(){
31852          this.removeClass("x-tree-node-loading");
31853     },
31854
31855     onTextChange : function(node, text, oldText){
31856         if(this.rendered){
31857             this.textNode.innerHTML = text;
31858         }
31859     },
31860
31861     onDisableChange : function(node, state){
31862         this.disabled = state;
31863         if(state){
31864             this.addClass("x-tree-node-disabled");
31865         }else{
31866             this.removeClass("x-tree-node-disabled");
31867         }
31868     },
31869
31870     onSelectedChange : function(state){
31871         if(state){
31872             this.focus();
31873             this.addClass("x-tree-selected");
31874         }else{
31875             //this.blur();
31876             this.removeClass("x-tree-selected");
31877         }
31878     },
31879
31880     onMove : function(tree, node, oldParent, newParent, index, refNode){
31881         this.childIndent = null;
31882         if(this.rendered){
31883             var targetNode = newParent.ui.getContainer();
31884             if(!targetNode){//target not rendered
31885                 this.holder = document.createElement("div");
31886                 this.holder.appendChild(this.wrap);
31887                 return;
31888             }
31889             var insertBefore = refNode ? refNode.ui.getEl() : null;
31890             if(insertBefore){
31891                 targetNode.insertBefore(this.wrap, insertBefore);
31892             }else{
31893                 targetNode.appendChild(this.wrap);
31894             }
31895             this.node.renderIndent(true);
31896         }
31897     },
31898
31899     addClass : function(cls){
31900         if(this.elNode){
31901             Roo.fly(this.elNode).addClass(cls);
31902         }
31903     },
31904
31905     removeClass : function(cls){
31906         if(this.elNode){
31907             Roo.fly(this.elNode).removeClass(cls);
31908         }
31909     },
31910
31911     remove : function(){
31912         if(this.rendered){
31913             this.holder = document.createElement("div");
31914             this.holder.appendChild(this.wrap);
31915         }
31916     },
31917
31918     fireEvent : function(){
31919         return this.node.fireEvent.apply(this.node, arguments);
31920     },
31921
31922     initEvents : function(){
31923         this.node.on("move", this.onMove, this);
31924         var E = Roo.EventManager;
31925         var a = this.anchor;
31926
31927         var el = Roo.fly(a, '_treeui');
31928
31929         if(Roo.isOpera){ // opera render bug ignores the CSS
31930             el.setStyle("text-decoration", "none");
31931         }
31932
31933         el.on("click", this.onClick, this);
31934         el.on("dblclick", this.onDblClick, this);
31935
31936         if(this.checkbox){
31937             Roo.EventManager.on(this.checkbox,
31938                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
31939         }
31940
31941         el.on("contextmenu", this.onContextMenu, this);
31942
31943         var icon = Roo.fly(this.iconNode);
31944         icon.on("click", this.onClick, this);
31945         icon.on("dblclick", this.onDblClick, this);
31946         icon.on("contextmenu", this.onContextMenu, this);
31947         E.on(this.ecNode, "click", this.ecClick, this, true);
31948
31949         if(this.node.disabled){
31950             this.addClass("x-tree-node-disabled");
31951         }
31952         if(this.node.hidden){
31953             this.addClass("x-tree-node-disabled");
31954         }
31955         var ot = this.node.getOwnerTree();
31956         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
31957         if(dd && (!this.node.isRoot || ot.rootVisible)){
31958             Roo.dd.Registry.register(this.elNode, {
31959                 node: this.node,
31960                 handles: this.getDDHandles(),
31961                 isHandle: false
31962             });
31963         }
31964     },
31965
31966     getDDHandles : function(){
31967         return [this.iconNode, this.textNode];
31968     },
31969
31970     hide : function(){
31971         if(this.rendered){
31972             this.wrap.style.display = "none";
31973         }
31974     },
31975
31976     show : function(){
31977         if(this.rendered){
31978             this.wrap.style.display = "";
31979         }
31980     },
31981
31982     onContextMenu : function(e){
31983         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
31984             e.preventDefault();
31985             this.focus();
31986             this.fireEvent("contextmenu", this.node, e);
31987         }
31988     },
31989
31990     onClick : function(e){
31991         if(this.dropping){
31992             e.stopEvent();
31993             return;
31994         }
31995         if(this.fireEvent("beforeclick", this.node, e) !== false){
31996             if(!this.disabled && this.node.attributes.href){
31997                 this.fireEvent("click", this.node, e);
31998                 return;
31999             }
32000             e.preventDefault();
32001             if(this.disabled){
32002                 return;
32003             }
32004
32005             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32006                 this.node.toggle();
32007             }
32008
32009             this.fireEvent("click", this.node, e);
32010         }else{
32011             e.stopEvent();
32012         }
32013     },
32014
32015     onDblClick : function(e){
32016         e.preventDefault();
32017         if(this.disabled){
32018             return;
32019         }
32020         if(this.checkbox){
32021             this.toggleCheck();
32022         }
32023         if(!this.animating && this.node.hasChildNodes()){
32024             this.node.toggle();
32025         }
32026         this.fireEvent("dblclick", this.node, e);
32027     },
32028
32029     onCheckChange : function(){
32030         var checked = this.checkbox.checked;
32031         this.node.attributes.checked = checked;
32032         this.fireEvent('checkchange', this.node, checked);
32033     },
32034
32035     ecClick : function(e){
32036         if(!this.animating && this.node.hasChildNodes()){
32037             this.node.toggle();
32038         }
32039     },
32040
32041     startDrop : function(){
32042         this.dropping = true;
32043     },
32044
32045     // delayed drop so the click event doesn't get fired on a drop
32046     endDrop : function(){
32047        setTimeout(function(){
32048            this.dropping = false;
32049        }.createDelegate(this), 50);
32050     },
32051
32052     expand : function(){
32053         this.updateExpandIcon();
32054         this.ctNode.style.display = "";
32055     },
32056
32057     focus : function(){
32058         if(!this.node.preventHScroll){
32059             try{this.anchor.focus();
32060             }catch(e){}
32061         }else if(!Roo.isIE){
32062             try{
32063                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32064                 var l = noscroll.scrollLeft;
32065                 this.anchor.focus();
32066                 noscroll.scrollLeft = l;
32067             }catch(e){}
32068         }
32069     },
32070
32071     toggleCheck : function(value){
32072         var cb = this.checkbox;
32073         if(cb){
32074             cb.checked = (value === undefined ? !cb.checked : value);
32075         }
32076     },
32077
32078     blur : function(){
32079         try{
32080             this.anchor.blur();
32081         }catch(e){}
32082     },
32083
32084     animExpand : function(callback){
32085         var ct = Roo.get(this.ctNode);
32086         ct.stopFx();
32087         if(!this.node.hasChildNodes()){
32088             this.updateExpandIcon();
32089             this.ctNode.style.display = "";
32090             Roo.callback(callback);
32091             return;
32092         }
32093         this.animating = true;
32094         this.updateExpandIcon();
32095
32096         ct.slideIn('t', {
32097            callback : function(){
32098                this.animating = false;
32099                Roo.callback(callback);
32100             },
32101             scope: this,
32102             duration: this.node.ownerTree.duration || .25
32103         });
32104     },
32105
32106     highlight : function(){
32107         var tree = this.node.getOwnerTree();
32108         Roo.fly(this.wrap).highlight(
32109             tree.hlColor || "C3DAF9",
32110             {endColor: tree.hlBaseColor}
32111         );
32112     },
32113
32114     collapse : function(){
32115         this.updateExpandIcon();
32116         this.ctNode.style.display = "none";
32117     },
32118
32119     animCollapse : function(callback){
32120         var ct = Roo.get(this.ctNode);
32121         ct.enableDisplayMode('block');
32122         ct.stopFx();
32123
32124         this.animating = true;
32125         this.updateExpandIcon();
32126
32127         ct.slideOut('t', {
32128             callback : function(){
32129                this.animating = false;
32130                Roo.callback(callback);
32131             },
32132             scope: this,
32133             duration: this.node.ownerTree.duration || .25
32134         });
32135     },
32136
32137     getContainer : function(){
32138         return this.ctNode;
32139     },
32140
32141     getEl : function(){
32142         return this.wrap;
32143     },
32144
32145     appendDDGhost : function(ghostNode){
32146         ghostNode.appendChild(this.elNode.cloneNode(true));
32147     },
32148
32149     getDDRepairXY : function(){
32150         return Roo.lib.Dom.getXY(this.iconNode);
32151     },
32152
32153     onRender : function(){
32154         this.render();
32155     },
32156
32157     render : function(bulkRender){
32158         var n = this.node, a = n.attributes;
32159         var targetNode = n.parentNode ?
32160               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32161
32162         if(!this.rendered){
32163             this.rendered = true;
32164
32165             this.renderElements(n, a, targetNode, bulkRender);
32166
32167             if(a.qtip){
32168                if(this.textNode.setAttributeNS){
32169                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32170                    if(a.qtipTitle){
32171                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32172                    }
32173                }else{
32174                    this.textNode.setAttribute("ext:qtip", a.qtip);
32175                    if(a.qtipTitle){
32176                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32177                    }
32178                }
32179             }else if(a.qtipCfg){
32180                 a.qtipCfg.target = Roo.id(this.textNode);
32181                 Roo.QuickTips.register(a.qtipCfg);
32182             }
32183             this.initEvents();
32184             if(!this.node.expanded){
32185                 this.updateExpandIcon();
32186             }
32187         }else{
32188             if(bulkRender === true) {
32189                 targetNode.appendChild(this.wrap);
32190             }
32191         }
32192     },
32193
32194     renderElements : function(n, a, targetNode, bulkRender){
32195         // add some indent caching, this helps performance when rendering a large tree
32196         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32197         var t = n.getOwnerTree();
32198         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32199         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32200         var cb = typeof a.checked == 'boolean';
32201         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32202         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32203             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32204             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32205             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32206             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32207             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32208              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32209                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32210             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32211             "</li>"];
32212
32213         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32214             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32215                                 n.nextSibling.ui.getEl(), buf.join(""));
32216         }else{
32217             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32218         }
32219
32220         this.elNode = this.wrap.childNodes[0];
32221         this.ctNode = this.wrap.childNodes[1];
32222         var cs = this.elNode.childNodes;
32223         this.indentNode = cs[0];
32224         this.ecNode = cs[1];
32225         this.iconNode = cs[2];
32226         var index = 3;
32227         if(cb){
32228             this.checkbox = cs[3];
32229             index++;
32230         }
32231         this.anchor = cs[index];
32232         this.textNode = cs[index].firstChild;
32233     },
32234
32235     getAnchor : function(){
32236         return this.anchor;
32237     },
32238
32239     getTextEl : function(){
32240         return this.textNode;
32241     },
32242
32243     getIconEl : function(){
32244         return this.iconNode;
32245     },
32246
32247     isChecked : function(){
32248         return this.checkbox ? this.checkbox.checked : false;
32249     },
32250
32251     updateExpandIcon : function(){
32252         if(this.rendered){
32253             var n = this.node, c1, c2;
32254             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32255             var hasChild = n.hasChildNodes();
32256             if(hasChild){
32257                 if(n.expanded){
32258                     cls += "-minus";
32259                     c1 = "x-tree-node-collapsed";
32260                     c2 = "x-tree-node-expanded";
32261                 }else{
32262                     cls += "-plus";
32263                     c1 = "x-tree-node-expanded";
32264                     c2 = "x-tree-node-collapsed";
32265                 }
32266                 if(this.wasLeaf){
32267                     this.removeClass("x-tree-node-leaf");
32268                     this.wasLeaf = false;
32269                 }
32270                 if(this.c1 != c1 || this.c2 != c2){
32271                     Roo.fly(this.elNode).replaceClass(c1, c2);
32272                     this.c1 = c1; this.c2 = c2;
32273                 }
32274             }else{
32275                 if(!this.wasLeaf){
32276                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32277                     delete this.c1;
32278                     delete this.c2;
32279                     this.wasLeaf = true;
32280                 }
32281             }
32282             var ecc = "x-tree-ec-icon "+cls;
32283             if(this.ecc != ecc){
32284                 this.ecNode.className = ecc;
32285                 this.ecc = ecc;
32286             }
32287         }
32288     },
32289
32290     getChildIndent : function(){
32291         if(!this.childIndent){
32292             var buf = [];
32293             var p = this.node;
32294             while(p){
32295                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32296                     if(!p.isLast()) {
32297                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32298                     } else {
32299                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32300                     }
32301                 }
32302                 p = p.parentNode;
32303             }
32304             this.childIndent = buf.join("");
32305         }
32306         return this.childIndent;
32307     },
32308
32309     renderIndent : function(){
32310         if(this.rendered){
32311             var indent = "";
32312             var p = this.node.parentNode;
32313             if(p){
32314                 indent = p.ui.getChildIndent();
32315             }
32316             if(this.indentMarkup != indent){ // don't rerender if not required
32317                 this.indentNode.innerHTML = indent;
32318                 this.indentMarkup = indent;
32319             }
32320             this.updateExpandIcon();
32321         }
32322     }
32323 };
32324
32325 Roo.tree.RootTreeNodeUI = function(){
32326     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32327 };
32328 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32329     render : function(){
32330         if(!this.rendered){
32331             var targetNode = this.node.ownerTree.innerCt.dom;
32332             this.node.expanded = true;
32333             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32334             this.wrap = this.ctNode = targetNode.firstChild;
32335         }
32336     },
32337     collapse : function(){
32338     },
32339     expand : function(){
32340     }
32341 });/*
32342  * Based on:
32343  * Ext JS Library 1.1.1
32344  * Copyright(c) 2006-2007, Ext JS, LLC.
32345  *
32346  * Originally Released Under LGPL - original licence link has changed is not relivant.
32347  *
32348  * Fork - LGPL
32349  * <script type="text/javascript">
32350  */
32351 /**
32352  * @class Roo.tree.TreeLoader
32353  * @extends Roo.util.Observable
32354  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32355  * nodes from a specified URL. The response must be a javascript Array definition
32356  * who's elements are node definition objects. eg:
32357  * <pre><code>
32358    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32359     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32360 </code></pre>
32361  * <br><br>
32362  * A server request is sent, and child nodes are loaded only when a node is expanded.
32363  * The loading node's id is passed to the server under the parameter name "node" to
32364  * enable the server to produce the correct child nodes.
32365  * <br><br>
32366  * To pass extra parameters, an event handler may be attached to the "beforeload"
32367  * event, and the parameters specified in the TreeLoader's baseParams property:
32368  * <pre><code>
32369     myTreeLoader.on("beforeload", function(treeLoader, node) {
32370         this.baseParams.category = node.attributes.category;
32371     }, this);
32372 </code></pre><
32373  * This would pass an HTTP parameter called "category" to the server containing
32374  * the value of the Node's "category" attribute.
32375  * @constructor
32376  * Creates a new Treeloader.
32377  * @param {Object} config A config object containing config properties.
32378  */
32379 Roo.tree.TreeLoader = function(config){
32380     this.baseParams = {};
32381     this.requestMethod = "POST";
32382     Roo.apply(this, config);
32383
32384     this.addEvents({
32385     
32386         /**
32387          * @event beforeload
32388          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32389          * @param {Object} This TreeLoader object.
32390          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32391          * @param {Object} callback The callback function specified in the {@link #load} call.
32392          */
32393         beforeload : true,
32394         /**
32395          * @event load
32396          * Fires when the node has been successfuly loaded.
32397          * @param {Object} This TreeLoader object.
32398          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32399          * @param {Object} response The response object containing the data from the server.
32400          */
32401         load : true,
32402         /**
32403          * @event loadexception
32404          * Fires if the network request failed.
32405          * @param {Object} This TreeLoader object.
32406          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32407          * @param {Object} response The response object containing the data from the server.
32408          */
32409         loadexception : true,
32410         /**
32411          * @event create
32412          * Fires before a node is created, enabling you to return custom Node types 
32413          * @param {Object} This TreeLoader object.
32414          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32415          */
32416         create : true
32417     });
32418
32419     Roo.tree.TreeLoader.superclass.constructor.call(this);
32420 };
32421
32422 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32423     /**
32424     * @cfg {String} dataUrl The URL from which to request a Json string which
32425     * specifies an array of node definition object representing the child nodes
32426     * to be loaded.
32427     */
32428     /**
32429     * @cfg {Object} baseParams (optional) An object containing properties which
32430     * specify HTTP parameters to be passed to each request for child nodes.
32431     */
32432     /**
32433     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32434     * created by this loader. If the attributes sent by the server have an attribute in this object,
32435     * they take priority.
32436     */
32437     /**
32438     * @cfg {Object} uiProviders (optional) An object containing properties which
32439     * 
32440     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32441     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32442     * <i>uiProvider</i> attribute of a returned child node is a string rather
32443     * than a reference to a TreeNodeUI implementation, this that string value
32444     * is used as a property name in the uiProviders object. You can define the provider named
32445     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32446     */
32447     uiProviders : {},
32448
32449     /**
32450     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32451     * child nodes before loading.
32452     */
32453     clearOnLoad : true,
32454
32455     /**
32456     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32457     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32458     * Grid query { data : [ .....] }
32459     */
32460     
32461     root : false,
32462      /**
32463     * @cfg {String} queryParam (optional) 
32464     * Name of the query as it will be passed on the querystring (defaults to 'node')
32465     * eg. the request will be ?node=[id]
32466     */
32467     
32468     
32469     queryParam: false,
32470     
32471     /**
32472      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32473      * This is called automatically when a node is expanded, but may be used to reload
32474      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32475      * @param {Roo.tree.TreeNode} node
32476      * @param {Function} callback
32477      */
32478     load : function(node, callback){
32479         if(this.clearOnLoad){
32480             while(node.firstChild){
32481                 node.removeChild(node.firstChild);
32482             }
32483         }
32484         if(node.attributes.children){ // preloaded json children
32485             var cs = node.attributes.children;
32486             for(var i = 0, len = cs.length; i < len; i++){
32487                 node.appendChild(this.createNode(cs[i]));
32488             }
32489             if(typeof callback == "function"){
32490                 callback();
32491             }
32492         }else if(this.dataUrl){
32493             this.requestData(node, callback);
32494         }
32495     },
32496
32497     getParams: function(node){
32498         var buf = [], bp = this.baseParams;
32499         for(var key in bp){
32500             if(typeof bp[key] != "function"){
32501                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32502             }
32503         }
32504         var n = this.queryParam === false ? 'node' : this.queryParam;
32505         buf.push(n + "=", encodeURIComponent(node.id));
32506         return buf.join("");
32507     },
32508
32509     requestData : function(node, callback){
32510         if(this.fireEvent("beforeload", this, node, callback) !== false){
32511             this.transId = Roo.Ajax.request({
32512                 method:this.requestMethod,
32513                 url: this.dataUrl||this.url,
32514                 success: this.handleResponse,
32515                 failure: this.handleFailure,
32516                 scope: this,
32517                 argument: {callback: callback, node: node},
32518                 params: this.getParams(node)
32519             });
32520         }else{
32521             // if the load is cancelled, make sure we notify
32522             // the node that we are done
32523             if(typeof callback == "function"){
32524                 callback();
32525             }
32526         }
32527     },
32528
32529     isLoading : function(){
32530         return this.transId ? true : false;
32531     },
32532
32533     abort : function(){
32534         if(this.isLoading()){
32535             Roo.Ajax.abort(this.transId);
32536         }
32537     },
32538
32539     // private
32540     createNode : function(attr){
32541         // apply baseAttrs, nice idea Corey!
32542         if(this.baseAttrs){
32543             Roo.applyIf(attr, this.baseAttrs);
32544         }
32545         if(this.applyLoader !== false){
32546             attr.loader = this;
32547         }
32548         // uiProvider = depreciated..
32549         
32550         if(typeof(attr.uiProvider) == 'string'){
32551            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32552                 /**  eval:var:attr */ eval(attr.uiProvider);
32553         }
32554         if(typeof(this.uiProviders['default']) != 'undefined') {
32555             attr.uiProvider = this.uiProviders['default'];
32556         }
32557         
32558         this.fireEvent('create', this, attr);
32559         
32560         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32561         return(attr.leaf ?
32562                         new Roo.tree.TreeNode(attr) :
32563                         new Roo.tree.AsyncTreeNode(attr));
32564     },
32565
32566     processResponse : function(response, node, callback){
32567         var json = response.responseText;
32568         try {
32569             
32570             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32571             if (this.root !== false) {
32572                 o = o[this.root];
32573             }
32574             
32575             for(var i = 0, len = o.length; i < len; i++){
32576                 var n = this.createNode(o[i]);
32577                 if(n){
32578                     node.appendChild(n);
32579                 }
32580             }
32581             if(typeof callback == "function"){
32582                 callback(this, node);
32583             }
32584         }catch(e){
32585             this.handleFailure(response);
32586         }
32587     },
32588
32589     handleResponse : function(response){
32590         this.transId = false;
32591         var a = response.argument;
32592         this.processResponse(response, a.node, a.callback);
32593         this.fireEvent("load", this, a.node, response);
32594     },
32595
32596     handleFailure : function(response){
32597         this.transId = false;
32598         var a = response.argument;
32599         this.fireEvent("loadexception", this, a.node, response);
32600         if(typeof a.callback == "function"){
32601             a.callback(this, a.node);
32602         }
32603     }
32604 });/*
32605  * Based on:
32606  * Ext JS Library 1.1.1
32607  * Copyright(c) 2006-2007, Ext JS, LLC.
32608  *
32609  * Originally Released Under LGPL - original licence link has changed is not relivant.
32610  *
32611  * Fork - LGPL
32612  * <script type="text/javascript">
32613  */
32614
32615 /**
32616 * @class Roo.tree.TreeFilter
32617 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32618 * @param {TreePanel} tree
32619 * @param {Object} config (optional)
32620  */
32621 Roo.tree.TreeFilter = function(tree, config){
32622     this.tree = tree;
32623     this.filtered = {};
32624     Roo.apply(this, config);
32625 };
32626
32627 Roo.tree.TreeFilter.prototype = {
32628     clearBlank:false,
32629     reverse:false,
32630     autoClear:false,
32631     remove:false,
32632
32633      /**
32634      * Filter the data by a specific attribute.
32635      * @param {String/RegExp} value Either string that the attribute value
32636      * should start with or a RegExp to test against the attribute
32637      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32638      * @param {TreeNode} startNode (optional) The node to start the filter at.
32639      */
32640     filter : function(value, attr, startNode){
32641         attr = attr || "text";
32642         var f;
32643         if(typeof value == "string"){
32644             var vlen = value.length;
32645             // auto clear empty filter
32646             if(vlen == 0 && this.clearBlank){
32647                 this.clear();
32648                 return;
32649             }
32650             value = value.toLowerCase();
32651             f = function(n){
32652                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32653             };
32654         }else if(value.exec){ // regex?
32655             f = function(n){
32656                 return value.test(n.attributes[attr]);
32657             };
32658         }else{
32659             throw 'Illegal filter type, must be string or regex';
32660         }
32661         this.filterBy(f, null, startNode);
32662         },
32663
32664     /**
32665      * Filter by a function. The passed function will be called with each
32666      * node in the tree (or from the startNode). If the function returns true, the node is kept
32667      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32668      * @param {Function} fn The filter function
32669      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32670      */
32671     filterBy : function(fn, scope, startNode){
32672         startNode = startNode || this.tree.root;
32673         if(this.autoClear){
32674             this.clear();
32675         }
32676         var af = this.filtered, rv = this.reverse;
32677         var f = function(n){
32678             if(n == startNode){
32679                 return true;
32680             }
32681             if(af[n.id]){
32682                 return false;
32683             }
32684             var m = fn.call(scope || n, n);
32685             if(!m || rv){
32686                 af[n.id] = n;
32687                 n.ui.hide();
32688                 return false;
32689             }
32690             return true;
32691         };
32692         startNode.cascade(f);
32693         if(this.remove){
32694            for(var id in af){
32695                if(typeof id != "function"){
32696                    var n = af[id];
32697                    if(n && n.parentNode){
32698                        n.parentNode.removeChild(n);
32699                    }
32700                }
32701            }
32702         }
32703     },
32704
32705     /**
32706      * Clears the current filter. Note: with the "remove" option
32707      * set a filter cannot be cleared.
32708      */
32709     clear : function(){
32710         var t = this.tree;
32711         var af = this.filtered;
32712         for(var id in af){
32713             if(typeof id != "function"){
32714                 var n = af[id];
32715                 if(n){
32716                     n.ui.show();
32717                 }
32718             }
32719         }
32720         this.filtered = {};
32721     }
32722 };
32723 /*
32724  * Based on:
32725  * Ext JS Library 1.1.1
32726  * Copyright(c) 2006-2007, Ext JS, LLC.
32727  *
32728  * Originally Released Under LGPL - original licence link has changed is not relivant.
32729  *
32730  * Fork - LGPL
32731  * <script type="text/javascript">
32732  */
32733  
32734
32735 /**
32736  * @class Roo.tree.TreeSorter
32737  * Provides sorting of nodes in a TreePanel
32738  * 
32739  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32740  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32741  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32742  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32743  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32744  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32745  * @constructor
32746  * @param {TreePanel} tree
32747  * @param {Object} config
32748  */
32749 Roo.tree.TreeSorter = function(tree, config){
32750     Roo.apply(this, config);
32751     tree.on("beforechildrenrendered", this.doSort, this);
32752     tree.on("append", this.updateSort, this);
32753     tree.on("insert", this.updateSort, this);
32754     
32755     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32756     var p = this.property || "text";
32757     var sortType = this.sortType;
32758     var fs = this.folderSort;
32759     var cs = this.caseSensitive === true;
32760     var leafAttr = this.leafAttr || 'leaf';
32761
32762     this.sortFn = function(n1, n2){
32763         if(fs){
32764             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32765                 return 1;
32766             }
32767             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32768                 return -1;
32769             }
32770         }
32771         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32772         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32773         if(v1 < v2){
32774                         return dsc ? +1 : -1;
32775                 }else if(v1 > v2){
32776                         return dsc ? -1 : +1;
32777         }else{
32778                 return 0;
32779         }
32780     };
32781 };
32782
32783 Roo.tree.TreeSorter.prototype = {
32784     doSort : function(node){
32785         node.sort(this.sortFn);
32786     },
32787     
32788     compareNodes : function(n1, n2){
32789         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
32790     },
32791     
32792     updateSort : function(tree, node){
32793         if(node.childrenRendered){
32794             this.doSort.defer(1, this, [node]);
32795         }
32796     }
32797 };/*
32798  * Based on:
32799  * Ext JS Library 1.1.1
32800  * Copyright(c) 2006-2007, Ext JS, LLC.
32801  *
32802  * Originally Released Under LGPL - original licence link has changed is not relivant.
32803  *
32804  * Fork - LGPL
32805  * <script type="text/javascript">
32806  */
32807
32808 if(Roo.dd.DropZone){
32809     
32810 Roo.tree.TreeDropZone = function(tree, config){
32811     this.allowParentInsert = false;
32812     this.allowContainerDrop = false;
32813     this.appendOnly = false;
32814     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
32815     this.tree = tree;
32816     this.lastInsertClass = "x-tree-no-status";
32817     this.dragOverData = {};
32818 };
32819
32820 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
32821     ddGroup : "TreeDD",
32822     
32823     expandDelay : 1000,
32824     
32825     expandNode : function(node){
32826         if(node.hasChildNodes() && !node.isExpanded()){
32827             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
32828         }
32829     },
32830     
32831     queueExpand : function(node){
32832         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
32833     },
32834     
32835     cancelExpand : function(){
32836         if(this.expandProcId){
32837             clearTimeout(this.expandProcId);
32838             this.expandProcId = false;
32839         }
32840     },
32841     
32842     isValidDropPoint : function(n, pt, dd, e, data){
32843         if(!n || !data){ return false; }
32844         var targetNode = n.node;
32845         var dropNode = data.node;
32846         // default drop rules
32847         if(!(targetNode && targetNode.isTarget && pt)){
32848             return false;
32849         }
32850         if(pt == "append" && targetNode.allowChildren === false){
32851             return false;
32852         }
32853         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
32854             return false;
32855         }
32856         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
32857             return false;
32858         }
32859         // reuse the object
32860         var overEvent = this.dragOverData;
32861         overEvent.tree = this.tree;
32862         overEvent.target = targetNode;
32863         overEvent.data = data;
32864         overEvent.point = pt;
32865         overEvent.source = dd;
32866         overEvent.rawEvent = e;
32867         overEvent.dropNode = dropNode;
32868         overEvent.cancel = false;  
32869         var result = this.tree.fireEvent("nodedragover", overEvent);
32870         return overEvent.cancel === false && result !== false;
32871     },
32872     
32873     getDropPoint : function(e, n, dd){
32874         var tn = n.node;
32875         if(tn.isRoot){
32876             return tn.allowChildren !== false ? "append" : false; // always append for root
32877         }
32878         var dragEl = n.ddel;
32879         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
32880         var y = Roo.lib.Event.getPageY(e);
32881         //var noAppend = tn.allowChildren === false || tn.isLeaf();
32882         
32883         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
32884         var noAppend = tn.allowChildren === false;
32885         if(this.appendOnly || tn.parentNode.allowChildren === false){
32886             return noAppend ? false : "append";
32887         }
32888         var noBelow = false;
32889         if(!this.allowParentInsert){
32890             noBelow = tn.hasChildNodes() && tn.isExpanded();
32891         }
32892         var q = (b - t) / (noAppend ? 2 : 3);
32893         if(y >= t && y < (t + q)){
32894             return "above";
32895         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
32896             return "below";
32897         }else{
32898             return "append";
32899         }
32900     },
32901     
32902     onNodeEnter : function(n, dd, e, data){
32903         this.cancelExpand();
32904     },
32905     
32906     onNodeOver : function(n, dd, e, data){
32907         var pt = this.getDropPoint(e, n, dd);
32908         var node = n.node;
32909         
32910         // auto node expand check
32911         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
32912             this.queueExpand(node);
32913         }else if(pt != "append"){
32914             this.cancelExpand();
32915         }
32916         
32917         // set the insert point style on the target node
32918         var returnCls = this.dropNotAllowed;
32919         if(this.isValidDropPoint(n, pt, dd, e, data)){
32920            if(pt){
32921                var el = n.ddel;
32922                var cls;
32923                if(pt == "above"){
32924                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
32925                    cls = "x-tree-drag-insert-above";
32926                }else if(pt == "below"){
32927                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
32928                    cls = "x-tree-drag-insert-below";
32929                }else{
32930                    returnCls = "x-tree-drop-ok-append";
32931                    cls = "x-tree-drag-append";
32932                }
32933                if(this.lastInsertClass != cls){
32934                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
32935                    this.lastInsertClass = cls;
32936                }
32937            }
32938        }
32939        return returnCls;
32940     },
32941     
32942     onNodeOut : function(n, dd, e, data){
32943         this.cancelExpand();
32944         this.removeDropIndicators(n);
32945     },
32946     
32947     onNodeDrop : function(n, dd, e, data){
32948         var point = this.getDropPoint(e, n, dd);
32949         var targetNode = n.node;
32950         targetNode.ui.startDrop();
32951         if(!this.isValidDropPoint(n, point, dd, e, data)){
32952             targetNode.ui.endDrop();
32953             return false;
32954         }
32955         // first try to find the drop node
32956         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
32957         var dropEvent = {
32958             tree : this.tree,
32959             target: targetNode,
32960             data: data,
32961             point: point,
32962             source: dd,
32963             rawEvent: e,
32964             dropNode: dropNode,
32965             cancel: !dropNode   
32966         };
32967         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
32968         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
32969             targetNode.ui.endDrop();
32970             return false;
32971         }
32972         // allow target changing
32973         targetNode = dropEvent.target;
32974         if(point == "append" && !targetNode.isExpanded()){
32975             targetNode.expand(false, null, function(){
32976                 this.completeDrop(dropEvent);
32977             }.createDelegate(this));
32978         }else{
32979             this.completeDrop(dropEvent);
32980         }
32981         return true;
32982     },
32983     
32984     completeDrop : function(de){
32985         var ns = de.dropNode, p = de.point, t = de.target;
32986         if(!(ns instanceof Array)){
32987             ns = [ns];
32988         }
32989         var n;
32990         for(var i = 0, len = ns.length; i < len; i++){
32991             n = ns[i];
32992             if(p == "above"){
32993                 t.parentNode.insertBefore(n, t);
32994             }else if(p == "below"){
32995                 t.parentNode.insertBefore(n, t.nextSibling);
32996             }else{
32997                 t.appendChild(n);
32998             }
32999         }
33000         n.ui.focus();
33001         if(this.tree.hlDrop){
33002             n.ui.highlight();
33003         }
33004         t.ui.endDrop();
33005         this.tree.fireEvent("nodedrop", de);
33006     },
33007     
33008     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33009         if(this.tree.hlDrop){
33010             dropNode.ui.focus();
33011             dropNode.ui.highlight();
33012         }
33013         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33014     },
33015     
33016     getTree : function(){
33017         return this.tree;
33018     },
33019     
33020     removeDropIndicators : function(n){
33021         if(n && n.ddel){
33022             var el = n.ddel;
33023             Roo.fly(el).removeClass([
33024                     "x-tree-drag-insert-above",
33025                     "x-tree-drag-insert-below",
33026                     "x-tree-drag-append"]);
33027             this.lastInsertClass = "_noclass";
33028         }
33029     },
33030     
33031     beforeDragDrop : function(target, e, id){
33032         this.cancelExpand();
33033         return true;
33034     },
33035     
33036     afterRepair : function(data){
33037         if(data && Roo.enableFx){
33038             data.node.ui.highlight();
33039         }
33040         this.hideProxy();
33041     }    
33042 });
33043
33044 }
33045 /*
33046  * Based on:
33047  * Ext JS Library 1.1.1
33048  * Copyright(c) 2006-2007, Ext JS, LLC.
33049  *
33050  * Originally Released Under LGPL - original licence link has changed is not relivant.
33051  *
33052  * Fork - LGPL
33053  * <script type="text/javascript">
33054  */
33055  
33056
33057 if(Roo.dd.DragZone){
33058 Roo.tree.TreeDragZone = function(tree, config){
33059     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33060     this.tree = tree;
33061 };
33062
33063 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33064     ddGroup : "TreeDD",
33065     
33066     onBeforeDrag : function(data, e){
33067         var n = data.node;
33068         return n && n.draggable && !n.disabled;
33069     },
33070     
33071     onInitDrag : function(e){
33072         var data = this.dragData;
33073         this.tree.getSelectionModel().select(data.node);
33074         this.proxy.update("");
33075         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33076         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33077     },
33078     
33079     getRepairXY : function(e, data){
33080         return data.node.ui.getDDRepairXY();
33081     },
33082     
33083     onEndDrag : function(data, e){
33084         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33085     },
33086     
33087     onValidDrop : function(dd, e, id){
33088         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33089         this.hideProxy();
33090     },
33091     
33092     beforeInvalidDrop : function(e, id){
33093         // this scrolls the original position back into view
33094         var sm = this.tree.getSelectionModel();
33095         sm.clearSelections();
33096         sm.select(this.dragData.node);
33097     }
33098 });
33099 }/*
33100  * Based on:
33101  * Ext JS Library 1.1.1
33102  * Copyright(c) 2006-2007, Ext JS, LLC.
33103  *
33104  * Originally Released Under LGPL - original licence link has changed is not relivant.
33105  *
33106  * Fork - LGPL
33107  * <script type="text/javascript">
33108  */
33109 /**
33110  * @class Roo.tree.TreeEditor
33111  * @extends Roo.Editor
33112  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33113  * as the editor field.
33114  * @constructor
33115  * @param {TreePanel} tree
33116  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33117  */
33118 Roo.tree.TreeEditor = function(tree, config){
33119     config = config || {};
33120     var field = config.events ? config : new Roo.form.TextField(config);
33121     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33122
33123     this.tree = tree;
33124
33125     tree.on('beforeclick', this.beforeNodeClick, this);
33126     tree.getTreeEl().on('mousedown', this.hide, this);
33127     this.on('complete', this.updateNode, this);
33128     this.on('beforestartedit', this.fitToTree, this);
33129     this.on('startedit', this.bindScroll, this, {delay:10});
33130     this.on('specialkey', this.onSpecialKey, this);
33131 };
33132
33133 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33134     /**
33135      * @cfg {String} alignment
33136      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33137      */
33138     alignment: "l-l",
33139     // inherit
33140     autoSize: false,
33141     /**
33142      * @cfg {Boolean} hideEl
33143      * True to hide the bound element while the editor is displayed (defaults to false)
33144      */
33145     hideEl : false,
33146     /**
33147      * @cfg {String} cls
33148      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33149      */
33150     cls: "x-small-editor x-tree-editor",
33151     /**
33152      * @cfg {Boolean} shim
33153      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33154      */
33155     shim:false,
33156     // inherit
33157     shadow:"frame",
33158     /**
33159      * @cfg {Number} maxWidth
33160      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33161      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33162      * scroll and client offsets into account prior to each edit.
33163      */
33164     maxWidth: 250,
33165
33166     editDelay : 350,
33167
33168     // private
33169     fitToTree : function(ed, el){
33170         var td = this.tree.getTreeEl().dom, nd = el.dom;
33171         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33172             td.scrollLeft = nd.offsetLeft;
33173         }
33174         var w = Math.min(
33175                 this.maxWidth,
33176                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33177         this.setSize(w, '');
33178     },
33179
33180     // private
33181     triggerEdit : function(node){
33182         this.completeEdit();
33183         this.editNode = node;
33184         this.startEdit(node.ui.textNode, node.text);
33185     },
33186
33187     // private
33188     bindScroll : function(){
33189         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33190     },
33191
33192     // private
33193     beforeNodeClick : function(node, e){
33194         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33195         this.lastClick = new Date();
33196         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33197             e.stopEvent();
33198             this.triggerEdit(node);
33199             return false;
33200         }
33201     },
33202
33203     // private
33204     updateNode : function(ed, value){
33205         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33206         this.editNode.setText(value);
33207     },
33208
33209     // private
33210     onHide : function(){
33211         Roo.tree.TreeEditor.superclass.onHide.call(this);
33212         if(this.editNode){
33213             this.editNode.ui.focus();
33214         }
33215     },
33216
33217     // private
33218     onSpecialKey : function(field, e){
33219         var k = e.getKey();
33220         if(k == e.ESC){
33221             e.stopEvent();
33222             this.cancelEdit();
33223         }else if(k == e.ENTER && !e.hasModifier()){
33224             e.stopEvent();
33225             this.completeEdit();
33226         }
33227     }
33228 });//<Script type="text/javascript">
33229 /*
33230  * Based on:
33231  * Ext JS Library 1.1.1
33232  * Copyright(c) 2006-2007, Ext JS, LLC.
33233  *
33234  * Originally Released Under LGPL - original licence link has changed is not relivant.
33235  *
33236  * Fork - LGPL
33237  * <script type="text/javascript">
33238  */
33239  
33240 /**
33241  * Not documented??? - probably should be...
33242  */
33243
33244 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33245     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33246     
33247     renderElements : function(n, a, targetNode, bulkRender){
33248         //consel.log("renderElements?");
33249         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33250
33251         var t = n.getOwnerTree();
33252         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33253         
33254         var cols = t.columns;
33255         var bw = t.borderWidth;
33256         var c = cols[0];
33257         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33258          var cb = typeof a.checked == "boolean";
33259         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33260         var colcls = 'x-t-' + tid + '-c0';
33261         var buf = [
33262             '<li class="x-tree-node">',
33263             
33264                 
33265                 '<div class="x-tree-node-el ', a.cls,'">',
33266                     // extran...
33267                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33268                 
33269                 
33270                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33271                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33272                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33273                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33274                            (a.iconCls ? ' '+a.iconCls : ''),
33275                            '" unselectable="on" />',
33276                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33277                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33278                              
33279                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33280                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33281                             '<span unselectable="on" qtip="' + tx + '">',
33282                              tx,
33283                              '</span></a>' ,
33284                     '</div>',
33285                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33286                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33287                  ];
33288         
33289         for(var i = 1, len = cols.length; i < len; i++){
33290             c = cols[i];
33291             colcls = 'x-t-' + tid + '-c' +i;
33292             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33293             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33294                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33295                       "</div>");
33296          }
33297          
33298          buf.push(
33299             '</a>',
33300             '<div class="x-clear"></div></div>',
33301             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33302             "</li>");
33303         
33304         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33305             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33306                                 n.nextSibling.ui.getEl(), buf.join(""));
33307         }else{
33308             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33309         }
33310         var el = this.wrap.firstChild;
33311         this.elRow = el;
33312         this.elNode = el.firstChild;
33313         this.ranchor = el.childNodes[1];
33314         this.ctNode = this.wrap.childNodes[1];
33315         var cs = el.firstChild.childNodes;
33316         this.indentNode = cs[0];
33317         this.ecNode = cs[1];
33318         this.iconNode = cs[2];
33319         var index = 3;
33320         if(cb){
33321             this.checkbox = cs[3];
33322             index++;
33323         }
33324         this.anchor = cs[index];
33325         
33326         this.textNode = cs[index].firstChild;
33327         
33328         //el.on("click", this.onClick, this);
33329         //el.on("dblclick", this.onDblClick, this);
33330         
33331         
33332        // console.log(this);
33333     },
33334     initEvents : function(){
33335         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33336         
33337             
33338         var a = this.ranchor;
33339
33340         var el = Roo.get(a);
33341
33342         if(Roo.isOpera){ // opera render bug ignores the CSS
33343             el.setStyle("text-decoration", "none");
33344         }
33345
33346         el.on("click", this.onClick, this);
33347         el.on("dblclick", this.onDblClick, this);
33348         el.on("contextmenu", this.onContextMenu, this);
33349         
33350     },
33351     
33352     /*onSelectedChange : function(state){
33353         if(state){
33354             this.focus();
33355             this.addClass("x-tree-selected");
33356         }else{
33357             //this.blur();
33358             this.removeClass("x-tree-selected");
33359         }
33360     },*/
33361     addClass : function(cls){
33362         if(this.elRow){
33363             Roo.fly(this.elRow).addClass(cls);
33364         }
33365         
33366     },
33367     
33368     
33369     removeClass : function(cls){
33370         if(this.elRow){
33371             Roo.fly(this.elRow).removeClass(cls);
33372         }
33373     }
33374
33375     
33376     
33377 });//<Script type="text/javascript">
33378
33379 /*
33380  * Based on:
33381  * Ext JS Library 1.1.1
33382  * Copyright(c) 2006-2007, Ext JS, LLC.
33383  *
33384  * Originally Released Under LGPL - original licence link has changed is not relivant.
33385  *
33386  * Fork - LGPL
33387  * <script type="text/javascript">
33388  */
33389  
33390
33391 /**
33392  * @class Roo.tree.ColumnTree
33393  * @extends Roo.data.TreePanel
33394  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33395  * @cfg {int} borderWidth  compined right/left border allowance
33396  * @constructor
33397  * @param {String/HTMLElement/Element} el The container element
33398  * @param {Object} config
33399  */
33400 Roo.tree.ColumnTree =  function(el, config)
33401 {
33402    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33403    this.addEvents({
33404         /**
33405         * @event resize
33406         * Fire this event on a container when it resizes
33407         * @param {int} w Width
33408         * @param {int} h Height
33409         */
33410        "resize" : true
33411     });
33412     this.on('resize', this.onResize, this);
33413 };
33414
33415 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33416     //lines:false,
33417     
33418     
33419     borderWidth: Roo.isBorderBox ? 0 : 2, 
33420     headEls : false,
33421     
33422     render : function(){
33423         // add the header.....
33424        
33425         Roo.tree.ColumnTree.superclass.render.apply(this);
33426         
33427         this.el.addClass('x-column-tree');
33428         
33429         this.headers = this.el.createChild(
33430             {cls:'x-tree-headers'},this.innerCt.dom);
33431    
33432         var cols = this.columns, c;
33433         var totalWidth = 0;
33434         this.headEls = [];
33435         var  len = cols.length;
33436         for(var i = 0; i < len; i++){
33437              c = cols[i];
33438              totalWidth += c.width;
33439             this.headEls.push(this.headers.createChild({
33440                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33441                  cn: {
33442                      cls:'x-tree-hd-text',
33443                      html: c.header
33444                  },
33445                  style:'width:'+(c.width-this.borderWidth)+'px;'
33446              }));
33447         }
33448         this.headers.createChild({cls:'x-clear'});
33449         // prevent floats from wrapping when clipped
33450         this.headers.setWidth(totalWidth);
33451         //this.innerCt.setWidth(totalWidth);
33452         this.innerCt.setStyle({ overflow: 'auto' });
33453         this.onResize(this.width, this.height);
33454              
33455         
33456     },
33457     onResize : function(w,h)
33458     {
33459         this.height = h;
33460         this.width = w;
33461         // resize cols..
33462         this.innerCt.setWidth(this.width);
33463         this.innerCt.setHeight(this.height-20);
33464         
33465         // headers...
33466         var cols = this.columns, c;
33467         var totalWidth = 0;
33468         var expEl = false;
33469         var len = cols.length;
33470         for(var i = 0; i < len; i++){
33471             c = cols[i];
33472             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33473                 // it's the expander..
33474                 expEl  = this.headEls[i];
33475                 continue;
33476             }
33477             totalWidth += c.width;
33478             
33479         }
33480         if (expEl) {
33481             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33482         }
33483         this.headers.setWidth(w-20);
33484
33485         
33486         
33487         
33488     }
33489 });
33490 /*
33491  * Based on:
33492  * Ext JS Library 1.1.1
33493  * Copyright(c) 2006-2007, Ext JS, LLC.
33494  *
33495  * Originally Released Under LGPL - original licence link has changed is not relivant.
33496  *
33497  * Fork - LGPL
33498  * <script type="text/javascript">
33499  */
33500  
33501 /**
33502  * @class Roo.menu.Menu
33503  * @extends Roo.util.Observable
33504  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33505  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33506  * @constructor
33507  * Creates a new Menu
33508  * @param {Object} config Configuration options
33509  */
33510 Roo.menu.Menu = function(config){
33511     Roo.apply(this, config);
33512     this.id = this.id || Roo.id();
33513     this.addEvents({
33514         /**
33515          * @event beforeshow
33516          * Fires before this menu is displayed
33517          * @param {Roo.menu.Menu} this
33518          */
33519         beforeshow : true,
33520         /**
33521          * @event beforehide
33522          * Fires before this menu is hidden
33523          * @param {Roo.menu.Menu} this
33524          */
33525         beforehide : true,
33526         /**
33527          * @event show
33528          * Fires after this menu is displayed
33529          * @param {Roo.menu.Menu} this
33530          */
33531         show : true,
33532         /**
33533          * @event hide
33534          * Fires after this menu is hidden
33535          * @param {Roo.menu.Menu} this
33536          */
33537         hide : true,
33538         /**
33539          * @event click
33540          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33541          * @param {Roo.menu.Menu} this
33542          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33543          * @param {Roo.EventObject} e
33544          */
33545         click : true,
33546         /**
33547          * @event mouseover
33548          * Fires when the mouse is hovering over this menu
33549          * @param {Roo.menu.Menu} this
33550          * @param {Roo.EventObject} e
33551          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33552          */
33553         mouseover : true,
33554         /**
33555          * @event mouseout
33556          * Fires when the mouse exits this menu
33557          * @param {Roo.menu.Menu} this
33558          * @param {Roo.EventObject} e
33559          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33560          */
33561         mouseout : true,
33562         /**
33563          * @event itemclick
33564          * Fires when a menu item contained in this menu is clicked
33565          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33566          * @param {Roo.EventObject} e
33567          */
33568         itemclick: true
33569     });
33570     if (this.registerMenu) {
33571         Roo.menu.MenuMgr.register(this);
33572     }
33573     
33574     var mis = this.items;
33575     this.items = new Roo.util.MixedCollection();
33576     if(mis){
33577         this.add.apply(this, mis);
33578     }
33579 };
33580
33581 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33582     /**
33583      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33584      */
33585     minWidth : 120,
33586     /**
33587      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33588      * for bottom-right shadow (defaults to "sides")
33589      */
33590     shadow : "sides",
33591     /**
33592      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33593      * this menu (defaults to "tl-tr?")
33594      */
33595     subMenuAlign : "tl-tr?",
33596     /**
33597      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33598      * relative to its element of origin (defaults to "tl-bl?")
33599      */
33600     defaultAlign : "tl-bl?",
33601     /**
33602      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33603      */
33604     allowOtherMenus : false,
33605     /**
33606      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33607      */
33608     registerMenu : true,
33609
33610     hidden:true,
33611
33612     // private
33613     render : function(){
33614         if(this.el){
33615             return;
33616         }
33617         var el = this.el = new Roo.Layer({
33618             cls: "x-menu",
33619             shadow:this.shadow,
33620             constrain: false,
33621             parentEl: this.parentEl || document.body,
33622             zindex:15000
33623         });
33624
33625         this.keyNav = new Roo.menu.MenuNav(this);
33626
33627         if(this.plain){
33628             el.addClass("x-menu-plain");
33629         }
33630         if(this.cls){
33631             el.addClass(this.cls);
33632         }
33633         // generic focus element
33634         this.focusEl = el.createChild({
33635             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33636         });
33637         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33638         ul.on("click", this.onClick, this);
33639         ul.on("mouseover", this.onMouseOver, this);
33640         ul.on("mouseout", this.onMouseOut, this);
33641         this.items.each(function(item){
33642             var li = document.createElement("li");
33643             li.className = "x-menu-list-item";
33644             ul.dom.appendChild(li);
33645             item.render(li, this);
33646         }, this);
33647         this.ul = ul;
33648         this.autoWidth();
33649     },
33650
33651     // private
33652     autoWidth : function(){
33653         var el = this.el, ul = this.ul;
33654         if(!el){
33655             return;
33656         }
33657         var w = this.width;
33658         if(w){
33659             el.setWidth(w);
33660         }else if(Roo.isIE){
33661             el.setWidth(this.minWidth);
33662             var t = el.dom.offsetWidth; // force recalc
33663             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33664         }
33665     },
33666
33667     // private
33668     delayAutoWidth : function(){
33669         if(this.rendered){
33670             if(!this.awTask){
33671                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33672             }
33673             this.awTask.delay(20);
33674         }
33675     },
33676
33677     // private
33678     findTargetItem : function(e){
33679         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33680         if(t && t.menuItemId){
33681             return this.items.get(t.menuItemId);
33682         }
33683     },
33684
33685     // private
33686     onClick : function(e){
33687         var t;
33688         if(t = this.findTargetItem(e)){
33689             t.onClick(e);
33690             this.fireEvent("click", this, t, e);
33691         }
33692     },
33693
33694     // private
33695     setActiveItem : function(item, autoExpand){
33696         if(item != this.activeItem){
33697             if(this.activeItem){
33698                 this.activeItem.deactivate();
33699             }
33700             this.activeItem = item;
33701             item.activate(autoExpand);
33702         }else if(autoExpand){
33703             item.expandMenu();
33704         }
33705     },
33706
33707     // private
33708     tryActivate : function(start, step){
33709         var items = this.items;
33710         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33711             var item = items.get(i);
33712             if(!item.disabled && item.canActivate){
33713                 this.setActiveItem(item, false);
33714                 return item;
33715             }
33716         }
33717         return false;
33718     },
33719
33720     // private
33721     onMouseOver : function(e){
33722         var t;
33723         if(t = this.findTargetItem(e)){
33724             if(t.canActivate && !t.disabled){
33725                 this.setActiveItem(t, true);
33726             }
33727         }
33728         this.fireEvent("mouseover", this, e, t);
33729     },
33730
33731     // private
33732     onMouseOut : function(e){
33733         var t;
33734         if(t = this.findTargetItem(e)){
33735             if(t == this.activeItem && t.shouldDeactivate(e)){
33736                 this.activeItem.deactivate();
33737                 delete this.activeItem;
33738             }
33739         }
33740         this.fireEvent("mouseout", this, e, t);
33741     },
33742
33743     /**
33744      * Read-only.  Returns true if the menu is currently displayed, else false.
33745      * @type Boolean
33746      */
33747     isVisible : function(){
33748         return this.el && !this.hidden;
33749     },
33750
33751     /**
33752      * Displays this menu relative to another element
33753      * @param {String/HTMLElement/Roo.Element} element The element to align to
33754      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33755      * the element (defaults to this.defaultAlign)
33756      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33757      */
33758     show : function(el, pos, parentMenu){
33759         this.parentMenu = parentMenu;
33760         if(!this.el){
33761             this.render();
33762         }
33763         this.fireEvent("beforeshow", this);
33764         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33765     },
33766
33767     /**
33768      * Displays this menu at a specific xy position
33769      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33770      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33771      */
33772     showAt : function(xy, parentMenu, /* private: */_e){
33773         this.parentMenu = parentMenu;
33774         if(!this.el){
33775             this.render();
33776         }
33777         if(_e !== false){
33778             this.fireEvent("beforeshow", this);
33779             xy = this.el.adjustForConstraints(xy);
33780         }
33781         this.el.setXY(xy);
33782         this.el.show();
33783         this.hidden = false;
33784         this.focus();
33785         this.fireEvent("show", this);
33786     },
33787
33788     focus : function(){
33789         if(!this.hidden){
33790             this.doFocus.defer(50, this);
33791         }
33792     },
33793
33794     doFocus : function(){
33795         if(!this.hidden){
33796             this.focusEl.focus();
33797         }
33798     },
33799
33800     /**
33801      * Hides this menu and optionally all parent menus
33802      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
33803      */
33804     hide : function(deep){
33805         if(this.el && this.isVisible()){
33806             this.fireEvent("beforehide", this);
33807             if(this.activeItem){
33808                 this.activeItem.deactivate();
33809                 this.activeItem = null;
33810             }
33811             this.el.hide();
33812             this.hidden = true;
33813             this.fireEvent("hide", this);
33814         }
33815         if(deep === true && this.parentMenu){
33816             this.parentMenu.hide(true);
33817         }
33818     },
33819
33820     /**
33821      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
33822      * Any of the following are valid:
33823      * <ul>
33824      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
33825      * <li>An HTMLElement object which will be converted to a menu item</li>
33826      * <li>A menu item config object that will be created as a new menu item</li>
33827      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
33828      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
33829      * </ul>
33830      * Usage:
33831      * <pre><code>
33832 // Create the menu
33833 var menu = new Roo.menu.Menu();
33834
33835 // Create a menu item to add by reference
33836 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
33837
33838 // Add a bunch of items at once using different methods.
33839 // Only the last item added will be returned.
33840 var item = menu.add(
33841     menuItem,                // add existing item by ref
33842     'Dynamic Item',          // new TextItem
33843     '-',                     // new separator
33844     { text: 'Config Item' }  // new item by config
33845 );
33846 </code></pre>
33847      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
33848      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
33849      */
33850     add : function(){
33851         var a = arguments, l = a.length, item;
33852         for(var i = 0; i < l; i++){
33853             var el = a[i];
33854             if ((typeof(el) == "object") && el.xtype && el.xns) {
33855                 el = Roo.factory(el, Roo.menu);
33856             }
33857             
33858             if(el.render){ // some kind of Item
33859                 item = this.addItem(el);
33860             }else if(typeof el == "string"){ // string
33861                 if(el == "separator" || el == "-"){
33862                     item = this.addSeparator();
33863                 }else{
33864                     item = this.addText(el);
33865                 }
33866             }else if(el.tagName || el.el){ // element
33867                 item = this.addElement(el);
33868             }else if(typeof el == "object"){ // must be menu item config?
33869                 item = this.addMenuItem(el);
33870             }
33871         }
33872         return item;
33873     },
33874
33875     /**
33876      * Returns this menu's underlying {@link Roo.Element} object
33877      * @return {Roo.Element} The element
33878      */
33879     getEl : function(){
33880         if(!this.el){
33881             this.render();
33882         }
33883         return this.el;
33884     },
33885
33886     /**
33887      * Adds a separator bar to the menu
33888      * @return {Roo.menu.Item} The menu item that was added
33889      */
33890     addSeparator : function(){
33891         return this.addItem(new Roo.menu.Separator());
33892     },
33893
33894     /**
33895      * Adds an {@link Roo.Element} object to the menu
33896      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
33897      * @return {Roo.menu.Item} The menu item that was added
33898      */
33899     addElement : function(el){
33900         return this.addItem(new Roo.menu.BaseItem(el));
33901     },
33902
33903     /**
33904      * Adds an existing object based on {@link Roo.menu.Item} to the menu
33905      * @param {Roo.menu.Item} item The menu item to add
33906      * @return {Roo.menu.Item} The menu item that was added
33907      */
33908     addItem : function(item){
33909         this.items.add(item);
33910         if(this.ul){
33911             var li = document.createElement("li");
33912             li.className = "x-menu-list-item";
33913             this.ul.dom.appendChild(li);
33914             item.render(li, this);
33915             this.delayAutoWidth();
33916         }
33917         return item;
33918     },
33919
33920     /**
33921      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
33922      * @param {Object} config A MenuItem config object
33923      * @return {Roo.menu.Item} The menu item that was added
33924      */
33925     addMenuItem : function(config){
33926         if(!(config instanceof Roo.menu.Item)){
33927             if(typeof config.checked == "boolean"){ // must be check menu item config?
33928                 config = new Roo.menu.CheckItem(config);
33929             }else{
33930                 config = new Roo.menu.Item(config);
33931             }
33932         }
33933         return this.addItem(config);
33934     },
33935
33936     /**
33937      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
33938      * @param {String} text The text to display in the menu item
33939      * @return {Roo.menu.Item} The menu item that was added
33940      */
33941     addText : function(text){
33942         return this.addItem(new Roo.menu.TextItem({ text : text }));
33943     },
33944
33945     /**
33946      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
33947      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
33948      * @param {Roo.menu.Item} item The menu item to add
33949      * @return {Roo.menu.Item} The menu item that was added
33950      */
33951     insert : function(index, item){
33952         this.items.insert(index, item);
33953         if(this.ul){
33954             var li = document.createElement("li");
33955             li.className = "x-menu-list-item";
33956             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
33957             item.render(li, this);
33958             this.delayAutoWidth();
33959         }
33960         return item;
33961     },
33962
33963     /**
33964      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
33965      * @param {Roo.menu.Item} item The menu item to remove
33966      */
33967     remove : function(item){
33968         this.items.removeKey(item.id);
33969         item.destroy();
33970     },
33971
33972     /**
33973      * Removes and destroys all items in the menu
33974      */
33975     removeAll : function(){
33976         var f;
33977         while(f = this.items.first()){
33978             this.remove(f);
33979         }
33980     }
33981 });
33982
33983 // MenuNav is a private utility class used internally by the Menu
33984 Roo.menu.MenuNav = function(menu){
33985     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
33986     this.scope = this.menu = menu;
33987 };
33988
33989 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
33990     doRelay : function(e, h){
33991         var k = e.getKey();
33992         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
33993             this.menu.tryActivate(0, 1);
33994             return false;
33995         }
33996         return h.call(this.scope || this, e, this.menu);
33997     },
33998
33999     up : function(e, m){
34000         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34001             m.tryActivate(m.items.length-1, -1);
34002         }
34003     },
34004
34005     down : function(e, m){
34006         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34007             m.tryActivate(0, 1);
34008         }
34009     },
34010
34011     right : function(e, m){
34012         if(m.activeItem){
34013             m.activeItem.expandMenu(true);
34014         }
34015     },
34016
34017     left : function(e, m){
34018         m.hide();
34019         if(m.parentMenu && m.parentMenu.activeItem){
34020             m.parentMenu.activeItem.activate();
34021         }
34022     },
34023
34024     enter : function(e, m){
34025         if(m.activeItem){
34026             e.stopPropagation();
34027             m.activeItem.onClick(e);
34028             m.fireEvent("click", this, m.activeItem);
34029             return true;
34030         }
34031     }
34032 });/*
34033  * Based on:
34034  * Ext JS Library 1.1.1
34035  * Copyright(c) 2006-2007, Ext JS, LLC.
34036  *
34037  * Originally Released Under LGPL - original licence link has changed is not relivant.
34038  *
34039  * Fork - LGPL
34040  * <script type="text/javascript">
34041  */
34042  
34043 /**
34044  * @class Roo.menu.MenuMgr
34045  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34046  * @singleton
34047  */
34048 Roo.menu.MenuMgr = function(){
34049    var menus, active, groups = {}, attached = false, lastShow = new Date();
34050
34051    // private - called when first menu is created
34052    function init(){
34053        menus = {};
34054        active = new Roo.util.MixedCollection();
34055        Roo.get(document).addKeyListener(27, function(){
34056            if(active.length > 0){
34057                hideAll();
34058            }
34059        });
34060    }
34061
34062    // private
34063    function hideAll(){
34064        if(active && active.length > 0){
34065            var c = active.clone();
34066            c.each(function(m){
34067                m.hide();
34068            });
34069        }
34070    }
34071
34072    // private
34073    function onHide(m){
34074        active.remove(m);
34075        if(active.length < 1){
34076            Roo.get(document).un("mousedown", onMouseDown);
34077            attached = false;
34078        }
34079    }
34080
34081    // private
34082    function onShow(m){
34083        var last = active.last();
34084        lastShow = new Date();
34085        active.add(m);
34086        if(!attached){
34087            Roo.get(document).on("mousedown", onMouseDown);
34088            attached = true;
34089        }
34090        if(m.parentMenu){
34091           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34092           m.parentMenu.activeChild = m;
34093        }else if(last && last.isVisible()){
34094           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34095        }
34096    }
34097
34098    // private
34099    function onBeforeHide(m){
34100        if(m.activeChild){
34101            m.activeChild.hide();
34102        }
34103        if(m.autoHideTimer){
34104            clearTimeout(m.autoHideTimer);
34105            delete m.autoHideTimer;
34106        }
34107    }
34108
34109    // private
34110    function onBeforeShow(m){
34111        var pm = m.parentMenu;
34112        if(!pm && !m.allowOtherMenus){
34113            hideAll();
34114        }else if(pm && pm.activeChild && active != m){
34115            pm.activeChild.hide();
34116        }
34117    }
34118
34119    // private
34120    function onMouseDown(e){
34121        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34122            hideAll();
34123        }
34124    }
34125
34126    // private
34127    function onBeforeCheck(mi, state){
34128        if(state){
34129            var g = groups[mi.group];
34130            for(var i = 0, l = g.length; i < l; i++){
34131                if(g[i] != mi){
34132                    g[i].setChecked(false);
34133                }
34134            }
34135        }
34136    }
34137
34138    return {
34139
34140        /**
34141         * Hides all menus that are currently visible
34142         */
34143        hideAll : function(){
34144             hideAll();  
34145        },
34146
34147        // private
34148        register : function(menu){
34149            if(!menus){
34150                init();
34151            }
34152            menus[menu.id] = menu;
34153            menu.on("beforehide", onBeforeHide);
34154            menu.on("hide", onHide);
34155            menu.on("beforeshow", onBeforeShow);
34156            menu.on("show", onShow);
34157            var g = menu.group;
34158            if(g && menu.events["checkchange"]){
34159                if(!groups[g]){
34160                    groups[g] = [];
34161                }
34162                groups[g].push(menu);
34163                menu.on("checkchange", onCheck);
34164            }
34165        },
34166
34167         /**
34168          * Returns a {@link Roo.menu.Menu} object
34169          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34170          * be used to generate and return a new Menu instance.
34171          */
34172        get : function(menu){
34173            if(typeof menu == "string"){ // menu id
34174                return menus[menu];
34175            }else if(menu.events){  // menu instance
34176                return menu;
34177            }else if(typeof menu.length == 'number'){ // array of menu items?
34178                return new Roo.menu.Menu({items:menu});
34179            }else{ // otherwise, must be a config
34180                return new Roo.menu.Menu(menu);
34181            }
34182        },
34183
34184        // private
34185        unregister : function(menu){
34186            delete menus[menu.id];
34187            menu.un("beforehide", onBeforeHide);
34188            menu.un("hide", onHide);
34189            menu.un("beforeshow", onBeforeShow);
34190            menu.un("show", onShow);
34191            var g = menu.group;
34192            if(g && menu.events["checkchange"]){
34193                groups[g].remove(menu);
34194                menu.un("checkchange", onCheck);
34195            }
34196        },
34197
34198        // private
34199        registerCheckable : function(menuItem){
34200            var g = menuItem.group;
34201            if(g){
34202                if(!groups[g]){
34203                    groups[g] = [];
34204                }
34205                groups[g].push(menuItem);
34206                menuItem.on("beforecheckchange", onBeforeCheck);
34207            }
34208        },
34209
34210        // private
34211        unregisterCheckable : function(menuItem){
34212            var g = menuItem.group;
34213            if(g){
34214                groups[g].remove(menuItem);
34215                menuItem.un("beforecheckchange", onBeforeCheck);
34216            }
34217        }
34218    };
34219 }();/*
34220  * Based on:
34221  * Ext JS Library 1.1.1
34222  * Copyright(c) 2006-2007, Ext JS, LLC.
34223  *
34224  * Originally Released Under LGPL - original licence link has changed is not relivant.
34225  *
34226  * Fork - LGPL
34227  * <script type="text/javascript">
34228  */
34229  
34230
34231 /**
34232  * @class Roo.menu.BaseItem
34233  * @extends Roo.Component
34234  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34235  * management and base configuration options shared by all menu components.
34236  * @constructor
34237  * Creates a new BaseItem
34238  * @param {Object} config Configuration options
34239  */
34240 Roo.menu.BaseItem = function(config){
34241     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34242
34243     this.addEvents({
34244         /**
34245          * @event click
34246          * Fires when this item is clicked
34247          * @param {Roo.menu.BaseItem} this
34248          * @param {Roo.EventObject} e
34249          */
34250         click: true,
34251         /**
34252          * @event activate
34253          * Fires when this item is activated
34254          * @param {Roo.menu.BaseItem} this
34255          */
34256         activate : true,
34257         /**
34258          * @event deactivate
34259          * Fires when this item is deactivated
34260          * @param {Roo.menu.BaseItem} this
34261          */
34262         deactivate : true
34263     });
34264
34265     if(this.handler){
34266         this.on("click", this.handler, this.scope, true);
34267     }
34268 };
34269
34270 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34271     /**
34272      * @cfg {Function} handler
34273      * A function that will handle the click event of this menu item (defaults to undefined)
34274      */
34275     /**
34276      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34277      */
34278     canActivate : false,
34279     /**
34280      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34281      */
34282     activeClass : "x-menu-item-active",
34283     /**
34284      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34285      */
34286     hideOnClick : true,
34287     /**
34288      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34289      */
34290     hideDelay : 100,
34291
34292     // private
34293     ctype: "Roo.menu.BaseItem",
34294
34295     // private
34296     actionMode : "container",
34297
34298     // private
34299     render : function(container, parentMenu){
34300         this.parentMenu = parentMenu;
34301         Roo.menu.BaseItem.superclass.render.call(this, container);
34302         this.container.menuItemId = this.id;
34303     },
34304
34305     // private
34306     onRender : function(container, position){
34307         this.el = Roo.get(this.el);
34308         container.dom.appendChild(this.el.dom);
34309     },
34310
34311     // private
34312     onClick : function(e){
34313         if(!this.disabled && this.fireEvent("click", this, e) !== false
34314                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34315             this.handleClick(e);
34316         }else{
34317             e.stopEvent();
34318         }
34319     },
34320
34321     // private
34322     activate : function(){
34323         if(this.disabled){
34324             return false;
34325         }
34326         var li = this.container;
34327         li.addClass(this.activeClass);
34328         this.region = li.getRegion().adjust(2, 2, -2, -2);
34329         this.fireEvent("activate", this);
34330         return true;
34331     },
34332
34333     // private
34334     deactivate : function(){
34335         this.container.removeClass(this.activeClass);
34336         this.fireEvent("deactivate", this);
34337     },
34338
34339     // private
34340     shouldDeactivate : function(e){
34341         return !this.region || !this.region.contains(e.getPoint());
34342     },
34343
34344     // private
34345     handleClick : function(e){
34346         if(this.hideOnClick){
34347             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34348         }
34349     },
34350
34351     // private
34352     expandMenu : function(autoActivate){
34353         // do nothing
34354     },
34355
34356     // private
34357     hideMenu : function(){
34358         // do nothing
34359     }
34360 });/*
34361  * Based on:
34362  * Ext JS Library 1.1.1
34363  * Copyright(c) 2006-2007, Ext JS, LLC.
34364  *
34365  * Originally Released Under LGPL - original licence link has changed is not relivant.
34366  *
34367  * Fork - LGPL
34368  * <script type="text/javascript">
34369  */
34370  
34371 /**
34372  * @class Roo.menu.Adapter
34373  * @extends Roo.menu.BaseItem
34374  * 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.
34375  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34376  * @constructor
34377  * Creates a new Adapter
34378  * @param {Object} config Configuration options
34379  */
34380 Roo.menu.Adapter = function(component, config){
34381     Roo.menu.Adapter.superclass.constructor.call(this, config);
34382     this.component = component;
34383 };
34384 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34385     // private
34386     canActivate : true,
34387
34388     // private
34389     onRender : function(container, position){
34390         this.component.render(container);
34391         this.el = this.component.getEl();
34392     },
34393
34394     // private
34395     activate : function(){
34396         if(this.disabled){
34397             return false;
34398         }
34399         this.component.focus();
34400         this.fireEvent("activate", this);
34401         return true;
34402     },
34403
34404     // private
34405     deactivate : function(){
34406         this.fireEvent("deactivate", this);
34407     },
34408
34409     // private
34410     disable : function(){
34411         this.component.disable();
34412         Roo.menu.Adapter.superclass.disable.call(this);
34413     },
34414
34415     // private
34416     enable : function(){
34417         this.component.enable();
34418         Roo.menu.Adapter.superclass.enable.call(this);
34419     }
34420 });/*
34421  * Based on:
34422  * Ext JS Library 1.1.1
34423  * Copyright(c) 2006-2007, Ext JS, LLC.
34424  *
34425  * Originally Released Under LGPL - original licence link has changed is not relivant.
34426  *
34427  * Fork - LGPL
34428  * <script type="text/javascript">
34429  */
34430
34431 /**
34432  * @class Roo.menu.TextItem
34433  * @extends Roo.menu.BaseItem
34434  * Adds a static text string to a menu, usually used as either a heading or group separator.
34435  * Note: old style constructor with text is still supported.
34436  * 
34437  * @constructor
34438  * Creates a new TextItem
34439  * @param {Object} cfg Configuration
34440  */
34441 Roo.menu.TextItem = function(cfg){
34442     if (typeof(cfg) == 'string') {
34443         this.text = cfg;
34444     } else {
34445         Roo.apply(this,cfg);
34446     }
34447     
34448     Roo.menu.TextItem.superclass.constructor.call(this);
34449 };
34450
34451 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34452     /**
34453      * @cfg {Boolean} text Text to show on item.
34454      */
34455     text : '',
34456     
34457     /**
34458      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34459      */
34460     hideOnClick : false,
34461     /**
34462      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34463      */
34464     itemCls : "x-menu-text",
34465
34466     // private
34467     onRender : function(){
34468         var s = document.createElement("span");
34469         s.className = this.itemCls;
34470         s.innerHTML = this.text;
34471         this.el = s;
34472         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34473     }
34474 });/*
34475  * Based on:
34476  * Ext JS Library 1.1.1
34477  * Copyright(c) 2006-2007, Ext JS, LLC.
34478  *
34479  * Originally Released Under LGPL - original licence link has changed is not relivant.
34480  *
34481  * Fork - LGPL
34482  * <script type="text/javascript">
34483  */
34484
34485 /**
34486  * @class Roo.menu.Separator
34487  * @extends Roo.menu.BaseItem
34488  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34489  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34490  * @constructor
34491  * @param {Object} config Configuration options
34492  */
34493 Roo.menu.Separator = function(config){
34494     Roo.menu.Separator.superclass.constructor.call(this, config);
34495 };
34496
34497 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34498     /**
34499      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34500      */
34501     itemCls : "x-menu-sep",
34502     /**
34503      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34504      */
34505     hideOnClick : false,
34506
34507     // private
34508     onRender : function(li){
34509         var s = document.createElement("span");
34510         s.className = this.itemCls;
34511         s.innerHTML = "&#160;";
34512         this.el = s;
34513         li.addClass("x-menu-sep-li");
34514         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34515     }
34516 });/*
34517  * Based on:
34518  * Ext JS Library 1.1.1
34519  * Copyright(c) 2006-2007, Ext JS, LLC.
34520  *
34521  * Originally Released Under LGPL - original licence link has changed is not relivant.
34522  *
34523  * Fork - LGPL
34524  * <script type="text/javascript">
34525  */
34526 /**
34527  * @class Roo.menu.Item
34528  * @extends Roo.menu.BaseItem
34529  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34530  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34531  * activation and click handling.
34532  * @constructor
34533  * Creates a new Item
34534  * @param {Object} config Configuration options
34535  */
34536 Roo.menu.Item = function(config){
34537     Roo.menu.Item.superclass.constructor.call(this, config);
34538     if(this.menu){
34539         this.menu = Roo.menu.MenuMgr.get(this.menu);
34540     }
34541 };
34542 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34543     
34544     /**
34545      * @cfg {String} text
34546      * The text to show on the menu item.
34547      */
34548     text: '',
34549     /**
34550      * @cfg {String} icon
34551      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34552      */
34553     icon: undefined,
34554     /**
34555      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34556      */
34557     itemCls : "x-menu-item",
34558     /**
34559      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34560      */
34561     canActivate : true,
34562     /**
34563      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34564      */
34565     showDelay: 200,
34566     // doc'd in BaseItem
34567     hideDelay: 200,
34568
34569     // private
34570     ctype: "Roo.menu.Item",
34571     
34572     // private
34573     onRender : function(container, position){
34574         var el = document.createElement("a");
34575         el.hideFocus = true;
34576         el.unselectable = "on";
34577         el.href = this.href || "#";
34578         if(this.hrefTarget){
34579             el.target = this.hrefTarget;
34580         }
34581         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34582         el.innerHTML = String.format(
34583                 '<img src="{0}" class="x-menu-item-icon {2}" />{1}',
34584                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || '');
34585         this.el = el;
34586         Roo.menu.Item.superclass.onRender.call(this, container, position);
34587     },
34588
34589     /**
34590      * Sets the text to display in this menu item
34591      * @param {String} text The text to display
34592      */
34593     setText : function(text){
34594         this.text = text;
34595         if(this.rendered){
34596             this.el.update(String.format(
34597                 '<img src="{0}" class="x-menu-item-icon {2}">{1}',
34598                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34599             this.parentMenu.autoWidth();
34600         }
34601     },
34602
34603     // private
34604     handleClick : function(e){
34605         if(!this.href){ // if no link defined, stop the event automatically
34606             e.stopEvent();
34607         }
34608         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34609     },
34610
34611     // private
34612     activate : function(autoExpand){
34613         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34614             this.focus();
34615             if(autoExpand){
34616                 this.expandMenu();
34617             }
34618         }
34619         return true;
34620     },
34621
34622     // private
34623     shouldDeactivate : function(e){
34624         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34625             if(this.menu && this.menu.isVisible()){
34626                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34627             }
34628             return true;
34629         }
34630         return false;
34631     },
34632
34633     // private
34634     deactivate : function(){
34635         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34636         this.hideMenu();
34637     },
34638
34639     // private
34640     expandMenu : function(autoActivate){
34641         if(!this.disabled && this.menu){
34642             clearTimeout(this.hideTimer);
34643             delete this.hideTimer;
34644             if(!this.menu.isVisible() && !this.showTimer){
34645                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34646             }else if (this.menu.isVisible() && autoActivate){
34647                 this.menu.tryActivate(0, 1);
34648             }
34649         }
34650     },
34651
34652     // private
34653     deferExpand : function(autoActivate){
34654         delete this.showTimer;
34655         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34656         if(autoActivate){
34657             this.menu.tryActivate(0, 1);
34658         }
34659     },
34660
34661     // private
34662     hideMenu : function(){
34663         clearTimeout(this.showTimer);
34664         delete this.showTimer;
34665         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34666             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34667         }
34668     },
34669
34670     // private
34671     deferHide : function(){
34672         delete this.hideTimer;
34673         this.menu.hide();
34674     }
34675 });/*
34676  * Based on:
34677  * Ext JS Library 1.1.1
34678  * Copyright(c) 2006-2007, Ext JS, LLC.
34679  *
34680  * Originally Released Under LGPL - original licence link has changed is not relivant.
34681  *
34682  * Fork - LGPL
34683  * <script type="text/javascript">
34684  */
34685  
34686 /**
34687  * @class Roo.menu.CheckItem
34688  * @extends Roo.menu.Item
34689  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34690  * @constructor
34691  * Creates a new CheckItem
34692  * @param {Object} config Configuration options
34693  */
34694 Roo.menu.CheckItem = function(config){
34695     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34696     this.addEvents({
34697         /**
34698          * @event beforecheckchange
34699          * Fires before the checked value is set, providing an opportunity to cancel if needed
34700          * @param {Roo.menu.CheckItem} this
34701          * @param {Boolean} checked The new checked value that will be set
34702          */
34703         "beforecheckchange" : true,
34704         /**
34705          * @event checkchange
34706          * Fires after the checked value has been set
34707          * @param {Roo.menu.CheckItem} this
34708          * @param {Boolean} checked The checked value that was set
34709          */
34710         "checkchange" : true
34711     });
34712     if(this.checkHandler){
34713         this.on('checkchange', this.checkHandler, this.scope);
34714     }
34715 };
34716 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34717     /**
34718      * @cfg {String} group
34719      * All check items with the same group name will automatically be grouped into a single-select
34720      * radio button group (defaults to '')
34721      */
34722     /**
34723      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34724      */
34725     itemCls : "x-menu-item x-menu-check-item",
34726     /**
34727      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34728      */
34729     groupClass : "x-menu-group-item",
34730
34731     /**
34732      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34733      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34734      * initialized with checked = true will be rendered as checked.
34735      */
34736     checked: false,
34737
34738     // private
34739     ctype: "Roo.menu.CheckItem",
34740
34741     // private
34742     onRender : function(c){
34743         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34744         if(this.group){
34745             this.el.addClass(this.groupClass);
34746         }
34747         Roo.menu.MenuMgr.registerCheckable(this);
34748         if(this.checked){
34749             this.checked = false;
34750             this.setChecked(true, true);
34751         }
34752     },
34753
34754     // private
34755     destroy : function(){
34756         if(this.rendered){
34757             Roo.menu.MenuMgr.unregisterCheckable(this);
34758         }
34759         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34760     },
34761
34762     /**
34763      * Set the checked state of this item
34764      * @param {Boolean} checked The new checked value
34765      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
34766      */
34767     setChecked : function(state, suppressEvent){
34768         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
34769             if(this.container){
34770                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
34771             }
34772             this.checked = state;
34773             if(suppressEvent !== true){
34774                 this.fireEvent("checkchange", this, state);
34775             }
34776         }
34777     },
34778
34779     // private
34780     handleClick : function(e){
34781        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
34782            this.setChecked(!this.checked);
34783        }
34784        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
34785     }
34786 });/*
34787  * Based on:
34788  * Ext JS Library 1.1.1
34789  * Copyright(c) 2006-2007, Ext JS, LLC.
34790  *
34791  * Originally Released Under LGPL - original licence link has changed is not relivant.
34792  *
34793  * Fork - LGPL
34794  * <script type="text/javascript">
34795  */
34796  
34797 /**
34798  * @class Roo.menu.DateItem
34799  * @extends Roo.menu.Adapter
34800  * A menu item that wraps the {@link Roo.DatPicker} component.
34801  * @constructor
34802  * Creates a new DateItem
34803  * @param {Object} config Configuration options
34804  */
34805 Roo.menu.DateItem = function(config){
34806     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
34807     /** The Roo.DatePicker object @type Roo.DatePicker */
34808     this.picker = this.component;
34809     this.addEvents({select: true});
34810     
34811     this.picker.on("render", function(picker){
34812         picker.getEl().swallowEvent("click");
34813         picker.container.addClass("x-menu-date-item");
34814     });
34815
34816     this.picker.on("select", this.onSelect, this);
34817 };
34818
34819 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
34820     // private
34821     onSelect : function(picker, date){
34822         this.fireEvent("select", this, date, picker);
34823         Roo.menu.DateItem.superclass.handleClick.call(this);
34824     }
34825 });/*
34826  * Based on:
34827  * Ext JS Library 1.1.1
34828  * Copyright(c) 2006-2007, Ext JS, LLC.
34829  *
34830  * Originally Released Under LGPL - original licence link has changed is not relivant.
34831  *
34832  * Fork - LGPL
34833  * <script type="text/javascript">
34834  */
34835  
34836 /**
34837  * @class Roo.menu.ColorItem
34838  * @extends Roo.menu.Adapter
34839  * A menu item that wraps the {@link Roo.ColorPalette} component.
34840  * @constructor
34841  * Creates a new ColorItem
34842  * @param {Object} config Configuration options
34843  */
34844 Roo.menu.ColorItem = function(config){
34845     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
34846     /** The Roo.ColorPalette object @type Roo.ColorPalette */
34847     this.palette = this.component;
34848     this.relayEvents(this.palette, ["select"]);
34849     if(this.selectHandler){
34850         this.on('select', this.selectHandler, this.scope);
34851     }
34852 };
34853 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
34854  * Based on:
34855  * Ext JS Library 1.1.1
34856  * Copyright(c) 2006-2007, Ext JS, LLC.
34857  *
34858  * Originally Released Under LGPL - original licence link has changed is not relivant.
34859  *
34860  * Fork - LGPL
34861  * <script type="text/javascript">
34862  */
34863  
34864
34865 /**
34866  * @class Roo.menu.DateMenu
34867  * @extends Roo.menu.Menu
34868  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
34869  * @constructor
34870  * Creates a new DateMenu
34871  * @param {Object} config Configuration options
34872  */
34873 Roo.menu.DateMenu = function(config){
34874     Roo.menu.DateMenu.superclass.constructor.call(this, config);
34875     this.plain = true;
34876     var di = new Roo.menu.DateItem(config);
34877     this.add(di);
34878     /**
34879      * The {@link Roo.DatePicker} instance for this DateMenu
34880      * @type DatePicker
34881      */
34882     this.picker = di.picker;
34883     /**
34884      * @event select
34885      * @param {DatePicker} picker
34886      * @param {Date} date
34887      */
34888     this.relayEvents(di, ["select"]);
34889
34890     this.on('beforeshow', function(){
34891         if(this.picker){
34892             this.picker.hideMonthPicker(true);
34893         }
34894     }, this);
34895 };
34896 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
34897     cls:'x-date-menu'
34898 });/*
34899  * Based on:
34900  * Ext JS Library 1.1.1
34901  * Copyright(c) 2006-2007, Ext JS, LLC.
34902  *
34903  * Originally Released Under LGPL - original licence link has changed is not relivant.
34904  *
34905  * Fork - LGPL
34906  * <script type="text/javascript">
34907  */
34908  
34909
34910 /**
34911  * @class Roo.menu.ColorMenu
34912  * @extends Roo.menu.Menu
34913  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
34914  * @constructor
34915  * Creates a new ColorMenu
34916  * @param {Object} config Configuration options
34917  */
34918 Roo.menu.ColorMenu = function(config){
34919     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
34920     this.plain = true;
34921     var ci = new Roo.menu.ColorItem(config);
34922     this.add(ci);
34923     /**
34924      * The {@link Roo.ColorPalette} instance for this ColorMenu
34925      * @type ColorPalette
34926      */
34927     this.palette = ci.palette;
34928     /**
34929      * @event select
34930      * @param {ColorPalette} palette
34931      * @param {String} color
34932      */
34933     this.relayEvents(ci, ["select"]);
34934 };
34935 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
34936  * Based on:
34937  * Ext JS Library 1.1.1
34938  * Copyright(c) 2006-2007, Ext JS, LLC.
34939  *
34940  * Originally Released Under LGPL - original licence link has changed is not relivant.
34941  *
34942  * Fork - LGPL
34943  * <script type="text/javascript">
34944  */
34945  
34946 /**
34947  * @class Roo.form.Field
34948  * @extends Roo.BoxComponent
34949  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
34950  * @constructor
34951  * Creates a new Field
34952  * @param {Object} config Configuration options
34953  */
34954 Roo.form.Field = function(config){
34955     Roo.form.Field.superclass.constructor.call(this, config);
34956 };
34957
34958 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
34959     /**
34960      * @cfg {String} fieldLabel Label to use when rendering a form.
34961      */
34962        /**
34963      * @cfg {String} qtip Mouse over tip
34964      */
34965      
34966     /**
34967      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
34968      */
34969     invalidClass : "x-form-invalid",
34970     /**
34971      * @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")
34972      */
34973     invalidText : "The value in this field is invalid",
34974     /**
34975      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
34976      */
34977     focusClass : "x-form-focus",
34978     /**
34979      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
34980       automatic validation (defaults to "keyup").
34981      */
34982     validationEvent : "keyup",
34983     /**
34984      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
34985      */
34986     validateOnBlur : true,
34987     /**
34988      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
34989      */
34990     validationDelay : 250,
34991     /**
34992      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
34993      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
34994      */
34995     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
34996     /**
34997      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
34998      */
34999     fieldClass : "x-form-field",
35000     /**
35001      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35002      *<pre>
35003 Value         Description
35004 -----------   ----------------------------------------------------------------------
35005 qtip          Display a quick tip when the user hovers over the field
35006 title         Display a default browser title attribute popup
35007 under         Add a block div beneath the field containing the error text
35008 side          Add an error icon to the right of the field with a popup on hover
35009 [element id]  Add the error text directly to the innerHTML of the specified element
35010 </pre>
35011      */
35012     msgTarget : 'qtip',
35013     /**
35014      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35015      */
35016     msgFx : 'normal',
35017
35018     /**
35019      * @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.
35020      */
35021     readOnly : false,
35022
35023     /**
35024      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35025      */
35026     disabled : false,
35027
35028     /**
35029      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35030      */
35031     inputType : undefined,
35032     
35033     /**
35034      * @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).
35035          */
35036         tabIndex : undefined,
35037         
35038     // private
35039     isFormField : true,
35040
35041     // private
35042     hasFocus : false,
35043     /**
35044      * @property {Roo.Element} fieldEl
35045      * Element Containing the rendered Field (with label etc.)
35046      */
35047     /**
35048      * @cfg {Mixed} value A value to initialize this field with.
35049      */
35050     value : undefined,
35051
35052     /**
35053      * @cfg {String} name The field's HTML name attribute.
35054      */
35055     /**
35056      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35057      */
35058
35059         // private ??
35060         initComponent : function(){
35061         Roo.form.Field.superclass.initComponent.call(this);
35062         this.addEvents({
35063             /**
35064              * @event focus
35065              * Fires when this field receives input focus.
35066              * @param {Roo.form.Field} this
35067              */
35068             focus : true,
35069             /**
35070              * @event blur
35071              * Fires when this field loses input focus.
35072              * @param {Roo.form.Field} this
35073              */
35074             blur : true,
35075             /**
35076              * @event specialkey
35077              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35078              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35079              * @param {Roo.form.Field} this
35080              * @param {Roo.EventObject} e The event object
35081              */
35082             specialkey : true,
35083             /**
35084              * @event change
35085              * Fires just before the field blurs if the field value has changed.
35086              * @param {Roo.form.Field} this
35087              * @param {Mixed} newValue The new value
35088              * @param {Mixed} oldValue The original value
35089              */
35090             change : true,
35091             /**
35092              * @event invalid
35093              * Fires after the field has been marked as invalid.
35094              * @param {Roo.form.Field} this
35095              * @param {String} msg The validation message
35096              */
35097             invalid : true,
35098             /**
35099              * @event valid
35100              * Fires after the field has been validated with no errors.
35101              * @param {Roo.form.Field} this
35102              */
35103             valid : true
35104         });
35105     },
35106
35107     /**
35108      * Returns the name attribute of the field if available
35109      * @return {String} name The field name
35110      */
35111     getName: function(){
35112          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35113     },
35114
35115     // private
35116     onRender : function(ct, position){
35117         Roo.form.Field.superclass.onRender.call(this, ct, position);
35118         if(!this.el){
35119             var cfg = this.getAutoCreate();
35120             if(!cfg.name){
35121                 cfg.name = this.name || this.id;
35122             }
35123             if(this.inputType){
35124                 cfg.type = this.inputType;
35125             }
35126             this.el = ct.createChild(cfg, position);
35127         }
35128         var type = this.el.dom.type;
35129         if(type){
35130             if(type == 'password'){
35131                 type = 'text';
35132             }
35133             this.el.addClass('x-form-'+type);
35134         }
35135         if(this.readOnly){
35136             this.el.dom.readOnly = true;
35137         }
35138         if(this.tabIndex !== undefined){
35139             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35140         }
35141
35142         this.el.addClass([this.fieldClass, this.cls]);
35143         this.initValue();
35144     },
35145
35146     /**
35147      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35148      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35149      * @return {Roo.form.Field} this
35150      */
35151     applyTo : function(target){
35152         this.allowDomMove = false;
35153         this.el = Roo.get(target);
35154         this.render(this.el.dom.parentNode);
35155         return this;
35156     },
35157
35158     // private
35159     initValue : function(){
35160         if(this.value !== undefined){
35161             this.setValue(this.value);
35162         }else if(this.el.dom.value.length > 0){
35163             this.setValue(this.el.dom.value);
35164         }
35165     },
35166
35167     /**
35168      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35169      */
35170     isDirty : function() {
35171         if(this.disabled) {
35172             return false;
35173         }
35174         return String(this.getValue()) !== String(this.originalValue);
35175     },
35176
35177     // private
35178     afterRender : function(){
35179         Roo.form.Field.superclass.afterRender.call(this);
35180         this.initEvents();
35181     },
35182
35183     // private
35184     fireKey : function(e){
35185         if(e.isNavKeyPress()){
35186             this.fireEvent("specialkey", this, e);
35187         }
35188     },
35189
35190     /**
35191      * Resets the current field value to the originally loaded value and clears any validation messages
35192      */
35193     reset : function(){
35194         this.setValue(this.originalValue);
35195         this.clearInvalid();
35196     },
35197
35198     // private
35199     initEvents : function(){
35200         this.el.on(Roo.isIE ? "keydown" : "keypress", this.fireKey,  this);
35201         this.el.on("focus", this.onFocus,  this);
35202         this.el.on("blur", this.onBlur,  this);
35203
35204         // reference to original value for reset
35205         this.originalValue = this.getValue();
35206     },
35207
35208     // private
35209     onFocus : function(){
35210         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35211             this.el.addClass(this.focusClass);
35212         }
35213         if(!this.hasFocus){
35214             this.hasFocus = true;
35215             this.startValue = this.getValue();
35216             this.fireEvent("focus", this);
35217         }
35218     },
35219
35220     beforeBlur : Roo.emptyFn,
35221
35222     // private
35223     onBlur : function(){
35224         this.beforeBlur();
35225         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35226             this.el.removeClass(this.focusClass);
35227         }
35228         this.hasFocus = false;
35229         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35230             this.validate();
35231         }
35232         var v = this.getValue();
35233         if(String(v) !== String(this.startValue)){
35234             this.fireEvent('change', this, v, this.startValue);
35235         }
35236         this.fireEvent("blur", this);
35237     },
35238
35239     /**
35240      * Returns whether or not the field value is currently valid
35241      * @param {Boolean} preventMark True to disable marking the field invalid
35242      * @return {Boolean} True if the value is valid, else false
35243      */
35244     isValid : function(preventMark){
35245         if(this.disabled){
35246             return true;
35247         }
35248         var restore = this.preventMark;
35249         this.preventMark = preventMark === true;
35250         var v = this.validateValue(this.processValue(this.getRawValue()));
35251         this.preventMark = restore;
35252         return v;
35253     },
35254
35255     /**
35256      * Validates the field value
35257      * @return {Boolean} True if the value is valid, else false
35258      */
35259     validate : function(){
35260         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35261             this.clearInvalid();
35262             return true;
35263         }
35264         return false;
35265     },
35266
35267     processValue : function(value){
35268         return value;
35269     },
35270
35271     // private
35272     // Subclasses should provide the validation implementation by overriding this
35273     validateValue : function(value){
35274         return true;
35275     },
35276
35277     /**
35278      * Mark this field as invalid
35279      * @param {String} msg The validation message
35280      */
35281     markInvalid : function(msg){
35282         if(!this.rendered || this.preventMark){ // not rendered
35283             return;
35284         }
35285         this.el.addClass(this.invalidClass);
35286         msg = msg || this.invalidText;
35287         switch(this.msgTarget){
35288             case 'qtip':
35289                 this.el.dom.qtip = msg;
35290                 this.el.dom.qclass = 'x-form-invalid-tip';
35291                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35292                     Roo.QuickTips.enable();
35293                 }
35294                 break;
35295             case 'title':
35296                 this.el.dom.title = msg;
35297                 break;
35298             case 'under':
35299                 if(!this.errorEl){
35300                     var elp = this.el.findParent('.x-form-element', 5, true);
35301                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35302                     this.errorEl.setWidth(elp.getWidth(true)-20);
35303                 }
35304                 this.errorEl.update(msg);
35305                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35306                 break;
35307             case 'side':
35308                 if(!this.errorIcon){
35309                     var elp = this.el.findParent('.x-form-element', 5, true);
35310                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35311                 }
35312                 this.alignErrorIcon();
35313                 this.errorIcon.dom.qtip = msg;
35314                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35315                 this.errorIcon.show();
35316                 this.on('resize', this.alignErrorIcon, this);
35317                 break;
35318             default:
35319                 var t = Roo.getDom(this.msgTarget);
35320                 t.innerHTML = msg;
35321                 t.style.display = this.msgDisplay;
35322                 break;
35323         }
35324         this.fireEvent('invalid', this, msg);
35325     },
35326
35327     // private
35328     alignErrorIcon : function(){
35329         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35330     },
35331
35332     /**
35333      * Clear any invalid styles/messages for this field
35334      */
35335     clearInvalid : function(){
35336         if(!this.rendered || this.preventMark){ // not rendered
35337             return;
35338         }
35339         this.el.removeClass(this.invalidClass);
35340         switch(this.msgTarget){
35341             case 'qtip':
35342                 this.el.dom.qtip = '';
35343                 break;
35344             case 'title':
35345                 this.el.dom.title = '';
35346                 break;
35347             case 'under':
35348                 if(this.errorEl){
35349                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35350                 }
35351                 break;
35352             case 'side':
35353                 if(this.errorIcon){
35354                     this.errorIcon.dom.qtip = '';
35355                     this.errorIcon.hide();
35356                     this.un('resize', this.alignErrorIcon, this);
35357                 }
35358                 break;
35359             default:
35360                 var t = Roo.getDom(this.msgTarget);
35361                 t.innerHTML = '';
35362                 t.style.display = 'none';
35363                 break;
35364         }
35365         this.fireEvent('valid', this);
35366     },
35367
35368     /**
35369      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35370      * @return {Mixed} value The field value
35371      */
35372     getRawValue : function(){
35373         var v = this.el.getValue();
35374         if(v === this.emptyText){
35375             v = '';
35376         }
35377         return v;
35378     },
35379
35380     /**
35381      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35382      * @return {Mixed} value The field value
35383      */
35384     getValue : function(){
35385         var v = this.el.getValue();
35386         if(v === this.emptyText || v === undefined){
35387             v = '';
35388         }
35389         return v;
35390     },
35391
35392     /**
35393      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35394      * @param {Mixed} value The value to set
35395      */
35396     setRawValue : function(v){
35397         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35398     },
35399
35400     /**
35401      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35402      * @param {Mixed} value The value to set
35403      */
35404     setValue : function(v){
35405         this.value = v;
35406         if(this.rendered){
35407             this.el.dom.value = (v === null || v === undefined ? '' : v);
35408             this.validate();
35409         }
35410     },
35411
35412     adjustSize : function(w, h){
35413         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35414         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35415         return s;
35416     },
35417
35418     adjustWidth : function(tag, w){
35419         tag = tag.toLowerCase();
35420         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35421             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35422                 if(tag == 'input'){
35423                     return w + 2;
35424                 }
35425                 if(tag = 'textarea'){
35426                     return w-2;
35427                 }
35428             }else if(Roo.isOpera){
35429                 if(tag == 'input'){
35430                     return w + 2;
35431                 }
35432                 if(tag = 'textarea'){
35433                     return w-2;
35434                 }
35435             }
35436         }
35437         return w;
35438     }
35439 });
35440
35441
35442 // anything other than normal should be considered experimental
35443 Roo.form.Field.msgFx = {
35444     normal : {
35445         show: function(msgEl, f){
35446             msgEl.setDisplayed('block');
35447         },
35448
35449         hide : function(msgEl, f){
35450             msgEl.setDisplayed(false).update('');
35451         }
35452     },
35453
35454     slide : {
35455         show: function(msgEl, f){
35456             msgEl.slideIn('t', {stopFx:true});
35457         },
35458
35459         hide : function(msgEl, f){
35460             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35461         }
35462     },
35463
35464     slideRight : {
35465         show: function(msgEl, f){
35466             msgEl.fixDisplay();
35467             msgEl.alignTo(f.el, 'tl-tr');
35468             msgEl.slideIn('l', {stopFx:true});
35469         },
35470
35471         hide : function(msgEl, f){
35472             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35473         }
35474     }
35475 };/*
35476  * Based on:
35477  * Ext JS Library 1.1.1
35478  * Copyright(c) 2006-2007, Ext JS, LLC.
35479  *
35480  * Originally Released Under LGPL - original licence link has changed is not relivant.
35481  *
35482  * Fork - LGPL
35483  * <script type="text/javascript">
35484  */
35485  
35486
35487 /**
35488  * @class Roo.form.TextField
35489  * @extends Roo.form.Field
35490  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35491  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35492  * @constructor
35493  * Creates a new TextField
35494  * @param {Object} config Configuration options
35495  */
35496 Roo.form.TextField = function(config){
35497     Roo.form.TextField.superclass.constructor.call(this, config);
35498     this.addEvents({
35499         /**
35500          * @event autosize
35501          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35502          * according to the default logic, but this event provides a hook for the developer to apply additional
35503          * logic at runtime to resize the field if needed.
35504              * @param {Roo.form.Field} this This text field
35505              * @param {Number} width The new field width
35506              */
35507         autosize : true
35508     });
35509 };
35510
35511 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35512     /**
35513      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35514      */
35515     grow : false,
35516     /**
35517      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35518      */
35519     growMin : 30,
35520     /**
35521      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35522      */
35523     growMax : 800,
35524     /**
35525      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35526      */
35527     vtype : null,
35528     /**
35529      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35530      */
35531     maskRe : null,
35532     /**
35533      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35534      */
35535     disableKeyFilter : false,
35536     /**
35537      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35538      */
35539     allowBlank : true,
35540     /**
35541      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35542      */
35543     minLength : 0,
35544     /**
35545      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35546      */
35547     maxLength : Number.MAX_VALUE,
35548     /**
35549      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35550      */
35551     minLengthText : "The minimum length for this field is {0}",
35552     /**
35553      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35554      */
35555     maxLengthText : "The maximum length for this field is {0}",
35556     /**
35557      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35558      */
35559     selectOnFocus : false,
35560     /**
35561      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35562      */
35563     blankText : "This field is required",
35564     /**
35565      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35566      * If available, this function will be called only after the basic validators all return true, and will be passed the
35567      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35568      */
35569     validator : null,
35570     /**
35571      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35572      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35573      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35574      */
35575     regex : null,
35576     /**
35577      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35578      */
35579     regexText : "",
35580     /**
35581      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35582      */
35583     emptyText : null,
35584     /**
35585      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35586      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35587      */
35588     emptyClass : 'x-form-empty-field',
35589
35590     // private
35591     initEvents : function(){
35592         Roo.form.TextField.superclass.initEvents.call(this);
35593         if(this.validationEvent == 'keyup'){
35594             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35595             this.el.on('keyup', this.filterValidation, this);
35596         }
35597         else if(this.validationEvent !== false){
35598             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35599         }
35600         if(this.selectOnFocus || this.emptyText){
35601             this.on("focus", this.preFocus, this);
35602             if(this.emptyText){
35603                 this.on('blur', this.postBlur, this);
35604                 this.applyEmptyText();
35605             }
35606         }
35607         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35608             this.el.on("keypress", this.filterKeys, this);
35609         }
35610         if(this.grow){
35611             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35612             this.el.on("click", this.autoSize,  this);
35613         }
35614     },
35615
35616     processValue : function(value){
35617         if(this.stripCharsRe){
35618             var newValue = value.replace(this.stripCharsRe, '');
35619             if(newValue !== value){
35620                 this.setRawValue(newValue);
35621                 return newValue;
35622             }
35623         }
35624         return value;
35625     },
35626
35627     filterValidation : function(e){
35628         if(!e.isNavKeyPress()){
35629             this.validationTask.delay(this.validationDelay);
35630         }
35631     },
35632
35633     // private
35634     onKeyUp : function(e){
35635         if(!e.isNavKeyPress()){
35636             this.autoSize();
35637         }
35638     },
35639
35640     /**
35641      * Resets the current field value to the originally-loaded value and clears any validation messages.
35642      * Also adds emptyText and emptyClass if the original value was blank.
35643      */
35644     reset : function(){
35645         Roo.form.TextField.superclass.reset.call(this);
35646         this.applyEmptyText();
35647     },
35648
35649     applyEmptyText : function(){
35650         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35651             this.setRawValue(this.emptyText);
35652             this.el.addClass(this.emptyClass);
35653         }
35654     },
35655
35656     // private
35657     preFocus : function(){
35658         if(this.emptyText){
35659             if(this.el.dom.value == this.emptyText){
35660                 this.setRawValue('');
35661             }
35662             this.el.removeClass(this.emptyClass);
35663         }
35664         if(this.selectOnFocus){
35665             this.el.dom.select();
35666         }
35667     },
35668
35669     // private
35670     postBlur : function(){
35671         this.applyEmptyText();
35672     },
35673
35674     // private
35675     filterKeys : function(e){
35676         var k = e.getKey();
35677         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35678             return;
35679         }
35680         var c = e.getCharCode(), cc = String.fromCharCode(c);
35681         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35682             return;
35683         }
35684         if(!this.maskRe.test(cc)){
35685             e.stopEvent();
35686         }
35687     },
35688
35689     setValue : function(v){
35690         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35691             this.el.removeClass(this.emptyClass);
35692         }
35693         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35694         this.applyEmptyText();
35695         this.autoSize();
35696     },
35697
35698     /**
35699      * Validates a value according to the field's validation rules and marks the field as invalid
35700      * if the validation fails
35701      * @param {Mixed} value The value to validate
35702      * @return {Boolean} True if the value is valid, else false
35703      */
35704     validateValue : function(value){
35705         if(value.length < 1 || value === this.emptyText){ // if it's blank
35706              if(this.allowBlank){
35707                 this.clearInvalid();
35708                 return true;
35709              }else{
35710                 this.markInvalid(this.blankText);
35711                 return false;
35712              }
35713         }
35714         if(value.length < this.minLength){
35715             this.markInvalid(String.format(this.minLengthText, this.minLength));
35716             return false;
35717         }
35718         if(value.length > this.maxLength){
35719             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35720             return false;
35721         }
35722         if(this.vtype){
35723             var vt = Roo.form.VTypes;
35724             if(!vt[this.vtype](value, this)){
35725                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35726                 return false;
35727             }
35728         }
35729         if(typeof this.validator == "function"){
35730             var msg = this.validator(value);
35731             if(msg !== true){
35732                 this.markInvalid(msg);
35733                 return false;
35734             }
35735         }
35736         if(this.regex && !this.regex.test(value)){
35737             this.markInvalid(this.regexText);
35738             return false;
35739         }
35740         return true;
35741     },
35742
35743     /**
35744      * Selects text in this field
35745      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35746      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35747      */
35748     selectText : function(start, end){
35749         var v = this.getRawValue();
35750         if(v.length > 0){
35751             start = start === undefined ? 0 : start;
35752             end = end === undefined ? v.length : end;
35753             var d = this.el.dom;
35754             if(d.setSelectionRange){
35755                 d.setSelectionRange(start, end);
35756             }else if(d.createTextRange){
35757                 var range = d.createTextRange();
35758                 range.moveStart("character", start);
35759                 range.moveEnd("character", v.length-end);
35760                 range.select();
35761             }
35762         }
35763     },
35764
35765     /**
35766      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
35767      * This only takes effect if grow = true, and fires the autosize event.
35768      */
35769     autoSize : function(){
35770         if(!this.grow || !this.rendered){
35771             return;
35772         }
35773         if(!this.metrics){
35774             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
35775         }
35776         var el = this.el;
35777         var v = el.dom.value;
35778         var d = document.createElement('div');
35779         d.appendChild(document.createTextNode(v));
35780         v = d.innerHTML;
35781         d = null;
35782         v += "&#160;";
35783         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
35784         this.el.setWidth(w);
35785         this.fireEvent("autosize", this, w);
35786     }
35787 });/*
35788  * Based on:
35789  * Ext JS Library 1.1.1
35790  * Copyright(c) 2006-2007, Ext JS, LLC.
35791  *
35792  * Originally Released Under LGPL - original licence link has changed is not relivant.
35793  *
35794  * Fork - LGPL
35795  * <script type="text/javascript">
35796  */
35797  
35798 /**
35799  * @class Roo.form.Hidden
35800  * @extends Roo.form.TextField
35801  * Simple Hidden element used on forms 
35802  * 
35803  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
35804  * 
35805  * @constructor
35806  * Creates a new Hidden form element.
35807  * @param {Object} config Configuration options
35808  */
35809
35810
35811
35812 // easy hidden field...
35813 Roo.form.Hidden = function(config){
35814     Roo.form.Hidden.superclass.constructor.call(this, config);
35815 };
35816   
35817 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
35818     fieldLabel:      '',
35819     inputType:      'hidden',
35820     width:          50,
35821     allowBlank:     true,
35822     labelSeparator: '',
35823     hidden:         true,
35824     itemCls :       'x-form-item-display-none'
35825
35826
35827 });
35828
35829
35830 /*
35831  * Based on:
35832  * Ext JS Library 1.1.1
35833  * Copyright(c) 2006-2007, Ext JS, LLC.
35834  *
35835  * Originally Released Under LGPL - original licence link has changed is not relivant.
35836  *
35837  * Fork - LGPL
35838  * <script type="text/javascript">
35839  */
35840  
35841 /**
35842  * @class Roo.form.TriggerField
35843  * @extends Roo.form.TextField
35844  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
35845  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
35846  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
35847  * for which you can provide a custom implementation.  For example:
35848  * <pre><code>
35849 var trigger = new Roo.form.TriggerField();
35850 trigger.onTriggerClick = myTriggerFn;
35851 trigger.applyTo('my-field');
35852 </code></pre>
35853  *
35854  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
35855  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
35856  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
35857  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
35858  * @constructor
35859  * Create a new TriggerField.
35860  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
35861  * to the base TextField)
35862  */
35863 Roo.form.TriggerField = function(config){
35864     this.mimicing = false;
35865     Roo.form.TriggerField.superclass.constructor.call(this, config);
35866 };
35867
35868 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
35869     /**
35870      * @cfg {String} triggerClass A CSS class to apply to the trigger
35871      */
35872     /**
35873      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35874      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
35875      */
35876     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
35877     /**
35878      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
35879      */
35880     hideTrigger:false,
35881
35882     /** @cfg {Boolean} grow @hide */
35883     /** @cfg {Number} growMin @hide */
35884     /** @cfg {Number} growMax @hide */
35885
35886     /**
35887      * @hide 
35888      * @method
35889      */
35890     autoSize: Roo.emptyFn,
35891     // private
35892     monitorTab : true,
35893     // private
35894     deferHeight : true,
35895
35896     
35897     actionMode : 'wrap',
35898     // private
35899     onResize : function(w, h){
35900         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
35901         if(typeof w == 'number'){
35902             this.el.setWidth(this.adjustWidth('input', w - this.trigger.getWidth()));
35903         }
35904     },
35905
35906     // private
35907     adjustSize : Roo.BoxComponent.prototype.adjustSize,
35908
35909     // private
35910     getResizeEl : function(){
35911         return this.wrap;
35912     },
35913
35914     // private
35915     getPositionEl : function(){
35916         return this.wrap;
35917     },
35918
35919     // private
35920     alignErrorIcon : function(){
35921         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
35922     },
35923
35924     // private
35925     onRender : function(ct, position){
35926         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
35927         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
35928         this.trigger = this.wrap.createChild(this.triggerConfig ||
35929                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
35930         if(this.hideTrigger){
35931             this.trigger.setDisplayed(false);
35932         }
35933         this.initTrigger();
35934         if(!this.width){
35935             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
35936         }
35937     },
35938
35939     // private
35940     initTrigger : function(){
35941         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
35942         this.trigger.addClassOnOver('x-form-trigger-over');
35943         this.trigger.addClassOnClick('x-form-trigger-click');
35944     },
35945
35946     // private
35947     onDestroy : function(){
35948         if(this.trigger){
35949             this.trigger.removeAllListeners();
35950             this.trigger.remove();
35951         }
35952         if(this.wrap){
35953             this.wrap.remove();
35954         }
35955         Roo.form.TriggerField.superclass.onDestroy.call(this);
35956     },
35957
35958     // private
35959     onFocus : function(){
35960         Roo.form.TriggerField.superclass.onFocus.call(this);
35961         if(!this.mimicing){
35962             this.wrap.addClass('x-trigger-wrap-focus');
35963             this.mimicing = true;
35964             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
35965             if(this.monitorTab){
35966                 this.el.on("keydown", this.checkTab, this);
35967             }
35968         }
35969     },
35970
35971     // private
35972     checkTab : function(e){
35973         if(e.getKey() == e.TAB){
35974             this.triggerBlur();
35975         }
35976     },
35977
35978     // private
35979     onBlur : function(){
35980         // do nothing
35981     },
35982
35983     // private
35984     mimicBlur : function(e, t){
35985         if(!this.wrap.contains(t) && this.validateBlur()){
35986             this.triggerBlur();
35987         }
35988     },
35989
35990     // private
35991     triggerBlur : function(){
35992         this.mimicing = false;
35993         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
35994         if(this.monitorTab){
35995             this.el.un("keydown", this.checkTab, this);
35996         }
35997         this.wrap.removeClass('x-trigger-wrap-focus');
35998         Roo.form.TriggerField.superclass.onBlur.call(this);
35999     },
36000
36001     // private
36002     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36003     validateBlur : function(e, t){
36004         return true;
36005     },
36006
36007     // private
36008     onDisable : function(){
36009         Roo.form.TriggerField.superclass.onDisable.call(this);
36010         if(this.wrap){
36011             this.wrap.addClass('x-item-disabled');
36012         }
36013     },
36014
36015     // private
36016     onEnable : function(){
36017         Roo.form.TriggerField.superclass.onEnable.call(this);
36018         if(this.wrap){
36019             this.wrap.removeClass('x-item-disabled');
36020         }
36021     },
36022
36023     // private
36024     onShow : function(){
36025         var ae = this.getActionEl();
36026         
36027         if(ae){
36028             ae.dom.style.display = '';
36029             ae.dom.style.visibility = 'visible';
36030         }
36031     },
36032
36033     // private
36034     
36035     onHide : function(){
36036         var ae = this.getActionEl();
36037         ae.dom.style.display = 'none';
36038     },
36039
36040     /**
36041      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36042      * by an implementing function.
36043      * @method
36044      * @param {EventObject} e
36045      */
36046     onTriggerClick : Roo.emptyFn
36047 });
36048
36049 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36050 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36051 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36052 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36053     initComponent : function(){
36054         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36055
36056         this.triggerConfig = {
36057             tag:'span', cls:'x-form-twin-triggers', cn:[
36058             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36059             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36060         ]};
36061     },
36062
36063     getTrigger : function(index){
36064         return this.triggers[index];
36065     },
36066
36067     initTrigger : function(){
36068         var ts = this.trigger.select('.x-form-trigger', true);
36069         this.wrap.setStyle('overflow', 'hidden');
36070         var triggerField = this;
36071         ts.each(function(t, all, index){
36072             t.hide = function(){
36073                 var w = triggerField.wrap.getWidth();
36074                 this.dom.style.display = 'none';
36075                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36076             };
36077             t.show = function(){
36078                 var w = triggerField.wrap.getWidth();
36079                 this.dom.style.display = '';
36080                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36081             };
36082             var triggerIndex = 'Trigger'+(index+1);
36083
36084             if(this['hide'+triggerIndex]){
36085                 t.dom.style.display = 'none';
36086             }
36087             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36088             t.addClassOnOver('x-form-trigger-over');
36089             t.addClassOnClick('x-form-trigger-click');
36090         }, this);
36091         this.triggers = ts.elements;
36092     },
36093
36094     onTrigger1Click : Roo.emptyFn,
36095     onTrigger2Click : Roo.emptyFn
36096 });/*
36097  * Based on:
36098  * Ext JS Library 1.1.1
36099  * Copyright(c) 2006-2007, Ext JS, LLC.
36100  *
36101  * Originally Released Under LGPL - original licence link has changed is not relivant.
36102  *
36103  * Fork - LGPL
36104  * <script type="text/javascript">
36105  */
36106  
36107 /**
36108  * @class Roo.form.TextArea
36109  * @extends Roo.form.TextField
36110  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36111  * support for auto-sizing.
36112  * @constructor
36113  * Creates a new TextArea
36114  * @param {Object} config Configuration options
36115  */
36116 Roo.form.TextArea = function(config){
36117     Roo.form.TextArea.superclass.constructor.call(this, config);
36118     // these are provided exchanges for backwards compat
36119     // minHeight/maxHeight were replaced by growMin/growMax to be
36120     // compatible with TextField growing config values
36121     if(this.minHeight !== undefined){
36122         this.growMin = this.minHeight;
36123     }
36124     if(this.maxHeight !== undefined){
36125         this.growMax = this.maxHeight;
36126     }
36127 };
36128
36129 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36130     /**
36131      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36132      */
36133     growMin : 60,
36134     /**
36135      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36136      */
36137     growMax: 1000,
36138     /**
36139      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36140      * in the field (equivalent to setting overflow: hidden, defaults to false)
36141      */
36142     preventScrollbars: false,
36143     /**
36144      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36145      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36146      */
36147
36148     // private
36149     onRender : function(ct, position){
36150         if(!this.el){
36151             this.defaultAutoCreate = {
36152                 tag: "textarea",
36153                 style:"width:300px;height:60px;",
36154                 autocomplete: "off"
36155             };
36156         }
36157         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36158         if(this.grow){
36159             this.textSizeEl = Roo.DomHelper.append(document.body, {
36160                 tag: "pre", cls: "x-form-grow-sizer"
36161             });
36162             if(this.preventScrollbars){
36163                 this.el.setStyle("overflow", "hidden");
36164             }
36165             this.el.setHeight(this.growMin);
36166         }
36167     },
36168
36169     onDestroy : function(){
36170         if(this.textSizeEl){
36171             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36172         }
36173         Roo.form.TextArea.superclass.onDestroy.call(this);
36174     },
36175
36176     // private
36177     onKeyUp : function(e){
36178         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36179             this.autoSize();
36180         }
36181     },
36182
36183     /**
36184      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36185      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36186      */
36187     autoSize : function(){
36188         if(!this.grow || !this.textSizeEl){
36189             return;
36190         }
36191         var el = this.el;
36192         var v = el.dom.value;
36193         var ts = this.textSizeEl;
36194
36195         ts.innerHTML = '';
36196         ts.appendChild(document.createTextNode(v));
36197         v = ts.innerHTML;
36198
36199         Roo.fly(ts).setWidth(this.el.getWidth());
36200         if(v.length < 1){
36201             v = "&#160;&#160;";
36202         }else{
36203             if(Roo.isIE){
36204                 v = v.replace(/\n/g, '<p>&#160;</p>');
36205             }
36206             v += "&#160;\n&#160;";
36207         }
36208         ts.innerHTML = v;
36209         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36210         if(h != this.lastHeight){
36211             this.lastHeight = h;
36212             this.el.setHeight(h);
36213             this.fireEvent("autosize", this, h);
36214         }
36215     }
36216 });/*
36217  * Based on:
36218  * Ext JS Library 1.1.1
36219  * Copyright(c) 2006-2007, Ext JS, LLC.
36220  *
36221  * Originally Released Under LGPL - original licence link has changed is not relivant.
36222  *
36223  * Fork - LGPL
36224  * <script type="text/javascript">
36225  */
36226  
36227
36228 /**
36229  * @class Roo.form.NumberField
36230  * @extends Roo.form.TextField
36231  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36232  * @constructor
36233  * Creates a new NumberField
36234  * @param {Object} config Configuration options
36235  */
36236 Roo.form.NumberField = function(config){
36237     Roo.form.NumberField.superclass.constructor.call(this, config);
36238 };
36239
36240 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36241     /**
36242      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36243      */
36244     fieldClass: "x-form-field x-form-num-field",
36245     /**
36246      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36247      */
36248     allowDecimals : true,
36249     /**
36250      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36251      */
36252     decimalSeparator : ".",
36253     /**
36254      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36255      */
36256     decimalPrecision : 2,
36257     /**
36258      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36259      */
36260     allowNegative : true,
36261     /**
36262      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36263      */
36264     minValue : Number.NEGATIVE_INFINITY,
36265     /**
36266      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36267      */
36268     maxValue : Number.MAX_VALUE,
36269     /**
36270      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36271      */
36272     minText : "The minimum value for this field is {0}",
36273     /**
36274      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36275      */
36276     maxText : "The maximum value for this field is {0}",
36277     /**
36278      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36279      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36280      */
36281     nanText : "{0} is not a valid number",
36282
36283     // private
36284     initEvents : function(){
36285         Roo.form.NumberField.superclass.initEvents.call(this);
36286         var allowed = "0123456789";
36287         if(this.allowDecimals){
36288             allowed += this.decimalSeparator;
36289         }
36290         if(this.allowNegative){
36291             allowed += "-";
36292         }
36293         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36294         var keyPress = function(e){
36295             var k = e.getKey();
36296             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36297                 return;
36298             }
36299             var c = e.getCharCode();
36300             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36301                 e.stopEvent();
36302             }
36303         };
36304         this.el.on("keypress", keyPress, this);
36305     },
36306
36307     // private
36308     validateValue : function(value){
36309         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36310             return false;
36311         }
36312         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36313              return true;
36314         }
36315         var num = this.parseValue(value);
36316         if(isNaN(num)){
36317             this.markInvalid(String.format(this.nanText, value));
36318             return false;
36319         }
36320         if(num < this.minValue){
36321             this.markInvalid(String.format(this.minText, this.minValue));
36322             return false;
36323         }
36324         if(num > this.maxValue){
36325             this.markInvalid(String.format(this.maxText, this.maxValue));
36326             return false;
36327         }
36328         return true;
36329     },
36330
36331     getValue : function(){
36332         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36333     },
36334
36335     // private
36336     parseValue : function(value){
36337         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36338         return isNaN(value) ? '' : value;
36339     },
36340
36341     // private
36342     fixPrecision : function(value){
36343         var nan = isNaN(value);
36344         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36345             return nan ? '' : value;
36346         }
36347         return parseFloat(value).toFixed(this.decimalPrecision);
36348     },
36349
36350     setValue : function(v){
36351         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36352     },
36353
36354     // private
36355     decimalPrecisionFcn : function(v){
36356         return Math.floor(v);
36357     },
36358
36359     beforeBlur : function(){
36360         var v = this.parseValue(this.getRawValue());
36361         if(v){
36362             this.setValue(this.fixPrecision(v));
36363         }
36364     }
36365 });/*
36366  * Based on:
36367  * Ext JS Library 1.1.1
36368  * Copyright(c) 2006-2007, Ext JS, LLC.
36369  *
36370  * Originally Released Under LGPL - original licence link has changed is not relivant.
36371  *
36372  * Fork - LGPL
36373  * <script type="text/javascript">
36374  */
36375  
36376 /**
36377  * @class Roo.form.DateField
36378  * @extends Roo.form.TriggerField
36379  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36380 * @constructor
36381 * Create a new DateField
36382 * @param {Object} config
36383  */
36384 Roo.form.DateField = function(config){
36385     Roo.form.DateField.superclass.constructor.call(this, config);
36386     
36387       this.addEvents({
36388          
36389         /**
36390          * @event select
36391          * Fires when a date is selected
36392              * @param {Roo.form.DateField} combo This combo box
36393              * @param {Date} date The date selected
36394              */
36395         'select' : true
36396          
36397     });
36398     
36399     
36400     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36401     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36402     this.ddMatch = null;
36403     if(this.disabledDates){
36404         var dd = this.disabledDates;
36405         var re = "(?:";
36406         for(var i = 0; i < dd.length; i++){
36407             re += dd[i];
36408             if(i != dd.length-1) re += "|";
36409         }
36410         this.ddMatch = new RegExp(re + ")");
36411     }
36412 };
36413
36414 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36415     /**
36416      * @cfg {String} format
36417      * The default date format string which can be overriden for localization support.  The format must be
36418      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36419      */
36420     format : "m/d/y",
36421     /**
36422      * @cfg {String} altFormats
36423      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36424      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36425      */
36426     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36427     /**
36428      * @cfg {Array} disabledDays
36429      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36430      */
36431     disabledDays : null,
36432     /**
36433      * @cfg {String} disabledDaysText
36434      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36435      */
36436     disabledDaysText : "Disabled",
36437     /**
36438      * @cfg {Array} disabledDates
36439      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36440      * expression so they are very powerful. Some examples:
36441      * <ul>
36442      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36443      * <li>["03/08", "09/16"] would disable those days for every year</li>
36444      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36445      * <li>["03/../2006"] would disable every day in March 2006</li>
36446      * <li>["^03"] would disable every day in every March</li>
36447      * </ul>
36448      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36449      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36450      */
36451     disabledDates : null,
36452     /**
36453      * @cfg {String} disabledDatesText
36454      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36455      */
36456     disabledDatesText : "Disabled",
36457     /**
36458      * @cfg {Date/String} minValue
36459      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36460      * valid format (defaults to null).
36461      */
36462     minValue : null,
36463     /**
36464      * @cfg {Date/String} maxValue
36465      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36466      * valid format (defaults to null).
36467      */
36468     maxValue : null,
36469     /**
36470      * @cfg {String} minText
36471      * The error text to display when the date in the cell is before minValue (defaults to
36472      * 'The date in this field must be after {minValue}').
36473      */
36474     minText : "The date in this field must be equal to or after {0}",
36475     /**
36476      * @cfg {String} maxText
36477      * The error text to display when the date in the cell is after maxValue (defaults to
36478      * 'The date in this field must be before {maxValue}').
36479      */
36480     maxText : "The date in this field must be equal to or before {0}",
36481     /**
36482      * @cfg {String} invalidText
36483      * The error text to display when the date in the field is invalid (defaults to
36484      * '{value} is not a valid date - it must be in the format {format}').
36485      */
36486     invalidText : "{0} is not a valid date - it must be in the format {1}",
36487     /**
36488      * @cfg {String} triggerClass
36489      * An additional CSS class used to style the trigger button.  The trigger will always get the
36490      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36491      * which displays a calendar icon).
36492      */
36493     triggerClass : 'x-form-date-trigger',
36494     
36495
36496     /**
36497      * @cfg {bool} useIso
36498      * if enabled, then the date field will use a hidden field to store the 
36499      * real value as iso formated date. default (false)
36500      */ 
36501     useIso : false,
36502     /**
36503      * @cfg {String/Object} autoCreate
36504      * A DomHelper element spec, or true for a default element spec (defaults to
36505      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36506      */ 
36507     // private
36508     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36509     
36510     // private
36511     hiddenField: false,
36512     
36513     onRender : function(ct, position)
36514     {
36515         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36516         if (this.useIso) {
36517             this.el.dom.removeAttribute('name'); 
36518             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36519                     'before', true);
36520             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36521             // prevent input submission
36522             this.hiddenName = this.name;
36523         }
36524             
36525             
36526     },
36527     
36528     // private
36529     validateValue : function(value)
36530     {
36531         value = this.formatDate(value);
36532         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36533             return false;
36534         }
36535         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36536              return true;
36537         }
36538         var svalue = value;
36539         value = this.parseDate(value);
36540         if(!value){
36541             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36542             return false;
36543         }
36544         var time = value.getTime();
36545         if(this.minValue && time < this.minValue.getTime()){
36546             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36547             return false;
36548         }
36549         if(this.maxValue && time > this.maxValue.getTime()){
36550             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36551             return false;
36552         }
36553         if(this.disabledDays){
36554             var day = value.getDay();
36555             for(var i = 0; i < this.disabledDays.length; i++) {
36556                 if(day === this.disabledDays[i]){
36557                     this.markInvalid(this.disabledDaysText);
36558                     return false;
36559                 }
36560             }
36561         }
36562         var fvalue = this.formatDate(value);
36563         if(this.ddMatch && this.ddMatch.test(fvalue)){
36564             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36565             return false;
36566         }
36567         return true;
36568     },
36569
36570     // private
36571     // Provides logic to override the default TriggerField.validateBlur which just returns true
36572     validateBlur : function(){
36573         return !this.menu || !this.menu.isVisible();
36574     },
36575
36576     /**
36577      * Returns the current date value of the date field.
36578      * @return {Date} The date value
36579      */
36580     getValue : function(){
36581         
36582         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36583     },
36584
36585     /**
36586      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36587      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36588      * (the default format used is "m/d/y").
36589      * <br />Usage:
36590      * <pre><code>
36591 //All of these calls set the same date value (May 4, 2006)
36592
36593 //Pass a date object:
36594 var dt = new Date('5/4/06');
36595 dateField.setValue(dt);
36596
36597 //Pass a date string (default format):
36598 dateField.setValue('5/4/06');
36599
36600 //Pass a date string (custom format):
36601 dateField.format = 'Y-m-d';
36602 dateField.setValue('2006-5-4');
36603 </code></pre>
36604      * @param {String/Date} date The date or valid date string
36605      */
36606     setValue : function(date){
36607         if (this.hiddenField) {
36608             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36609         }
36610         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36611     },
36612
36613     // private
36614     parseDate : function(value){
36615         if(!value || value instanceof Date){
36616             return value;
36617         }
36618         var v = Date.parseDate(value, this.format);
36619         if(!v && this.altFormats){
36620             if(!this.altFormatsArray){
36621                 this.altFormatsArray = this.altFormats.split("|");
36622             }
36623             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36624                 v = Date.parseDate(value, this.altFormatsArray[i]);
36625             }
36626         }
36627         return v;
36628     },
36629
36630     // private
36631     formatDate : function(date, fmt){
36632         return (!date || !(date instanceof Date)) ?
36633                date : date.dateFormat(fmt || this.format);
36634     },
36635
36636     // private
36637     menuListeners : {
36638         select: function(m, d){
36639             this.setValue(d);
36640             this.fireEvent('select', this, d);
36641         },
36642         show : function(){ // retain focus styling
36643             this.onFocus();
36644         },
36645         hide : function(){
36646             this.focus.defer(10, this);
36647             var ml = this.menuListeners;
36648             this.menu.un("select", ml.select,  this);
36649             this.menu.un("show", ml.show,  this);
36650             this.menu.un("hide", ml.hide,  this);
36651         }
36652     },
36653
36654     // private
36655     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36656     onTriggerClick : function(){
36657         if(this.disabled){
36658             return;
36659         }
36660         if(this.menu == null){
36661             this.menu = new Roo.menu.DateMenu();
36662         }
36663         Roo.apply(this.menu.picker,  {
36664             showClear: this.allowBlank,
36665             minDate : this.minValue,
36666             maxDate : this.maxValue,
36667             disabledDatesRE : this.ddMatch,
36668             disabledDatesText : this.disabledDatesText,
36669             disabledDays : this.disabledDays,
36670             disabledDaysText : this.disabledDaysText,
36671             format : this.format,
36672             minText : String.format(this.minText, this.formatDate(this.minValue)),
36673             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36674         });
36675         this.menu.on(Roo.apply({}, this.menuListeners, {
36676             scope:this
36677         }));
36678         this.menu.picker.setValue(this.getValue() || new Date());
36679         this.menu.show(this.el, "tl-bl?");
36680     },
36681
36682     beforeBlur : function(){
36683         var v = this.parseDate(this.getRawValue());
36684         if(v){
36685             this.setValue(v);
36686         }
36687     }
36688
36689     /** @cfg {Boolean} grow @hide */
36690     /** @cfg {Number} growMin @hide */
36691     /** @cfg {Number} growMax @hide */
36692     /**
36693      * @hide
36694      * @method autoSize
36695      */
36696 });/*
36697  * Based on:
36698  * Ext JS Library 1.1.1
36699  * Copyright(c) 2006-2007, Ext JS, LLC.
36700  *
36701  * Originally Released Under LGPL - original licence link has changed is not relivant.
36702  *
36703  * Fork - LGPL
36704  * <script type="text/javascript">
36705  */
36706  
36707
36708 /**
36709  * @class Roo.form.ComboBox
36710  * @extends Roo.form.TriggerField
36711  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36712  * @constructor
36713  * Create a new ComboBox.
36714  * @param {Object} config Configuration options
36715  */
36716 Roo.form.ComboBox = function(config){
36717     Roo.form.ComboBox.superclass.constructor.call(this, config);
36718     this.addEvents({
36719         /**
36720          * @event expand
36721          * Fires when the dropdown list is expanded
36722              * @param {Roo.form.ComboBox} combo This combo box
36723              */
36724         'expand' : true,
36725         /**
36726          * @event collapse
36727          * Fires when the dropdown list is collapsed
36728              * @param {Roo.form.ComboBox} combo This combo box
36729              */
36730         'collapse' : true,
36731         /**
36732          * @event beforeselect
36733          * Fires before a list item is selected. Return false to cancel the selection.
36734              * @param {Roo.form.ComboBox} combo This combo box
36735              * @param {Roo.data.Record} record The data record returned from the underlying store
36736              * @param {Number} index The index of the selected item in the dropdown list
36737              */
36738         'beforeselect' : true,
36739         /**
36740          * @event select
36741          * Fires when a list item is selected
36742              * @param {Roo.form.ComboBox} combo This combo box
36743              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36744              * @param {Number} index The index of the selected item in the dropdown list
36745              */
36746         'select' : true,
36747         /**
36748          * @event beforequery
36749          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36750          * The event object passed has these properties:
36751              * @param {Roo.form.ComboBox} combo This combo box
36752              * @param {String} query The query
36753              * @param {Boolean} forceAll true to force "all" query
36754              * @param {Boolean} cancel true to cancel the query
36755              * @param {Object} e The query event object
36756              */
36757         'beforequery': true
36758     });
36759     if(this.transform){
36760         this.allowDomMove = false;
36761         var s = Roo.getDom(this.transform);
36762         if(!this.hiddenName){
36763             this.hiddenName = s.name;
36764         }
36765         if(!this.store){
36766             this.mode = 'local';
36767             var d = [], opts = s.options;
36768             for(var i = 0, len = opts.length;i < len; i++){
36769                 var o = opts[i];
36770                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
36771                 if(o.selected) {
36772                     this.value = value;
36773                 }
36774                 d.push([value, o.text]);
36775             }
36776             this.store = new Roo.data.SimpleStore({
36777                 'id': 0,
36778                 fields: ['value', 'text'],
36779                 data : d
36780             });
36781             this.valueField = 'value';
36782             this.displayField = 'text';
36783         }
36784         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
36785         if(!this.lazyRender){
36786             this.target = true;
36787             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
36788             s.parentNode.removeChild(s); // remove it
36789             this.render(this.el.parentNode);
36790         }else{
36791             s.parentNode.removeChild(s); // remove it
36792         }
36793
36794     }
36795     if (this.store) {
36796         this.store = Roo.factory(this.store, Roo.data);
36797     }
36798     
36799     this.selectedIndex = -1;
36800     if(this.mode == 'local'){
36801         if(config.queryDelay === undefined){
36802             this.queryDelay = 10;
36803         }
36804         if(config.minChars === undefined){
36805             this.minChars = 0;
36806         }
36807     }
36808 };
36809
36810 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
36811     /**
36812      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
36813      */
36814     /**
36815      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
36816      * rendering into an Roo.Editor, defaults to false)
36817      */
36818     /**
36819      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
36820      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
36821      */
36822     /**
36823      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
36824      */
36825     /**
36826      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
36827      * the dropdown list (defaults to undefined, with no header element)
36828      */
36829
36830      /**
36831      * @cfg {String/Roo.Template} tpl The template to use to render the output
36832      */
36833      
36834     // private
36835     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
36836     /**
36837      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
36838      */
36839     listWidth: undefined,
36840     /**
36841      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
36842      * mode = 'remote' or 'text' if mode = 'local')
36843      */
36844     displayField: undefined,
36845     /**
36846      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
36847      * mode = 'remote' or 'value' if mode = 'local'). 
36848      * Note: use of a valueField requires the user make a selection
36849      * in order for a value to be mapped.
36850      */
36851     valueField: undefined,
36852     /**
36853      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
36854      * field's data value (defaults to the underlying DOM element's name)
36855      */
36856     hiddenName: undefined,
36857     /**
36858      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
36859      */
36860     listClass: '',
36861     /**
36862      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
36863      */
36864     selectedClass: 'x-combo-selected',
36865     /**
36866      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36867      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
36868      * which displays a downward arrow icon).
36869      */
36870     triggerClass : 'x-form-arrow-trigger',
36871     /**
36872      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
36873      */
36874     shadow:'sides',
36875     /**
36876      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
36877      * anchor positions (defaults to 'tl-bl')
36878      */
36879     listAlign: 'tl-bl?',
36880     /**
36881      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
36882      */
36883     maxHeight: 300,
36884     /**
36885      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
36886      * query specified by the allQuery config option (defaults to 'query')
36887      */
36888     triggerAction: 'query',
36889     /**
36890      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
36891      * (defaults to 4, does not apply if editable = false)
36892      */
36893     minChars : 4,
36894     /**
36895      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
36896      * delay (typeAheadDelay) if it matches a known value (defaults to false)
36897      */
36898     typeAhead: false,
36899     /**
36900      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
36901      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
36902      */
36903     queryDelay: 500,
36904     /**
36905      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
36906      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
36907      */
36908     pageSize: 0,
36909     /**
36910      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
36911      * when editable = true (defaults to false)
36912      */
36913     selectOnFocus:false,
36914     /**
36915      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
36916      */
36917     queryParam: 'query',
36918     /**
36919      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
36920      * when mode = 'remote' (defaults to 'Loading...')
36921      */
36922     loadingText: 'Loading...',
36923     /**
36924      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
36925      */
36926     resizable: false,
36927     /**
36928      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
36929      */
36930     handleHeight : 8,
36931     /**
36932      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
36933      * traditional select (defaults to true)
36934      */
36935     editable: true,
36936     /**
36937      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
36938      */
36939     allQuery: '',
36940     /**
36941      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
36942      */
36943     mode: 'remote',
36944     /**
36945      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
36946      * listWidth has a higher value)
36947      */
36948     minListWidth : 70,
36949     /**
36950      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
36951      * allow the user to set arbitrary text into the field (defaults to false)
36952      */
36953     forceSelection:false,
36954     /**
36955      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
36956      * if typeAhead = true (defaults to 250)
36957      */
36958     typeAheadDelay : 250,
36959     /**
36960      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
36961      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
36962      */
36963     valueNotFoundText : undefined,
36964     /**
36965      * @cfg {bool} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
36966      */
36967     blockFocus : false,
36968     
36969     /**
36970      * @cfg {bool} disableClear Disable showing of clear button.
36971      */
36972     disableClear : false,
36973     
36974     // private
36975     onRender : function(ct, position){
36976         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
36977         if(this.hiddenName){
36978             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
36979                     'before', true);
36980             this.hiddenField.value =
36981                 this.hiddenValue !== undefined ? this.hiddenValue :
36982                 this.value !== undefined ? this.value : '';
36983
36984             // prevent input submission
36985             this.el.dom.removeAttribute('name');
36986         }
36987         if(Roo.isGecko){
36988             this.el.dom.setAttribute('autocomplete', 'off');
36989         }
36990
36991         var cls = 'x-combo-list';
36992
36993         this.list = new Roo.Layer({
36994             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
36995         });
36996
36997         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
36998         this.list.setWidth(lw);
36999         this.list.swallowEvent('mousewheel');
37000         this.assetHeight = 0;
37001
37002         if(this.title){
37003             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37004             this.assetHeight += this.header.getHeight();
37005         }
37006
37007         this.innerList = this.list.createChild({cls:cls+'-inner'});
37008         this.innerList.on('mouseover', this.onViewOver, this);
37009         this.innerList.on('mousemove', this.onViewMove, this);
37010         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37011         
37012         if(this.allowBlank && !this.pageSize && !this.disableClear){
37013             this.footer = this.list.createChild({cls:cls+'-ft'});
37014             this.pageTb = new Roo.Toolbar(this.footer);
37015            
37016         }
37017         if(this.pageSize){
37018             this.footer = this.list.createChild({cls:cls+'-ft'});
37019             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37020                     {pageSize: this.pageSize});
37021             
37022         }
37023         
37024         if (this.pageTb && this.allowBlank && !this.disableClear) {
37025             var _this = this;
37026             this.pageTb.add(new Roo.Toolbar.Fill(), {
37027                 cls: 'x-btn-icon x-btn-clear',
37028                 text: '&#160;',
37029                 handler: function()
37030                 {
37031                     _this.collapse();
37032                     _this.clearValue();
37033                     _this.onSelect(false, -1);
37034                 }
37035             });
37036         }
37037         if (this.footer) {
37038             this.assetHeight += this.footer.getHeight();
37039         }
37040         
37041
37042         if(!this.tpl){
37043             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37044         }
37045
37046         this.view = new Roo.View(this.innerList, this.tpl, {
37047             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37048         });
37049
37050         this.view.on('click', this.onViewClick, this);
37051
37052         this.store.on('beforeload', this.onBeforeLoad, this);
37053         this.store.on('load', this.onLoad, this);
37054         this.store.on('loadexception', this.collapse, this);
37055
37056         if(this.resizable){
37057             this.resizer = new Roo.Resizable(this.list,  {
37058                pinned:true, handles:'se'
37059             });
37060             this.resizer.on('resize', function(r, w, h){
37061                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37062                 this.listWidth = w;
37063                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37064                 this.restrictHeight();
37065             }, this);
37066             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37067         }
37068         if(!this.editable){
37069             this.editable = true;
37070             this.setEditable(false);
37071         }
37072     },
37073
37074     // private
37075     initEvents : function(){
37076         Roo.form.ComboBox.superclass.initEvents.call(this);
37077
37078         this.keyNav = new Roo.KeyNav(this.el, {
37079             "up" : function(e){
37080                 this.inKeyMode = true;
37081                 this.selectPrev();
37082             },
37083
37084             "down" : function(e){
37085                 if(!this.isExpanded()){
37086                     this.onTriggerClick();
37087                 }else{
37088                     this.inKeyMode = true;
37089                     this.selectNext();
37090                 }
37091             },
37092
37093             "enter" : function(e){
37094                 this.onViewClick();
37095                 //return true;
37096             },
37097
37098             "esc" : function(e){
37099                 this.collapse();
37100             },
37101
37102             "tab" : function(e){
37103                 this.onViewClick(false);
37104                 return true;
37105             },
37106
37107             scope : this,
37108
37109             doRelay : function(foo, bar, hname){
37110                 if(hname == 'down' || this.scope.isExpanded()){
37111                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37112                 }
37113                 return true;
37114             },
37115
37116             forceKeyDown: true
37117         });
37118         this.queryDelay = Math.max(this.queryDelay || 10,
37119                 this.mode == 'local' ? 10 : 250);
37120         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37121         if(this.typeAhead){
37122             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37123         }
37124         if(this.editable !== false){
37125             this.el.on("keyup", this.onKeyUp, this);
37126         }
37127         if(this.forceSelection){
37128             this.on('blur', this.doForce, this);
37129         }
37130     },
37131
37132     onDestroy : function(){
37133         if(this.view){
37134             this.view.setStore(null);
37135             this.view.el.removeAllListeners();
37136             this.view.el.remove();
37137             this.view.purgeListeners();
37138         }
37139         if(this.list){
37140             this.list.destroy();
37141         }
37142         if(this.store){
37143             this.store.un('beforeload', this.onBeforeLoad, this);
37144             this.store.un('load', this.onLoad, this);
37145             this.store.un('loadexception', this.collapse, this);
37146         }
37147         Roo.form.ComboBox.superclass.onDestroy.call(this);
37148     },
37149
37150     // private
37151     fireKey : function(e){
37152         if(e.isNavKeyPress() && !this.list.isVisible()){
37153             this.fireEvent("specialkey", this, e);
37154         }
37155     },
37156
37157     // private
37158     onResize: function(w, h){
37159         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37160         if(this.list && this.listWidth === undefined){
37161             var lw = Math.max(w, this.minListWidth);
37162             this.list.setWidth(lw);
37163             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37164         }
37165     },
37166
37167     /**
37168      * Allow or prevent the user from directly editing the field text.  If false is passed,
37169      * the user will only be able to select from the items defined in the dropdown list.  This method
37170      * is the runtime equivalent of setting the 'editable' config option at config time.
37171      * @param {Boolean} value True to allow the user to directly edit the field text
37172      */
37173     setEditable : function(value){
37174         if(value == this.editable){
37175             return;
37176         }
37177         this.editable = value;
37178         if(!value){
37179             this.el.dom.setAttribute('readOnly', true);
37180             this.el.on('mousedown', this.onTriggerClick,  this);
37181             this.el.addClass('x-combo-noedit');
37182         }else{
37183             this.el.dom.setAttribute('readOnly', false);
37184             this.el.un('mousedown', this.onTriggerClick,  this);
37185             this.el.removeClass('x-combo-noedit');
37186         }
37187     },
37188
37189     // private
37190     onBeforeLoad : function(){
37191         if(!this.hasFocus){
37192             return;
37193         }
37194         this.innerList.update(this.loadingText ?
37195                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37196         this.restrictHeight();
37197         this.selectedIndex = -1;
37198     },
37199
37200     // private
37201     onLoad : function(){
37202         if(!this.hasFocus){
37203             return;
37204         }
37205         if(this.store.getCount() > 0){
37206             this.expand();
37207             this.restrictHeight();
37208             if(this.lastQuery == this.allQuery){
37209                 if(this.editable){
37210                     this.el.dom.select();
37211                 }
37212                 if(!this.selectByValue(this.value, true)){
37213                     this.select(0, true);
37214                 }
37215             }else{
37216                 this.selectNext();
37217                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37218                     this.taTask.delay(this.typeAheadDelay);
37219                 }
37220             }
37221         }else{
37222             this.onEmptyResults();
37223         }
37224         //this.el.focus();
37225     },
37226
37227     // private
37228     onTypeAhead : function(){
37229         if(this.store.getCount() > 0){
37230             var r = this.store.getAt(0);
37231             var newValue = r.data[this.displayField];
37232             var len = newValue.length;
37233             var selStart = this.getRawValue().length;
37234             if(selStart != len){
37235                 this.setRawValue(newValue);
37236                 this.selectText(selStart, newValue.length);
37237             }
37238         }
37239     },
37240
37241     // private
37242     onSelect : function(record, index){
37243         if(this.fireEvent('beforeselect', this, record, index) !== false){
37244             this.setFromData(index > -1 ? record.data : false);
37245             this.collapse();
37246             this.fireEvent('select', this, record, index);
37247         }
37248     },
37249
37250     /**
37251      * Returns the currently selected field value or empty string if no value is set.
37252      * @return {String} value The selected value
37253      */
37254     getValue : function(){
37255         if(this.valueField){
37256             return typeof this.value != 'undefined' ? this.value : '';
37257         }else{
37258             return Roo.form.ComboBox.superclass.getValue.call(this);
37259         }
37260     },
37261
37262     /**
37263      * Clears any text/value currently set in the field
37264      */
37265     clearValue : function(){
37266         if(this.hiddenField){
37267             this.hiddenField.value = '';
37268         }
37269         this.value = '';
37270         this.setRawValue('');
37271         this.lastSelectionText = '';
37272         this.applyEmptyText();
37273     },
37274
37275     /**
37276      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37277      * will be displayed in the field.  If the value does not match the data value of an existing item,
37278      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37279      * Otherwise the field will be blank (although the value will still be set).
37280      * @param {String} value The value to match
37281      */
37282     setValue : function(v){
37283         var text = v;
37284         if(this.valueField){
37285             var r = this.findRecord(this.valueField, v);
37286             if(r){
37287                 text = r.data[this.displayField];
37288             }else if(this.valueNotFoundText !== undefined){
37289                 text = this.valueNotFoundText;
37290             }
37291         }
37292         this.lastSelectionText = text;
37293         if(this.hiddenField){
37294             this.hiddenField.value = v;
37295         }
37296         Roo.form.ComboBox.superclass.setValue.call(this, text);
37297         this.value = v;
37298     },
37299     /**
37300      * @property {Object} the last set data for the element
37301      */
37302     
37303     lastData : false,
37304     /**
37305      * Sets the value of the field based on a object which is related to the record format for the store.
37306      * @param {Object} value the value to set as. or false on reset?
37307      */
37308     setFromData : function(o){
37309         var dv = ''; // display value
37310         var vv = ''; // value value..
37311         this.lastData = o;
37312         if (this.displayField) {
37313             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37314         } else {
37315             // this is an error condition!!!
37316             console.log('no value field set for '+ this.name);
37317         }
37318         
37319         if(this.valueField){
37320             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37321         }
37322         if(this.hiddenField){
37323             this.hiddenField.value = vv;
37324             
37325             this.lastSelectionText = dv;
37326             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37327             this.value = vv;
37328             return;
37329         }
37330         // no hidden field.. - we store the value in 'value', but still display
37331         // display field!!!!
37332         this.lastSelectionText = dv;
37333         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37334         this.value = vv;
37335         
37336         
37337     },
37338     // private
37339     reset : function(){
37340         // overridden so that last data is reset..
37341         this.setValue(this.originalValue);
37342         this.clearInvalid();
37343         this.lastData = false;
37344     },
37345     // private
37346     findRecord : function(prop, value){
37347         var record;
37348         if(this.store.getCount() > 0){
37349             this.store.each(function(r){
37350                 if(r.data[prop] == value){
37351                     record = r;
37352                     return false;
37353                 }
37354             });
37355         }
37356         return record;
37357     },
37358
37359     // private
37360     onViewMove : function(e, t){
37361         this.inKeyMode = false;
37362     },
37363
37364     // private
37365     onViewOver : function(e, t){
37366         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37367             return;
37368         }
37369         var item = this.view.findItemFromChild(t);
37370         if(item){
37371             var index = this.view.indexOf(item);
37372             this.select(index, false);
37373         }
37374     },
37375
37376     // private
37377     onViewClick : function(doFocus){
37378         var index = this.view.getSelectedIndexes()[0];
37379         var r = this.store.getAt(index);
37380         if(r){
37381             this.onSelect(r, index);
37382         }
37383         if(doFocus !== false && !this.blockFocus){
37384             this.el.focus();
37385         }
37386     },
37387
37388     // private
37389     restrictHeight : function(){
37390         this.innerList.dom.style.height = '';
37391         var inner = this.innerList.dom;
37392         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37393         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37394         this.list.beginUpdate();
37395         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37396         this.list.alignTo(this.el, this.listAlign);
37397         this.list.endUpdate();
37398     },
37399
37400     // private
37401     onEmptyResults : function(){
37402         this.collapse();
37403     },
37404
37405     /**
37406      * Returns true if the dropdown list is expanded, else false.
37407      */
37408     isExpanded : function(){
37409         return this.list.isVisible();
37410     },
37411
37412     /**
37413      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37414      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37415      * @param {String} value The data value of the item to select
37416      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37417      * selected item if it is not currently in view (defaults to true)
37418      * @return {Boolean} True if the value matched an item in the list, else false
37419      */
37420     selectByValue : function(v, scrollIntoView){
37421         if(v !== undefined && v !== null){
37422             var r = this.findRecord(this.valueField || this.displayField, v);
37423             if(r){
37424                 this.select(this.store.indexOf(r), scrollIntoView);
37425                 return true;
37426             }
37427         }
37428         return false;
37429     },
37430
37431     /**
37432      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37434      * @param {Number} index The zero-based index of the list item to select
37435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37436      * selected item if it is not currently in view (defaults to true)
37437      */
37438     select : function(index, scrollIntoView){
37439         this.selectedIndex = index;
37440         this.view.select(index);
37441         if(scrollIntoView !== false){
37442             var el = this.view.getNode(index);
37443             if(el){
37444                 this.innerList.scrollChildIntoView(el, false);
37445             }
37446         }
37447     },
37448
37449     // private
37450     selectNext : function(){
37451         var ct = this.store.getCount();
37452         if(ct > 0){
37453             if(this.selectedIndex == -1){
37454                 this.select(0);
37455             }else if(this.selectedIndex < ct-1){
37456                 this.select(this.selectedIndex+1);
37457             }
37458         }
37459     },
37460
37461     // private
37462     selectPrev : function(){
37463         var ct = this.store.getCount();
37464         if(ct > 0){
37465             if(this.selectedIndex == -1){
37466                 this.select(0);
37467             }else if(this.selectedIndex != 0){
37468                 this.select(this.selectedIndex-1);
37469             }
37470         }
37471     },
37472
37473     // private
37474     onKeyUp : function(e){
37475         if(this.editable !== false && !e.isSpecialKey()){
37476             this.lastKey = e.getKey();
37477             this.dqTask.delay(this.queryDelay);
37478         }
37479     },
37480
37481     // private
37482     validateBlur : function(){
37483         return !this.list || !this.list.isVisible();   
37484     },
37485
37486     // private
37487     initQuery : function(){
37488         this.doQuery(this.getRawValue());
37489     },
37490
37491     // private
37492     doForce : function(){
37493         if(this.el.dom.value.length > 0){
37494             this.el.dom.value =
37495                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37496             this.applyEmptyText();
37497         }
37498     },
37499
37500     /**
37501      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37502      * query allowing the query action to be canceled if needed.
37503      * @param {String} query The SQL query to execute
37504      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37505      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37506      * saved in the current store (defaults to false)
37507      */
37508     doQuery : function(q, forceAll){
37509         if(q === undefined || q === null){
37510             q = '';
37511         }
37512         var qe = {
37513             query: q,
37514             forceAll: forceAll,
37515             combo: this,
37516             cancel:false
37517         };
37518         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37519             return false;
37520         }
37521         q = qe.query;
37522         forceAll = qe.forceAll;
37523         if(forceAll === true || (q.length >= this.minChars)){
37524             if(this.lastQuery != q){
37525                 this.lastQuery = q;
37526                 if(this.mode == 'local'){
37527                     this.selectedIndex = -1;
37528                     if(forceAll){
37529                         this.store.clearFilter();
37530                     }else{
37531                         this.store.filter(this.displayField, q);
37532                     }
37533                     this.onLoad();
37534                 }else{
37535                     this.store.baseParams[this.queryParam] = q;
37536                     this.store.load({
37537                         params: this.getParams(q)
37538                     });
37539                     this.expand();
37540                 }
37541             }else{
37542                 this.selectedIndex = -1;
37543                 this.onLoad();   
37544             }
37545         }
37546     },
37547
37548     // private
37549     getParams : function(q){
37550         var p = {};
37551         //p[this.queryParam] = q;
37552         if(this.pageSize){
37553             p.start = 0;
37554             p.limit = this.pageSize;
37555         }
37556         return p;
37557     },
37558
37559     /**
37560      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37561      */
37562     collapse : function(){
37563         if(!this.isExpanded()){
37564             return;
37565         }
37566         this.list.hide();
37567         Roo.get(document).un('mousedown', this.collapseIf, this);
37568         Roo.get(document).un('mousewheel', this.collapseIf, this);
37569         this.fireEvent('collapse', this);
37570     },
37571
37572     // private
37573     collapseIf : function(e){
37574         if(!e.within(this.wrap) && !e.within(this.list)){
37575             this.collapse();
37576         }
37577     },
37578
37579     /**
37580      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37581      */
37582     expand : function(){
37583         if(this.isExpanded() || !this.hasFocus){
37584             return;
37585         }
37586         this.list.alignTo(this.el, this.listAlign);
37587         this.list.show();
37588         Roo.get(document).on('mousedown', this.collapseIf, this);
37589         Roo.get(document).on('mousewheel', this.collapseIf, this);
37590         this.fireEvent('expand', this);
37591     },
37592
37593     // private
37594     // Implements the default empty TriggerField.onTriggerClick function
37595     onTriggerClick : function(){
37596         if(this.disabled){
37597             return;
37598         }
37599         if(this.isExpanded()){
37600             this.collapse();
37601             if (!this.blockFocus) {
37602                 this.el.focus();
37603             }
37604             
37605         }else {
37606             this.hasFocus = true;
37607             if(this.triggerAction == 'all') {
37608                 this.doQuery(this.allQuery, true);
37609             } else {
37610                 this.doQuery(this.getRawValue());
37611             }
37612             if (!this.blockFocus) {
37613                 this.el.focus();
37614             }
37615         }
37616     }
37617
37618     /** 
37619     * @cfg {Boolean} grow 
37620     * @hide 
37621     */
37622     /** 
37623     * @cfg {Number} growMin 
37624     * @hide 
37625     */
37626     /** 
37627     * @cfg {Number} growMax 
37628     * @hide 
37629     */
37630     /**
37631      * @hide
37632      * @method autoSize
37633      */
37634 });/*
37635  * Based on:
37636  * Ext JS Library 1.1.1
37637  * Copyright(c) 2006-2007, Ext JS, LLC.
37638  *
37639  * Originally Released Under LGPL - original licence link has changed is not relivant.
37640  *
37641  * Fork - LGPL
37642  * <script type="text/javascript">
37643  */
37644 /**
37645  * @class Roo.form.Checkbox
37646  * @extends Roo.form.Field
37647  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
37648  * @constructor
37649  * Creates a new Checkbox
37650  * @param {Object} config Configuration options
37651  */
37652 Roo.form.Checkbox = function(config){
37653     Roo.form.Checkbox.superclass.constructor.call(this, config);
37654     this.addEvents({
37655         /**
37656          * @event check
37657          * Fires when the checkbox is checked or unchecked.
37658              * @param {Roo.form.Checkbox} this This checkbox
37659              * @param {Boolean} checked The new checked value
37660              */
37661         check : true
37662     });
37663 };
37664
37665 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
37666     /**
37667      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
37668      */
37669     focusClass : undefined,
37670     /**
37671      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
37672      */
37673     fieldClass: "x-form-field",
37674     /**
37675      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
37676      */
37677     checked: false,
37678     /**
37679      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37680      * {tag: "input", type: "checkbox", autocomplete: "off"})
37681      */
37682     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
37683     /**
37684      * @cfg {String} boxLabel The text that appears beside the checkbox
37685      */
37686     boxLabel : "",
37687     /**
37688      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
37689      */  
37690     inputValue : '1',
37691     /**
37692      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
37693      */
37694      valueOff: '0', // value when not checked..
37695
37696     actionMode : 'viewEl', 
37697     //
37698     // private
37699     itemCls : 'x-menu-check-item x-form-item',
37700     groupClass : 'x-menu-group-item',
37701     inputType : 'hidden',
37702     
37703     
37704     inSetChecked: false, // check that we are not calling self...
37705     
37706     inputElement: false, // real input element?
37707     basedOn: false, // ????
37708     
37709     isFormField: true, // not sure where this is needed!!!!
37710
37711     onResize : function(){
37712         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
37713         if(!this.boxLabel){
37714             this.el.alignTo(this.wrap, 'c-c');
37715         }
37716     },
37717
37718     initEvents : function(){
37719         Roo.form.Checkbox.superclass.initEvents.call(this);
37720         this.el.on("click", this.onClick,  this);
37721         this.el.on("change", this.onClick,  this);
37722     },
37723
37724
37725     getResizeEl : function(){
37726         return this.wrap;
37727     },
37728
37729     getPositionEl : function(){
37730         return this.wrap;
37731     },
37732
37733     // private
37734     onRender : function(ct, position){
37735         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
37736         /*
37737         if(this.inputValue !== undefined){
37738             this.el.dom.value = this.inputValue;
37739         }
37740         */
37741         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
37742         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
37743         var viewEl = this.wrap.createChild({ 
37744             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
37745         this.viewEl = viewEl;   
37746         this.wrap.on('click', this.onClick,  this); 
37747         
37748         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
37749         this.el.on('propertychange', this.setFromHidden,  this);  //ie
37750         
37751         
37752         
37753         if(this.boxLabel){
37754             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
37755         //    viewEl.on('click', this.onClick,  this); 
37756         }
37757         //if(this.checked){
37758             this.setChecked(this.checked);
37759         //}else{
37760             //this.checked = this.el.dom;
37761         //}
37762
37763     },
37764
37765     // private
37766     initValue : Roo.emptyFn,
37767
37768     /**
37769      * Returns the checked state of the checkbox.
37770      * @return {Boolean} True if checked, else false
37771      */
37772     getValue : function(){
37773         if(this.el){
37774             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
37775         }
37776         return this.valueOff;
37777         
37778     },
37779
37780         // private
37781     onClick : function(){ 
37782         this.setChecked(!this.checked);
37783
37784         //if(this.el.dom.checked != this.checked){
37785         //    this.setValue(this.el.dom.checked);
37786        // }
37787     },
37788
37789     /**
37790      * Sets the checked state of the checkbox.
37791      * On is always based on a string comparison between inputValue and the param.
37792      * @param {Boolean/String} value - the value to set 
37793      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
37794      */
37795     setValue : function(v,suppressEvent){
37796         
37797         
37798         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
37799         //if(this.el && this.el.dom){
37800         //    this.el.dom.checked = this.checked;
37801         //    this.el.dom.defaultChecked = this.checked;
37802         //}
37803         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
37804         //this.fireEvent("check", this, this.checked);
37805     },
37806     // private..
37807     setChecked : function(state,suppressEvent)
37808     {
37809         if (this.inSetChecked) {
37810             this.checked = state;
37811             return;
37812         }
37813         
37814     
37815         if(this.wrap){
37816             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
37817         }
37818         this.checked = state;
37819         if(suppressEvent !== true){
37820             this.fireEvent('checkchange', this, state);
37821         }
37822         this.inSetChecked = true;
37823         this.el.dom.value = state ? this.inputValue : this.valueOff;
37824         this.inSetChecked = false;
37825         
37826     },
37827     // handle setting of hidden value by some other method!!?!?
37828     setFromHidden: function()
37829     {
37830         if(!this.el){
37831             return;
37832         }
37833         //console.log("SET FROM HIDDEN");
37834         //alert('setFrom hidden');
37835         this.setValue(this.el.dom.value);
37836     },
37837     
37838     onDestroy : function()
37839     {
37840         if(this.viewEl){
37841             Roo.get(this.viewEl).remove();
37842         }
37843          
37844         Roo.form.Checkbox.superclass.onDestroy.call(this);
37845     }
37846
37847 });/*
37848  * Based on:
37849  * Ext JS Library 1.1.1
37850  * Copyright(c) 2006-2007, Ext JS, LLC.
37851  *
37852  * Originally Released Under LGPL - original licence link has changed is not relivant.
37853  *
37854  * Fork - LGPL
37855  * <script type="text/javascript">
37856  */
37857  
37858 /**
37859  * @class Roo.form.Radio
37860  * @extends Roo.form.Checkbox
37861  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
37862  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
37863  * @constructor
37864  * Creates a new Radio
37865  * @param {Object} config Configuration options
37866  */
37867 Roo.form.Radio = function(){
37868     Roo.form.Radio.superclass.constructor.apply(this, arguments);
37869 };
37870 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
37871     inputType: 'radio',
37872
37873     /**
37874      * If this radio is part of a group, it will return the selected value
37875      * @return {String}
37876      */
37877     getGroupValue : function(){
37878         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
37879     }
37880 });//<script type="text/javascript">
37881
37882 /*
37883  * Ext JS Library 1.1.1
37884  * Copyright(c) 2006-2007, Ext JS, LLC.
37885  * licensing@extjs.com
37886  * 
37887  * http://www.extjs.com/license
37888  */
37889  
37890  /*
37891   * 
37892   * Known bugs:
37893   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
37894   * - IE ? - no idea how much works there.
37895   * 
37896   * 
37897   * 
37898   */
37899  
37900
37901 /**
37902  * @class Ext.form.HtmlEditor
37903  * @extends Ext.form.Field
37904  * Provides a lightweight HTML Editor component.
37905  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
37906  * 
37907  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
37908  * supported by this editor.</b><br/><br/>
37909  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
37910  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
37911  */
37912 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
37913       /**
37914      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
37915      */
37916     toolbars : false,
37917     /**
37918      * @cfg {String} createLinkText The default text for the create link prompt
37919      */
37920     createLinkText : 'Please enter the URL for the link:',
37921     /**
37922      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
37923      */
37924     defaultLinkValue : 'http:/'+'/',
37925    
37926     
37927     // id of frame..
37928     frameId: false,
37929     
37930     // private properties
37931     validationEvent : false,
37932     deferHeight: true,
37933     initialized : false,
37934     activated : false,
37935     sourceEditMode : false,
37936     onFocus : Roo.emptyFn,
37937     iframePad:3,
37938     hideMode:'offsets',
37939     defaultAutoCreate : {
37940         tag: "textarea",
37941         style:"width:500px;height:300px;",
37942         autocomplete: "off"
37943     },
37944
37945     // private
37946     initComponent : function(){
37947         this.addEvents({
37948             /**
37949              * @event initialize
37950              * Fires when the editor is fully initialized (including the iframe)
37951              * @param {HtmlEditor} this
37952              */
37953             initialize: true,
37954             /**
37955              * @event activate
37956              * Fires when the editor is first receives the focus. Any insertion must wait
37957              * until after this event.
37958              * @param {HtmlEditor} this
37959              */
37960             activate: true,
37961              /**
37962              * @event beforesync
37963              * Fires before the textarea is updated with content from the editor iframe. Return false
37964              * to cancel the sync.
37965              * @param {HtmlEditor} this
37966              * @param {String} html
37967              */
37968             beforesync: true,
37969              /**
37970              * @event beforepush
37971              * Fires before the iframe editor is updated with content from the textarea. Return false
37972              * to cancel the push.
37973              * @param {HtmlEditor} this
37974              * @param {String} html
37975              */
37976             beforepush: true,
37977              /**
37978              * @event sync
37979              * Fires when the textarea is updated with content from the editor iframe.
37980              * @param {HtmlEditor} this
37981              * @param {String} html
37982              */
37983             sync: true,
37984              /**
37985              * @event push
37986              * Fires when the iframe editor is updated with content from the textarea.
37987              * @param {HtmlEditor} this
37988              * @param {String} html
37989              */
37990             push: true,
37991              /**
37992              * @event editmodechange
37993              * Fires when the editor switches edit modes
37994              * @param {HtmlEditor} this
37995              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
37996              */
37997             editmodechange: true,
37998             /**
37999              * @event editorevent
38000              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38001              * @param {HtmlEditor} this
38002              */
38003             editorevent: true
38004         })
38005     },
38006
38007     /**
38008      * Protected method that will not generally be called directly. It
38009      * is called when the editor creates its toolbar. Override this method if you need to
38010      * add custom toolbar buttons.
38011      * @param {HtmlEditor} editor
38012      */
38013     createToolbar : function(editor){
38014         if (!editor.toolbars || !editor.toolbars.length) {
38015             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38016         }
38017         
38018         for (var i =0 ; i < editor.toolbars.length;i++) {
38019             editor.toolbars[i].init(editor);
38020         }
38021          
38022         
38023     },
38024
38025     /**
38026      * Protected method that will not generally be called directly. It
38027      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38028      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38029      */
38030     getDocMarkup : function(){
38031         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38032     },
38033
38034     // private
38035     onRender : function(ct, position){
38036         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38037         this.el.dom.style.border = '0 none';
38038         this.el.dom.setAttribute('tabIndex', -1);
38039         this.el.addClass('x-hidden');
38040         if(Roo.isIE){ // fix IE 1px bogus margin
38041             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38042         }
38043         this.wrap = this.el.wrap({
38044             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38045         });
38046
38047         this.frameId = Roo.id();
38048         this.createToolbar(this);
38049         
38050         
38051         
38052         
38053       
38054         
38055         var iframe = this.wrap.createChild({
38056             tag: 'iframe',
38057             id: this.frameId,
38058             name: this.frameId,
38059             frameBorder : 'no',
38060             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38061         });
38062         
38063        // console.log(iframe);
38064         //this.wrap.dom.appendChild(iframe);
38065
38066         this.iframe = iframe.dom;
38067
38068          this.assignDocWin();
38069         
38070         this.doc.designMode = 'on';
38071        
38072         this.doc.open();
38073         this.doc.write(this.getDocMarkup());
38074         this.doc.close();
38075
38076         
38077         var task = { // must defer to wait for browser to be ready
38078             run : function(){
38079                 //console.log("run task?" + this.doc.readyState);
38080                 this.assignDocWin();
38081                 if(this.doc.body || this.doc.readyState == 'complete'){
38082                     try {
38083                         this.doc.designMode="on";
38084                     } catch (e) {
38085                         return;
38086                     }
38087                     Roo.TaskMgr.stop(task);
38088                     this.initEditor.defer(10, this);
38089                 }
38090             },
38091             interval : 10,
38092             duration:10000,
38093             scope: this
38094         };
38095         Roo.TaskMgr.start(task);
38096
38097         if(!this.width){
38098             this.setSize(this.el.getSize());
38099         }
38100     },
38101
38102     // private
38103     onResize : function(w, h){
38104         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38105         if(this.el && this.iframe){
38106             if(typeof w == 'number'){
38107                 var aw = w - this.wrap.getFrameWidth('lr');
38108                 this.el.setWidth(this.adjustWidth('textarea', aw));
38109                 this.iframe.style.width = aw + 'px';
38110             }
38111             if(typeof h == 'number'){
38112                 var tbh = 0;
38113                 for (var i =0; i < this.toolbars.length;i++) {
38114                     // fixme - ask toolbars for heights?
38115                     tbh += this.toolbars[i].tb.el.getHeight();
38116                 }
38117                 
38118                 
38119                 
38120                 
38121                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38122                 this.el.setHeight(this.adjustWidth('textarea', ah));
38123                 this.iframe.style.height = ah + 'px';
38124                 if(this.doc){
38125                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38126                 }
38127             }
38128         }
38129     },
38130
38131     /**
38132      * Toggles the editor between standard and source edit mode.
38133      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38134      */
38135     toggleSourceEdit : function(sourceEditMode){
38136         
38137         this.sourceEditMode = sourceEditMode === true;
38138         
38139         if(this.sourceEditMode){
38140           
38141             this.syncValue();
38142             this.iframe.className = 'x-hidden';
38143             this.el.removeClass('x-hidden');
38144             this.el.dom.removeAttribute('tabIndex');
38145             this.el.focus();
38146         }else{
38147              
38148             this.pushValue();
38149             this.iframe.className = '';
38150             this.el.addClass('x-hidden');
38151             this.el.dom.setAttribute('tabIndex', -1);
38152             this.deferFocus();
38153         }
38154         this.setSize(this.wrap.getSize());
38155         this.fireEvent('editmodechange', this, this.sourceEditMode);
38156     },
38157
38158     // private used internally
38159     createLink : function(){
38160         var url = prompt(this.createLinkText, this.defaultLinkValue);
38161         if(url && url != 'http:/'+'/'){
38162             this.relayCmd('createlink', url);
38163         }
38164     },
38165
38166     // private (for BoxComponent)
38167     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38168
38169     // private (for BoxComponent)
38170     getResizeEl : function(){
38171         return this.wrap;
38172     },
38173
38174     // private (for BoxComponent)
38175     getPositionEl : function(){
38176         return this.wrap;
38177     },
38178
38179     // private
38180     initEvents : function(){
38181         this.originalValue = this.getValue();
38182     },
38183
38184     /**
38185      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38186      * @method
38187      */
38188     markInvalid : Roo.emptyFn,
38189     /**
38190      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38191      * @method
38192      */
38193     clearInvalid : Roo.emptyFn,
38194
38195     setValue : function(v){
38196         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38197         this.pushValue();
38198     },
38199
38200     /**
38201      * Protected method that will not generally be called directly. If you need/want
38202      * custom HTML cleanup, this is the method you should override.
38203      * @param {String} html The HTML to be cleaned
38204      * return {String} The cleaned HTML
38205      */
38206     cleanHtml : function(html){
38207         html = String(html);
38208         if(html.length > 5){
38209             if(Roo.isSafari){ // strip safari nonsense
38210                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38211             }
38212         }
38213         if(html == '&nbsp;'){
38214             html = '';
38215         }
38216         return html;
38217     },
38218
38219     /**
38220      * Protected method that will not generally be called directly. Syncs the contents
38221      * of the editor iframe with the textarea.
38222      */
38223     syncValue : function(){
38224         if(this.initialized){
38225             var bd = (this.doc.body || this.doc.documentElement);
38226             var html = bd.innerHTML;
38227             if(Roo.isSafari){
38228                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38229                 var m = bs.match(/text-align:(.*?);/i);
38230                 if(m && m[1]){
38231                     html = '<div style="'+m[0]+'">' + html + '</div>';
38232                 }
38233             }
38234             html = this.cleanHtml(html);
38235             if(this.fireEvent('beforesync', this, html) !== false){
38236                 this.el.dom.value = html;
38237                 this.fireEvent('sync', this, html);
38238             }
38239         }
38240     },
38241
38242     /**
38243      * Protected method that will not generally be called directly. Pushes the value of the textarea
38244      * into the iframe editor.
38245      */
38246     pushValue : function(){
38247         if(this.initialized){
38248             var v = this.el.dom.value;
38249             if(v.length < 1){
38250                 v = '&#160;';
38251             }
38252             if(this.fireEvent('beforepush', this, v) !== false){
38253                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38254                 this.fireEvent('push', this, v);
38255             }
38256         }
38257     },
38258
38259     // private
38260     deferFocus : function(){
38261         this.focus.defer(10, this);
38262     },
38263
38264     // doc'ed in Field
38265     focus : function(){
38266         if(this.win && !this.sourceEditMode){
38267             this.win.focus();
38268         }else{
38269             this.el.focus();
38270         }
38271     },
38272     
38273     assignDocWin: function()
38274     {
38275         var iframe = this.iframe;
38276         
38277          if(Roo.isIE){
38278             this.doc = iframe.contentWindow.document;
38279             this.win = iframe.contentWindow;
38280         } else {
38281             if (!Roo.get(this.frameId)) {
38282                 return;
38283             }
38284             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38285             this.win = Roo.get(this.frameId).dom.contentWindow;
38286         }
38287     },
38288     
38289     // private
38290     initEditor : function(){
38291         //console.log("INIT EDITOR");
38292         this.assignDocWin();
38293         
38294         
38295         
38296         this.doc.designMode="on";
38297         this.doc.open();
38298         this.doc.write(this.getDocMarkup());
38299         this.doc.close();
38300         
38301         var dbody = (this.doc.body || this.doc.documentElement);
38302         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38303         // this copies styles from the containing element into thsi one..
38304         // not sure why we need all of this..
38305         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38306         ss['background-attachment'] = 'fixed'; // w3c
38307         dbody.bgProperties = 'fixed'; // ie
38308         Roo.DomHelper.applyStyles(dbody, ss);
38309         Roo.EventManager.on(this.doc, {
38310             'mousedown': this.onEditorEvent,
38311             'dblclick': this.onEditorEvent,
38312             'click': this.onEditorEvent,
38313             'keyup': this.onEditorEvent,
38314             buffer:100,
38315             scope: this
38316         });
38317         if(Roo.isGecko){
38318             Roo.EventManager.on(this.doc, 'keypress', this.applyCommand, this);
38319         }
38320         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38321             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38322         }
38323         this.initialized = true;
38324
38325         this.fireEvent('initialize', this);
38326         this.pushValue();
38327     },
38328
38329     // private
38330     onDestroy : function(){
38331         
38332         
38333         
38334         if(this.rendered){
38335             
38336             for (var i =0; i < this.toolbars.length;i++) {
38337                 // fixme - ask toolbars for heights?
38338                 this.toolbars[i].onDestroy();
38339             }
38340             
38341             this.wrap.dom.innerHTML = '';
38342             this.wrap.remove();
38343         }
38344     },
38345
38346     // private
38347     onFirstFocus : function(){
38348         
38349         this.assignDocWin();
38350         
38351         
38352         this.activated = true;
38353         for (var i =0; i < this.toolbars.length;i++) {
38354             this.toolbars[i].onFirstFocus();
38355         }
38356        
38357         if(Roo.isGecko){ // prevent silly gecko errors
38358             this.win.focus();
38359             var s = this.win.getSelection();
38360             if(!s.focusNode || s.focusNode.nodeType != 3){
38361                 var r = s.getRangeAt(0);
38362                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38363                 r.collapse(true);
38364                 this.deferFocus();
38365             }
38366             try{
38367                 this.execCmd('useCSS', true);
38368                 this.execCmd('styleWithCSS', false);
38369             }catch(e){}
38370         }
38371         this.fireEvent('activate', this);
38372     },
38373
38374     // private
38375     adjustFont: function(btn){
38376         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38377         //if(Roo.isSafari){ // safari
38378         //    adjust *= 2;
38379        // }
38380         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38381         if(Roo.isSafari){ // safari
38382             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38383             v =  (v < 10) ? 10 : v;
38384             v =  (v > 48) ? 48 : v;
38385             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38386             
38387         }
38388         
38389         
38390         v = Math.max(1, v+adjust);
38391         
38392         this.execCmd('FontSize', v  );
38393     },
38394
38395     onEditorEvent : function(e){
38396         this.fireEvent('editorevent', this, e);
38397       //  this.updateToolbar();
38398         this.syncValue();
38399     },
38400
38401     insertTag : function(tg)
38402     {
38403         // could be a bit smarter... -> wrap the current selected tRoo..
38404         
38405         this.execCmd("formatblock",   tg);
38406         
38407     },
38408     
38409     insertText : function(txt)
38410     {
38411         
38412         
38413         range = this.createRange();
38414         range.deleteContents();
38415                //alert(Sender.getAttribute('label'));
38416                
38417         range.insertNode(this.doc.createTextNode(txt));
38418     } ,
38419     
38420     // private
38421     relayBtnCmd : function(btn){
38422         this.relayCmd(btn.cmd);
38423     },
38424
38425     /**
38426      * Executes a Midas editor command on the editor document and performs necessary focus and
38427      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38428      * @param {String} cmd The Midas command
38429      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38430      */
38431     relayCmd : function(cmd, value){
38432         this.win.focus();
38433         this.execCmd(cmd, value);
38434         this.fireEvent('editorevent', this);
38435         //this.updateToolbar();
38436         this.deferFocus();
38437     },
38438
38439     /**
38440      * Executes a Midas editor command directly on the editor document.
38441      * For visual commands, you should use {@link #relayCmd} instead.
38442      * <b>This should only be called after the editor is initialized.</b>
38443      * @param {String} cmd The Midas command
38444      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38445      */
38446     execCmd : function(cmd, value){
38447         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38448         this.syncValue();
38449     },
38450
38451     // private
38452     applyCommand : function(e){
38453         if(e.ctrlKey){
38454             var c = e.getCharCode(), cmd;
38455             if(c > 0){
38456                 c = String.fromCharCode(c);
38457                 switch(c){
38458                     case 'b':
38459                         cmd = 'bold';
38460                     break;
38461                     case 'i':
38462                         cmd = 'italic';
38463                     break;
38464                     case 'u':
38465                         cmd = 'underline';
38466                     break;
38467                 }
38468                 if(cmd){
38469                     this.win.focus();
38470                     this.execCmd(cmd);
38471                     this.deferFocus();
38472                     e.preventDefault();
38473                 }
38474             }
38475         }
38476     },
38477
38478     /**
38479      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38480      * to insert tRoo.
38481      * @param {String} text
38482      */
38483     insertAtCursor : function(text){
38484         if(!this.activated){
38485             return;
38486         }
38487         if(Roo.isIE){
38488             this.win.focus();
38489             var r = this.doc.selection.createRange();
38490             if(r){
38491                 r.collapse(true);
38492                 r.pasteHTML(text);
38493                 this.syncValue();
38494                 this.deferFocus();
38495             }
38496         }else if(Roo.isGecko || Roo.isOpera){
38497             this.win.focus();
38498             this.execCmd('InsertHTML', text);
38499             this.deferFocus();
38500         }else if(Roo.isSafari){
38501             this.execCmd('InsertText', text);
38502             this.deferFocus();
38503         }
38504     },
38505
38506     // private
38507     fixKeys : function(){ // load time branching for fastest keydown performance
38508         if(Roo.isIE){
38509             return function(e){
38510                 var k = e.getKey(), r;
38511                 if(k == e.TAB){
38512                     e.stopEvent();
38513                     r = this.doc.selection.createRange();
38514                     if(r){
38515                         r.collapse(true);
38516                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38517                         this.deferFocus();
38518                     }
38519                 }else if(k == e.ENTER){
38520                     r = this.doc.selection.createRange();
38521                     if(r){
38522                         var target = r.parentElement();
38523                         if(!target || target.tagName.toLowerCase() != 'li'){
38524                             e.stopEvent();
38525                             r.pasteHTML('<br />');
38526                             r.collapse(false);
38527                             r.select();
38528                         }
38529                     }
38530                 }
38531             };
38532         }else if(Roo.isOpera){
38533             return function(e){
38534                 var k = e.getKey();
38535                 if(k == e.TAB){
38536                     e.stopEvent();
38537                     this.win.focus();
38538                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38539                     this.deferFocus();
38540                 }
38541             };
38542         }else if(Roo.isSafari){
38543             return function(e){
38544                 var k = e.getKey();
38545                 if(k == e.TAB){
38546                     e.stopEvent();
38547                     this.execCmd('InsertText','\t');
38548                     this.deferFocus();
38549                 }
38550              };
38551         }
38552     }(),
38553     
38554     getAllAncestors: function()
38555     {
38556         var p = this.getSelectedNode();
38557         var a = [];
38558         if (!p) {
38559             a.push(p); // push blank onto stack..
38560             p = this.getParentElement();
38561         }
38562         
38563         
38564         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38565             a.push(p);
38566             p = p.parentNode;
38567         }
38568         a.push(this.doc.body);
38569         return a;
38570     },
38571     lastSel : false,
38572     lastSelNode : false,
38573     
38574     
38575     getSelection : function() 
38576     {
38577         this.assignDocWin();
38578         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38579     },
38580     
38581     getSelectedNode: function() 
38582     {
38583         // this may only work on Gecko!!!
38584         
38585         // should we cache this!!!!
38586         
38587         
38588         
38589          
38590         var range = this.createRange(this.getSelection());
38591         
38592         if (Roo.isIE) {
38593             var parent = range.parentElement();
38594             while (true) {
38595                 var testRange = range.duplicate();
38596                 testRange.moveToElementText(parent);
38597                 if (testRange.inRange(range)) {
38598                     break;
38599                 }
38600                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38601                     break;
38602                 }
38603                 parent = parent.parentElement;
38604             }
38605             return parent;
38606         }
38607         
38608         
38609         var ar = range.endContainer.childNodes;
38610         if (!ar.length) {
38611             ar = range.commonAncestorContainer.childNodes;
38612             //alert(ar.length);
38613         }
38614         var nodes = [];
38615         var other_nodes = [];
38616         var has_other_nodes = false;
38617         for (var i=0;i<ar.length;i++) {
38618             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
38619                 continue;
38620             }
38621             // fullly contained node.
38622             
38623             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
38624                 nodes.push(ar[i]);
38625                 continue;
38626             }
38627             
38628             // probably selected..
38629             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
38630                 other_nodes.push(ar[i]);
38631                 continue;
38632             }
38633             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
38634                 continue;
38635             }
38636             
38637             
38638             has_other_nodes = true;
38639         }
38640         if (!nodes.length && other_nodes.length) {
38641             nodes= other_nodes;
38642         }
38643         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
38644             return false;
38645         }
38646         
38647         return nodes[0];
38648     },
38649     createRange: function(sel)
38650     {
38651         // this has strange effects when using with 
38652         // top toolbar - not sure if it's a great idea.
38653         //this.editor.contentWindow.focus();
38654         if (typeof sel != "undefined") {
38655             try {
38656                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
38657             } catch(e) {
38658                 return this.doc.createRange();
38659             }
38660         } else {
38661             return this.doc.createRange();
38662         }
38663     },
38664     getParentElement: function()
38665     {
38666         
38667         this.assignDocWin();
38668         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
38669         
38670         var range = this.createRange(sel);
38671          
38672         try {
38673             var p = range.commonAncestorContainer;
38674             while (p.nodeType == 3) { // text node
38675                 p = p.parentNode;
38676             }
38677             return p;
38678         } catch (e) {
38679             return null;
38680         }
38681     
38682     },
38683     
38684     
38685     
38686     // BC Hacks - cause I cant work out what i was trying to do..
38687     rangeIntersectsNode : function(range, node)
38688     {
38689         var nodeRange = node.ownerDocument.createRange();
38690         try {
38691             nodeRange.selectNode(node);
38692         }
38693         catch (e) {
38694             nodeRange.selectNodeContents(node);
38695         }
38696
38697         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
38698                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
38699     },
38700     rangeCompareNode : function(range, node) {
38701         var nodeRange = node.ownerDocument.createRange();
38702         try {
38703             nodeRange.selectNode(node);
38704         } catch (e) {
38705             nodeRange.selectNodeContents(node);
38706         }
38707         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
38708         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
38709
38710         if (nodeIsBefore && !nodeIsAfter)
38711             return 0;
38712         if (!nodeIsBefore && nodeIsAfter)
38713             return 1;
38714         if (nodeIsBefore && nodeIsAfter)
38715             return 2;
38716
38717         return 3;
38718     }
38719
38720     
38721     
38722     // hide stuff that is not compatible
38723     /**
38724      * @event blur
38725      * @hide
38726      */
38727     /**
38728      * @event change
38729      * @hide
38730      */
38731     /**
38732      * @event focus
38733      * @hide
38734      */
38735     /**
38736      * @event specialkey
38737      * @hide
38738      */
38739     /**
38740      * @cfg {String} fieldClass @hide
38741      */
38742     /**
38743      * @cfg {String} focusClass @hide
38744      */
38745     /**
38746      * @cfg {String} autoCreate @hide
38747      */
38748     /**
38749      * @cfg {String} inputType @hide
38750      */
38751     /**
38752      * @cfg {String} invalidClass @hide
38753      */
38754     /**
38755      * @cfg {String} invalidText @hide
38756      */
38757     /**
38758      * @cfg {String} msgFx @hide
38759      */
38760     /**
38761      * @cfg {String} validateOnBlur @hide
38762      */
38763 });// <script type="text/javascript">
38764 /*
38765  * Based on
38766  * Ext JS Library 1.1.1
38767  * Copyright(c) 2006-2007, Ext JS, LLC.
38768  *  
38769  
38770  */
38771
38772 /**
38773  * @class Roo.form.HtmlEditorToolbar1
38774  * Basic Toolbar
38775  * 
38776  * Usage:
38777  *
38778  new Roo.form.HtmlEditor({
38779     ....
38780     toolbars : [
38781         new Roo.form.HtmlEditorToolbar1({
38782             disable : { fonts: 1 , format: 1, ..., ... , ...],
38783             btns : [ .... ]
38784         })
38785     }
38786      
38787  * 
38788  * @cfg {Object} disable List of elements to disable..
38789  * @cfg {Array} btns List of additional buttons.
38790  * 
38791  * 
38792  * NEEDS Extra CSS? 
38793  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
38794  */
38795  
38796 Roo.form.HtmlEditor.ToolbarStandard = function(config)
38797 {
38798     
38799     Roo.apply(this, config);
38800     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
38801     // dont call parent... till later.
38802 }
38803
38804 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
38805     
38806     tb: false,
38807     
38808     rendered: false,
38809     
38810     editor : false,
38811     /**
38812      * @cfg {Object} disable  List of toolbar elements to disable
38813          
38814      */
38815     disable : false,
38816       /**
38817      * @cfg {Array} fontFamilies An array of available font families
38818      */
38819     fontFamilies : [
38820         'Arial',
38821         'Courier New',
38822         'Tahoma',
38823         'Times New Roman',
38824         'Verdana'
38825     ],
38826     
38827     specialChars : [
38828            "&#169;",
38829           "&#174;",     
38830           "&#8482;",    
38831           "&#163;" ,    
38832          // "&#8212;",    
38833           "&#8230;",    
38834           "&#247;" ,    
38835         //  "&#225;" ,     ?? a acute?
38836            "&#8364;"    , //Euro
38837        //   "&#8220;"    ,
38838         //  "&#8221;"    ,
38839         //  "&#8226;"    ,
38840           "&#176;"  //   , // degrees
38841
38842          // "&#233;"     , // e ecute
38843          // "&#250;"     , // u ecute?
38844     ],
38845     inputElements : [ 
38846             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
38847             "input:submit", "input:button", "select", "textarea", "label" ],
38848     formats : [
38849         ["p"] ,  
38850         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
38851         ["pre"],[ "code"], 
38852         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
38853     ],
38854      /**
38855      * @cfg {String} defaultFont default font to use.
38856      */
38857     defaultFont: 'tahoma',
38858    
38859     fontSelect : false,
38860     
38861     
38862     formatCombo : false,
38863     
38864     init : function(editor)
38865     {
38866         this.editor = editor;
38867         
38868         
38869         var fid = editor.frameId;
38870         var etb = this;
38871         function btn(id, toggle, handler){
38872             var xid = fid + '-'+ id ;
38873             return {
38874                 id : xid,
38875                 cmd : id,
38876                 cls : 'x-btn-icon x-edit-'+id,
38877                 enableToggle:toggle !== false,
38878                 scope: editor, // was editor...
38879                 handler:handler||editor.relayBtnCmd,
38880                 clickEvent:'mousedown',
38881                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
38882                 tabIndex:-1
38883             };
38884         }
38885         
38886         
38887         
38888         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
38889         this.tb = tb;
38890          // stop form submits
38891         tb.el.on('click', function(e){
38892             e.preventDefault(); // what does this do?
38893         });
38894
38895         if(!this.disable.font && !Roo.isSafari){
38896             /* why no safari for fonts
38897             editor.fontSelect = tb.el.createChild({
38898                 tag:'select',
38899                 tabIndex: -1,
38900                 cls:'x-font-select',
38901                 html: editor.createFontOptions()
38902             });
38903             editor.fontSelect.on('change', function(){
38904                 var font = editor.fontSelect.dom.value;
38905                 editor.relayCmd('fontname', font);
38906                 editor.deferFocus();
38907             }, editor);
38908             tb.add(
38909                 editor.fontSelect.dom,
38910                 '-'
38911             );
38912             */
38913         };
38914         if(!this.disable.formats){
38915             this.formatCombo = new Roo.form.ComboBox({
38916                 store: new Roo.data.SimpleStore({
38917                     id : 'tag',
38918                     fields: ['tag'],
38919                     data : this.formats // from states.js
38920                 }),
38921                 blockFocus : true,
38922                 //autoCreate : {tag: "div",  size: "20"},
38923                 displayField:'tag',
38924                 typeAhead: false,
38925                 mode: 'local',
38926                 editable : false,
38927                 triggerAction: 'all',
38928                 emptyText:'Add tag',
38929                 selectOnFocus:true,
38930                 width:135,
38931                 listeners : {
38932                     'select': function(c, r, i) {
38933                         editor.insertTag(r.get('tag'));
38934                         editor.focus();
38935                     }
38936                 }
38937
38938             });
38939             tb.addField(this.formatCombo);
38940             
38941         }
38942         
38943         if(!this.disable.format){
38944             tb.add(
38945                 btn('bold'),
38946                 btn('italic'),
38947                 btn('underline')
38948             );
38949         };
38950         if(!this.disable.fontSize){
38951             tb.add(
38952                 '-',
38953                 
38954                 
38955                 btn('increasefontsize', false, editor.adjustFont),
38956                 btn('decreasefontsize', false, editor.adjustFont)
38957             );
38958         };
38959         
38960         
38961         if(this.disable.colors){
38962             tb.add(
38963                 '-', {
38964                     id:editor.frameId +'-forecolor',
38965                     cls:'x-btn-icon x-edit-forecolor',
38966                     clickEvent:'mousedown',
38967                     tooltip: this.buttonTips['forecolor'] || undefined,
38968                     tabIndex:-1,
38969                     menu : new Roo.menu.ColorMenu({
38970                         allowReselect: true,
38971                         focus: Roo.emptyFn,
38972                         value:'000000',
38973                         plain:true,
38974                         selectHandler: function(cp, color){
38975                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
38976                             editor.deferFocus();
38977                         },
38978                         scope: editor,
38979                         clickEvent:'mousedown'
38980                     })
38981                 }, {
38982                     id:editor.frameId +'backcolor',
38983                     cls:'x-btn-icon x-edit-backcolor',
38984                     clickEvent:'mousedown',
38985                     tooltip: this.buttonTips['backcolor'] || undefined,
38986                     tabIndex:-1,
38987                     menu : new Roo.menu.ColorMenu({
38988                         focus: Roo.emptyFn,
38989                         value:'FFFFFF',
38990                         plain:true,
38991                         allowReselect: true,
38992                         selectHandler: function(cp, color){
38993                             if(Roo.isGecko){
38994                                 editor.execCmd('useCSS', false);
38995                                 editor.execCmd('hilitecolor', color);
38996                                 editor.execCmd('useCSS', true);
38997                                 editor.deferFocus();
38998                             }else{
38999                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39000                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39001                                 editor.deferFocus();
39002                             }
39003                         },
39004                         scope:editor,
39005                         clickEvent:'mousedown'
39006                     })
39007                 }
39008             );
39009         };
39010         // now add all the items...
39011         
39012
39013         if(!this.disable.alignments){
39014             tb.add(
39015                 '-',
39016                 btn('justifyleft'),
39017                 btn('justifycenter'),
39018                 btn('justifyright')
39019             );
39020         };
39021
39022         //if(!Roo.isSafari){
39023             if(!this.disable.links){
39024                 tb.add(
39025                     '-',
39026                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39027                 );
39028             };
39029
39030             if(!this.disable.lists){
39031                 tb.add(
39032                     '-',
39033                     btn('insertorderedlist'),
39034                     btn('insertunorderedlist')
39035                 );
39036             }
39037             if(!this.disable.sourceEdit){
39038                 tb.add(
39039                     '-',
39040                     btn('sourceedit', true, function(btn){
39041                         this.toggleSourceEdit(btn.pressed);
39042                     })
39043                 );
39044             }
39045         //}
39046         
39047         var smenu = { };
39048         // special menu.. - needs to be tidied up..
39049         if (!this.disable.special) {
39050             smenu = {
39051                 text: "&#169;",
39052                 cls: 'x-edit-none',
39053                 menu : {
39054                     items : []
39055                    }
39056             };
39057             for (var i =0; i < this.specialChars.length; i++) {
39058                 smenu.menu.items.push({
39059                     
39060                     text: this.specialChars[i],
39061                     handler: function(a,b) {
39062                         editor.insertAtCursor(String.fromCharCode(a.text.replace('&#','').replace(';', '')));
39063                     },
39064                     tabIndex:-1
39065                 });
39066             }
39067             
39068             
39069             tb.add(smenu);
39070             
39071             
39072         }
39073         if (this.btns) {
39074             for(var i =0; i< this.btns.length;i++) {
39075                 var b = this.btns[i];
39076                 b.cls =  'x-edit-none';
39077                 b.scope = editor;
39078                 tb.add(b);
39079             }
39080         
39081         }
39082         
39083         
39084         
39085         // disable everything...
39086         
39087         this.tb.items.each(function(item){
39088            if(item.id != editor.frameId+ '-sourceedit'){
39089                 item.disable();
39090             }
39091         });
39092         this.rendered = true;
39093         
39094         // the all the btns;
39095         editor.on('editorevent', this.updateToolbar, this);
39096         // other toolbars need to implement this..
39097         //editor.on('editmodechange', this.updateToolbar, this);
39098     },
39099     
39100     
39101     
39102     /**
39103      * Protected method that will not generally be called directly. It triggers
39104      * a toolbar update by reading the markup state of the current selection in the editor.
39105      */
39106     updateToolbar: function(){
39107
39108         if(!this.editor.activated){
39109             this.editor.onFirstFocus();
39110             return;
39111         }
39112
39113         var btns = this.tb.items.map, 
39114             doc = this.editor.doc,
39115             frameId = this.editor.frameId;
39116
39117         if(!this.disable.font && !Roo.isSafari){
39118             /*
39119             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39120             if(name != this.fontSelect.dom.value){
39121                 this.fontSelect.dom.value = name;
39122             }
39123             */
39124         }
39125         if(!this.disable.format){
39126             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39127             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39128             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39129         }
39130         if(!this.disable.alignments){
39131             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39132             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39133             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39134         }
39135         if(!Roo.isSafari && !this.disable.lists){
39136             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39137             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39138         }
39139         
39140         var ans = this.editor.getAllAncestors();
39141         if (this.formatCombo) {
39142             
39143             
39144             var store = this.formatCombo.store;
39145             this.formatCombo.setValue("");
39146             for (var i =0; i < ans.length;i++) {
39147                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), true).length) {
39148                     // select it..
39149                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39150                     break;
39151                 }
39152             }
39153         }
39154         
39155         
39156         
39157         // hides menus... - so this cant be on a menu...
39158         Roo.menu.MenuMgr.hideAll();
39159
39160         //this.editorsyncValue();
39161     },
39162    
39163     
39164     createFontOptions : function(){
39165         var buf = [], fs = this.fontFamilies, ff, lc;
39166         for(var i = 0, len = fs.length; i< len; i++){
39167             ff = fs[i];
39168             lc = ff.toLowerCase();
39169             buf.push(
39170                 '<option value="',lc,'" style="font-family:',ff,';"',
39171                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39172                     ff,
39173                 '</option>'
39174             );
39175         }
39176         return buf.join('');
39177     },
39178     
39179     toggleSourceEdit : function(sourceEditMode){
39180         if(sourceEditMode === undefined){
39181             sourceEditMode = !this.sourceEditMode;
39182         }
39183         this.sourceEditMode = sourceEditMode === true;
39184         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39185         // just toggle the button?
39186         if(btn.pressed !== this.editor.sourceEditMode){
39187             btn.toggle(this.editor.sourceEditMode);
39188             return;
39189         }
39190         
39191         if(this.sourceEditMode){
39192             this.tb.items.each(function(item){
39193                 if(item.cmd != 'sourceedit'){
39194                     item.disable();
39195                 }
39196             });
39197           
39198         }else{
39199             if(this.initialized){
39200                 this.tb.items.each(function(item){
39201                     item.enable();
39202                 });
39203             }
39204             
39205         }
39206         // tell the editor that it's been pressed..
39207         this.editor.toggleSourceEdit(sourceEditMode);
39208        
39209     },
39210      /**
39211      * Object collection of toolbar tooltips for the buttons in the editor. The key
39212      * is the command id associated with that button and the value is a valid QuickTips object.
39213      * For example:
39214 <pre><code>
39215 {
39216     bold : {
39217         title: 'Bold (Ctrl+B)',
39218         text: 'Make the selected text bold.',
39219         cls: 'x-html-editor-tip'
39220     },
39221     italic : {
39222         title: 'Italic (Ctrl+I)',
39223         text: 'Make the selected text italic.',
39224         cls: 'x-html-editor-tip'
39225     },
39226     ...
39227 </code></pre>
39228     * @type Object
39229      */
39230     buttonTips : {
39231         bold : {
39232             title: 'Bold (Ctrl+B)',
39233             text: 'Make the selected text bold.',
39234             cls: 'x-html-editor-tip'
39235         },
39236         italic : {
39237             title: 'Italic (Ctrl+I)',
39238             text: 'Make the selected text italic.',
39239             cls: 'x-html-editor-tip'
39240         },
39241         underline : {
39242             title: 'Underline (Ctrl+U)',
39243             text: 'Underline the selected text.',
39244             cls: 'x-html-editor-tip'
39245         },
39246         increasefontsize : {
39247             title: 'Grow Text',
39248             text: 'Increase the font size.',
39249             cls: 'x-html-editor-tip'
39250         },
39251         decreasefontsize : {
39252             title: 'Shrink Text',
39253             text: 'Decrease the font size.',
39254             cls: 'x-html-editor-tip'
39255         },
39256         backcolor : {
39257             title: 'Text Highlight Color',
39258             text: 'Change the background color of the selected text.',
39259             cls: 'x-html-editor-tip'
39260         },
39261         forecolor : {
39262             title: 'Font Color',
39263             text: 'Change the color of the selected text.',
39264             cls: 'x-html-editor-tip'
39265         },
39266         justifyleft : {
39267             title: 'Align Text Left',
39268             text: 'Align text to the left.',
39269             cls: 'x-html-editor-tip'
39270         },
39271         justifycenter : {
39272             title: 'Center Text',
39273             text: 'Center text in the editor.',
39274             cls: 'x-html-editor-tip'
39275         },
39276         justifyright : {
39277             title: 'Align Text Right',
39278             text: 'Align text to the right.',
39279             cls: 'x-html-editor-tip'
39280         },
39281         insertunorderedlist : {
39282             title: 'Bullet List',
39283             text: 'Start a bulleted list.',
39284             cls: 'x-html-editor-tip'
39285         },
39286         insertorderedlist : {
39287             title: 'Numbered List',
39288             text: 'Start a numbered list.',
39289             cls: 'x-html-editor-tip'
39290         },
39291         createlink : {
39292             title: 'Hyperlink',
39293             text: 'Make the selected text a hyperlink.',
39294             cls: 'x-html-editor-tip'
39295         },
39296         sourceedit : {
39297             title: 'Source Edit',
39298             text: 'Switch to source editing mode.',
39299             cls: 'x-html-editor-tip'
39300         }
39301     },
39302     // private
39303     onDestroy : function(){
39304         if(this.rendered){
39305             
39306             this.tb.items.each(function(item){
39307                 if(item.menu){
39308                     item.menu.removeAll();
39309                     if(item.menu.el){
39310                         item.menu.el.destroy();
39311                     }
39312                 }
39313                 item.destroy();
39314             });
39315              
39316         }
39317     },
39318     onFirstFocus: function() {
39319         this.tb.items.each(function(item){
39320            item.enable();
39321         });
39322     }
39323 });
39324
39325
39326
39327
39328 // <script type="text/javascript">
39329 /*
39330  * Based on
39331  * Ext JS Library 1.1.1
39332  * Copyright(c) 2006-2007, Ext JS, LLC.
39333  *  
39334  
39335  */
39336
39337  
39338 /**
39339  * @class Roo.form.HtmlEditor.ToolbarContext
39340  * Context Toolbar
39341  * 
39342  * Usage:
39343  *
39344  new Roo.form.HtmlEditor({
39345     ....
39346     toolbars : [
39347         new Roo.form.HtmlEditor.ToolbarStandard(),
39348         new Roo.form.HtmlEditor.ToolbarContext()
39349         })
39350     }
39351      
39352  * 
39353  * @config : {Object} disable List of elements to disable.. (not done yet.)
39354  * 
39355  * 
39356  */
39357
39358 Roo.form.HtmlEditor.ToolbarContext = function(config)
39359 {
39360     
39361     Roo.apply(this, config);
39362     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39363     // dont call parent... till later.
39364 }
39365 Roo.form.HtmlEditor.ToolbarContext.types = {
39366     'IMG' : {
39367         width : {
39368             title: "Width",
39369             width: 40
39370         },
39371         height:  {
39372             title: "Height",
39373             width: 40
39374         },
39375         align: {
39376             title: "Align",
39377             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39378             width : 80
39379             
39380         },
39381         border: {
39382             title: "Border",
39383             width: 40
39384         },
39385         alt: {
39386             title: "Alt",
39387             width: 120
39388         },
39389         src : {
39390             title: "Src",
39391             width: 220
39392         }
39393         
39394     },
39395     'A' : {
39396         name : {
39397             title: "Name",
39398             width: 50
39399         },
39400         href:  {
39401             title: "Href",
39402             width: 220
39403         } // border?
39404         
39405     },
39406     'TABLE' : {
39407         rows : {
39408             title: "Rows",
39409             width: 20
39410         },
39411         cols : {
39412             title: "Cols",
39413             width: 20
39414         },
39415         width : {
39416             title: "Width",
39417             width: 40
39418         },
39419         height : {
39420             title: "Height",
39421             width: 40
39422         },
39423         border : {
39424             title: "Border",
39425             width: 20
39426         }
39427     },
39428     'TD' : {
39429         width : {
39430             title: "Width",
39431             width: 40
39432         },
39433         height : {
39434             title: "Height",
39435             width: 40
39436         },   
39437         align: {
39438             title: "Align",
39439             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
39440             width: 40
39441         },
39442         valign: {
39443             title: "Valign",
39444             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
39445             width: 40
39446         },
39447         colspan: {
39448             title: "Colspan",
39449             width: 20
39450             
39451         }
39452     },
39453     'INPUT' : {
39454         name : {
39455             title: "name",
39456             width: 120
39457         },
39458         value : {
39459             title: "Value",
39460             width: 120
39461         },
39462         width : {
39463             title: "Width",
39464             width: 40
39465         }
39466     },
39467     'LABEL' : {
39468         'for' : {
39469             title: "For",
39470             width: 120
39471         }
39472     },
39473     'TEXTAREA' : {
39474           name : {
39475             title: "name",
39476             width: 120
39477         },
39478         rows : {
39479             title: "Rows",
39480             width: 20
39481         },
39482         cols : {
39483             title: "Cols",
39484             width: 20
39485         }
39486     },
39487     'SELECT' : {
39488         name : {
39489             title: "name",
39490             width: 120
39491         },
39492         selectoptions : {
39493             title: "Options",
39494             width: 200
39495         }
39496     },
39497     'BODY' : {
39498         title : {
39499             title: "title",
39500             width: 120,
39501             disabled : true
39502         }
39503     }
39504 };
39505
39506
39507
39508 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
39509     
39510     tb: false,
39511     
39512     rendered: false,
39513     
39514     editor : false,
39515     /**
39516      * @cfg {Object} disable  List of toolbar elements to disable
39517          
39518      */
39519     disable : false,
39520     
39521     
39522     
39523     toolbars : false,
39524     
39525     init : function(editor)
39526     {
39527         this.editor = editor;
39528         
39529         
39530         var fid = editor.frameId;
39531         var etb = this;
39532         function btn(id, toggle, handler){
39533             var xid = fid + '-'+ id ;
39534             return {
39535                 id : xid,
39536                 cmd : id,
39537                 cls : 'x-btn-icon x-edit-'+id,
39538                 enableToggle:toggle !== false,
39539                 scope: editor, // was editor...
39540                 handler:handler||editor.relayBtnCmd,
39541                 clickEvent:'mousedown',
39542                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39543                 tabIndex:-1
39544             };
39545         }
39546         // create a new element.
39547         var wdiv = editor.wrap.createChild({
39548                 tag: 'div'
39549             }, editor.wrap.dom.firstChild.nextSibling, true);
39550         
39551         // can we do this more than once??
39552         
39553          // stop form submits
39554       
39555  
39556         // disable everything...
39557         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39558         this.toolbars = {};
39559            
39560         for (var i in  ty) {
39561             this.toolbars[i] = this.buildToolbar(ty[i],i);
39562         }
39563         this.tb = this.toolbars.BODY;
39564         this.tb.el.show();
39565         
39566          
39567         this.rendered = true;
39568         
39569         // the all the btns;
39570         editor.on('editorevent', this.updateToolbar, this);
39571         // other toolbars need to implement this..
39572         //editor.on('editmodechange', this.updateToolbar, this);
39573     },
39574     
39575     
39576     
39577     /**
39578      * Protected method that will not generally be called directly. It triggers
39579      * a toolbar update by reading the markup state of the current selection in the editor.
39580      */
39581     updateToolbar: function(){
39582
39583         if(!this.editor.activated){
39584             this.editor.onFirstFocus();
39585             return;
39586         }
39587
39588         
39589         var ans = this.editor.getAllAncestors();
39590         
39591         // pick
39592         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
39593         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
39594         sel = sel ? sel : this.editor.doc.body;
39595         sel = sel.tagName.length ? sel : this.editor.doc.body;
39596         var tn = sel.tagName.toUpperCase();
39597         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
39598         tn = sel.tagName.toUpperCase();
39599         if (this.tb.name  == tn) {
39600             return; // no change
39601         }
39602         this.tb.el.hide();
39603         ///console.log("show: " + tn);
39604         this.tb =  this.toolbars[tn];
39605         this.tb.el.show();
39606         this.tb.fields.each(function(e) {
39607             e.setValue(sel.getAttribute(e.name));
39608         });
39609         this.tb.selectedNode = sel;
39610         
39611         
39612         Roo.menu.MenuMgr.hideAll();
39613
39614         //this.editorsyncValue();
39615     },
39616    
39617        
39618     // private
39619     onDestroy : function(){
39620         if(this.rendered){
39621             
39622             this.tb.items.each(function(item){
39623                 if(item.menu){
39624                     item.menu.removeAll();
39625                     if(item.menu.el){
39626                         item.menu.el.destroy();
39627                     }
39628                 }
39629                 item.destroy();
39630             });
39631              
39632         }
39633     },
39634     onFirstFocus: function() {
39635         // need to do this for all the toolbars..
39636         this.tb.items.each(function(item){
39637            item.enable();
39638         });
39639     },
39640     buildToolbar: function(tlist, nm)
39641     {
39642         var editor = this.editor;
39643          // create a new element.
39644         var wdiv = editor.wrap.createChild({
39645                 tag: 'div'
39646             }, editor.wrap.dom.firstChild.nextSibling, true);
39647         
39648        
39649         var tb = new Roo.Toolbar(wdiv);
39650         tb.add(nm+ ":&nbsp;");
39651         for (var i in tlist) {
39652             var item = tlist[i];
39653             tb.add(item.title + ":&nbsp;");
39654             if (item.opts) {
39655                 // fixme
39656                 
39657               
39658                 tb.addField( new Roo.form.ComboBox({
39659                     store: new Roo.data.SimpleStore({
39660                         id : 'val',
39661                         fields: ['val'],
39662                         data : item.opts // from states.js
39663                     }),
39664                     name : i,
39665                     displayField:'val',
39666                     typeAhead: false,
39667                     mode: 'local',
39668                     editable : false,
39669                     triggerAction: 'all',
39670                     emptyText:'Select',
39671                     selectOnFocus:true,
39672                     width: item.width ? item.width  : 130,
39673                     listeners : {
39674                         'select': function(c, r, i) {
39675                             tb.selectedNode.setAttribute(c.name, r.get('val'));
39676                         }
39677                     }
39678
39679                 }));
39680                 continue;
39681                     
39682                 
39683                 
39684                 
39685                 
39686                 tb.addField( new Roo.form.TextField({
39687                     name: i,
39688                     width: 100,
39689                     //allowBlank:false,
39690                     value: ''
39691                 }));
39692                 continue;
39693             }
39694             tb.addField( new Roo.form.TextField({
39695                 name: i,
39696                 width: item.width,
39697                 //allowBlank:true,
39698                 value: '',
39699                 listeners: {
39700                     'change' : function(f, nv, ov) {
39701                         tb.selectedNode.setAttribute(f.name, nv);
39702                     }
39703                 }
39704             }));
39705              
39706         }
39707         tb.el.on('click', function(e){
39708             e.preventDefault(); // what does this do?
39709         });
39710         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
39711         tb.el.hide();
39712         tb.name = nm;
39713         // dont need to disable them... as they will get hidden
39714         return tb;
39715          
39716         
39717     }
39718     
39719     
39720     
39721     
39722 });
39723
39724
39725
39726
39727
39728 /*
39729  * Based on:
39730  * Ext JS Library 1.1.1
39731  * Copyright(c) 2006-2007, Ext JS, LLC.
39732  *
39733  * Originally Released Under LGPL - original licence link has changed is not relivant.
39734  *
39735  * Fork - LGPL
39736  * <script type="text/javascript">
39737  */
39738  
39739 /**
39740  * @class Roo.form.BasicForm
39741  * @extends Roo.util.Observable
39742  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
39743  * @constructor
39744  * @param {String/HTMLElement/Roo.Element} el The form element or its id
39745  * @param {Object} config Configuration options
39746  */
39747 Roo.form.BasicForm = function(el, config){
39748     this.allItems = [];
39749     this.childForms = [];
39750     Roo.apply(this, config);
39751     /*
39752      * The Roo.form.Field items in this form.
39753      * @type MixedCollection
39754      */
39755      
39756      
39757     this.items = new Roo.util.MixedCollection(false, function(o){
39758         return o.id || (o.id = Roo.id());
39759     });
39760     this.addEvents({
39761         /**
39762          * @event beforeaction
39763          * Fires before any action is performed. Return false to cancel the action.
39764          * @param {Form} this
39765          * @param {Action} action The action to be performed
39766          */
39767         beforeaction: true,
39768         /**
39769          * @event actionfailed
39770          * Fires when an action fails.
39771          * @param {Form} this
39772          * @param {Action} action The action that failed
39773          */
39774         actionfailed : true,
39775         /**
39776          * @event actioncomplete
39777          * Fires when an action is completed.
39778          * @param {Form} this
39779          * @param {Action} action The action that completed
39780          */
39781         actioncomplete : true
39782     });
39783     if(el){
39784         this.initEl(el);
39785     }
39786     Roo.form.BasicForm.superclass.constructor.call(this);
39787 };
39788
39789 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
39790     /**
39791      * @cfg {String} method
39792      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
39793      */
39794     /**
39795      * @cfg {DataReader} reader
39796      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
39797      * This is optional as there is built-in support for processing JSON.
39798      */
39799     /**
39800      * @cfg {DataReader} errorReader
39801      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
39802      * This is completely optional as there is built-in support for processing JSON.
39803      */
39804     /**
39805      * @cfg {String} url
39806      * The URL to use for form actions if one isn't supplied in the action options.
39807      */
39808     /**
39809      * @cfg {Boolean} fileUpload
39810      * Set to true if this form is a file upload.
39811      */
39812     /**
39813      * @cfg {Object} baseParams
39814      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
39815      */
39816     /**
39817      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
39818      */
39819     timeout: 30,
39820
39821     // private
39822     activeAction : null,
39823
39824     /**
39825      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
39826      * or setValues() data instead of when the form was first created.
39827      */
39828     trackResetOnLoad : false,
39829     
39830     
39831     /**
39832      * childForms - used for multi-tab forms
39833      * @type {Array}
39834      */
39835     childForms : false,
39836     
39837     /**
39838      * allItems - full list of fields.
39839      * @type {Array}
39840      */
39841     allItems : false,
39842     
39843     /**
39844      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
39845      * element by passing it or its id or mask the form itself by passing in true.
39846      * @type Mixed
39847      */
39848     waitMsgTarget : undefined,
39849
39850     // private
39851     initEl : function(el){
39852         this.el = Roo.get(el);
39853         this.id = this.el.id || Roo.id();
39854         this.el.on('submit', this.onSubmit, this);
39855         this.el.addClass('x-form');
39856     },
39857
39858     // private
39859     onSubmit : function(e){
39860         e.stopEvent();
39861     },
39862
39863     /**
39864      * Returns true if client-side validation on the form is successful.
39865      * @return Boolean
39866      */
39867     isValid : function(){
39868         var valid = true;
39869         this.items.each(function(f){
39870            if(!f.validate()){
39871                valid = false;
39872            }
39873         });
39874         return valid;
39875     },
39876
39877     /**
39878      * Returns true if any fields in this form have changed since their original load.
39879      * @return Boolean
39880      */
39881     isDirty : function(){
39882         var dirty = false;
39883         this.items.each(function(f){
39884            if(f.isDirty()){
39885                dirty = true;
39886                return false;
39887            }
39888         });
39889         return dirty;
39890     },
39891
39892     /**
39893      * Performs a predefined action (submit or load) or custom actions you define on this form.
39894      * @param {String} actionName The name of the action type
39895      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
39896      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
39897      * accept other config options):
39898      * <pre>
39899 Property          Type             Description
39900 ----------------  ---------------  ----------------------------------------------------------------------------------
39901 url               String           The url for the action (defaults to the form's url)
39902 method            String           The form method to use (defaults to the form's method, or POST if not defined)
39903 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
39904 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
39905                                    validate the form on the client (defaults to false)
39906      * </pre>
39907      * @return {BasicForm} this
39908      */
39909     doAction : function(action, options){
39910         if(typeof action == 'string'){
39911             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
39912         }
39913         if(this.fireEvent('beforeaction', this, action) !== false){
39914             this.beforeAction(action);
39915             action.run.defer(100, action);
39916         }
39917         return this;
39918     },
39919
39920     /**
39921      * Shortcut to do a submit action.
39922      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39923      * @return {BasicForm} this
39924      */
39925     submit : function(options){
39926         this.doAction('submit', options);
39927         return this;
39928     },
39929
39930     /**
39931      * Shortcut to do a load action.
39932      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
39933      * @return {BasicForm} this
39934      */
39935     load : function(options){
39936         this.doAction('load', options);
39937         return this;
39938     },
39939
39940     /**
39941      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
39942      * @param {Record} record The record to edit
39943      * @return {BasicForm} this
39944      */
39945     updateRecord : function(record){
39946         record.beginEdit();
39947         var fs = record.fields;
39948         fs.each(function(f){
39949             var field = this.findField(f.name);
39950             if(field){
39951                 record.set(f.name, field.getValue());
39952             }
39953         }, this);
39954         record.endEdit();
39955         return this;
39956     },
39957
39958     /**
39959      * Loads an Roo.data.Record into this form.
39960      * @param {Record} record The record to load
39961      * @return {BasicForm} this
39962      */
39963     loadRecord : function(record){
39964         this.setValues(record.data);
39965         return this;
39966     },
39967
39968     // private
39969     beforeAction : function(action){
39970         var o = action.options;
39971         if(o.waitMsg){
39972             if(this.waitMsgTarget === true){
39973                 this.el.mask(o.waitMsg, 'x-mask-loading');
39974             }else if(this.waitMsgTarget){
39975                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
39976                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
39977             }else{
39978                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
39979             }
39980         }
39981     },
39982
39983     // private
39984     afterAction : function(action, success){
39985         this.activeAction = null;
39986         var o = action.options;
39987         if(o.waitMsg){
39988             if(this.waitMsgTarget === true){
39989                 this.el.unmask();
39990             }else if(this.waitMsgTarget){
39991                 this.waitMsgTarget.unmask();
39992             }else{
39993                 Roo.MessageBox.updateProgress(1);
39994                 Roo.MessageBox.hide();
39995             }
39996         }
39997         if(success){
39998             if(o.reset){
39999                 this.reset();
40000             }
40001             Roo.callback(o.success, o.scope, [this, action]);
40002             this.fireEvent('actioncomplete', this, action);
40003         }else{
40004             Roo.callback(o.failure, o.scope, [this, action]);
40005             this.fireEvent('actionfailed', this, action);
40006         }
40007     },
40008
40009     /**
40010      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40011      * @param {String} id The value to search for
40012      * @return Field
40013      */
40014     findField : function(id){
40015         var field = this.items.get(id);
40016         if(!field){
40017             this.items.each(function(f){
40018                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40019                     field = f;
40020                     return false;
40021                 }
40022             });
40023         }
40024         return field || null;
40025     },
40026
40027     /**
40028      * Add a secondary form to this one, 
40029      * Used to provide tabbed forms. One form is primary, with hidden values 
40030      * which mirror the elements from the other forms.
40031      * 
40032      * @param {Roo.form.Form} form to add.
40033      * 
40034      */
40035     addForm : function(form)
40036     {
40037        
40038         if (this.childForms.indexOf(form) > -1) {
40039             // already added..
40040             return;
40041         }
40042         this.childForms.push(form);
40043         Roo.each(form.allItems, function (fe) {
40044             
40045             if (this.findField(fe.name)) { // already added..
40046                 return;
40047             }
40048             var add = new Roo.form.Hidden({
40049                 name : fe.name
40050             });
40051             add.render(this.el);
40052             
40053             this.add( add );
40054         }, this);
40055         
40056     },
40057     /**
40058      * Mark fields in this form invalid in bulk.
40059      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40060      * @return {BasicForm} this
40061      */
40062     markInvalid : function(errors){
40063         if(errors instanceof Array){
40064             for(var i = 0, len = errors.length; i < len; i++){
40065                 var fieldError = errors[i];
40066                 var f = this.findField(fieldError.id);
40067                 if(f){
40068                     f.markInvalid(fieldError.msg);
40069                 }
40070             }
40071         }else{
40072             var field, id;
40073             for(id in errors){
40074                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40075                     field.markInvalid(errors[id]);
40076                 }
40077             }
40078         }
40079         Roo.each(this.childForms || [], function (f) {
40080             f.markInvalid(errors);
40081         });
40082         
40083         return this;
40084     },
40085
40086     /**
40087      * Set values for fields in this form in bulk.
40088      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40089      * @return {BasicForm} this
40090      */
40091     setValues : function(values){
40092         if(values instanceof Array){ // array of objects
40093             for(var i = 0, len = values.length; i < len; i++){
40094                 var v = values[i];
40095                 var f = this.findField(v.id);
40096                 if(f){
40097                     f.setValue(v.value);
40098                     if(this.trackResetOnLoad){
40099                         f.originalValue = f.getValue();
40100                     }
40101                 }
40102             }
40103         }else{ // object hash
40104             var field, id;
40105             for(id in values){
40106                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40107                     
40108                     if (field.setFromData && 
40109                         field.valueField && 
40110                         field.displayField &&
40111                         // combos' with local stores can 
40112                         // be queried via setValue()
40113                         // to set their value..
40114                         (field.store && !field.store.isLocal)
40115                         ) {
40116                         // it's a combo
40117                         var sd = { };
40118                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40119                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40120                         field.setFromData(sd);
40121                         
40122                     } else {
40123                         field.setValue(values[id]);
40124                     }
40125                     
40126                     
40127                     if(this.trackResetOnLoad){
40128                         field.originalValue = field.getValue();
40129                     }
40130                 }
40131             }
40132         }
40133          
40134         Roo.each(this.childForms || [], function (f) {
40135             f.setValues(values);
40136         });
40137                 
40138         return this;
40139     },
40140
40141     /**
40142      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40143      * they are returned as an array.
40144      * @param {Boolean} asString
40145      * @return {Object}
40146      */
40147     getValues : function(asString){
40148         if (this.childForms) {
40149             // copy values from the child forms
40150             Roo.each(this.childForms, function (f) {
40151                 this.setValues(f.getValues());
40152             }, this);
40153         }
40154         
40155         
40156         
40157         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40158         if(asString === true){
40159             return fs;
40160         }
40161         return Roo.urlDecode(fs);
40162     },
40163
40164     /**
40165      * Clears all invalid messages in this form.
40166      * @return {BasicForm} this
40167      */
40168     clearInvalid : function(){
40169         this.items.each(function(f){
40170            f.clearInvalid();
40171         });
40172         
40173         Roo.each(this.childForms || [], function (f) {
40174             f.clearInvalid();
40175         });
40176         
40177         
40178         return this;
40179     },
40180
40181     /**
40182      * Resets this form.
40183      * @return {BasicForm} this
40184      */
40185     reset : function(){
40186         this.items.each(function(f){
40187             f.reset();
40188         });
40189         
40190         Roo.each(this.childForms || [], function (f) {
40191             f.reset();
40192         });
40193        
40194         
40195         return this;
40196     },
40197
40198     /**
40199      * Add Roo.form components to this form.
40200      * @param {Field} field1
40201      * @param {Field} field2 (optional)
40202      * @param {Field} etc (optional)
40203      * @return {BasicForm} this
40204      */
40205     add : function(){
40206         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40207         return this;
40208     },
40209
40210
40211     /**
40212      * Removes a field from the items collection (does NOT remove its markup).
40213      * @param {Field} field
40214      * @return {BasicForm} this
40215      */
40216     remove : function(field){
40217         this.items.remove(field);
40218         return this;
40219     },
40220
40221     /**
40222      * Looks at the fields in this form, checks them for an id attribute,
40223      * and calls applyTo on the existing dom element with that id.
40224      * @return {BasicForm} this
40225      */
40226     render : function(){
40227         this.items.each(function(f){
40228             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40229                 f.applyTo(f.id);
40230             }
40231         });
40232         return this;
40233     },
40234
40235     /**
40236      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40237      * @param {Object} values
40238      * @return {BasicForm} this
40239      */
40240     applyToFields : function(o){
40241         this.items.each(function(f){
40242            Roo.apply(f, o);
40243         });
40244         return this;
40245     },
40246
40247     /**
40248      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40249      * @param {Object} values
40250      * @return {BasicForm} this
40251      */
40252     applyIfToFields : function(o){
40253         this.items.each(function(f){
40254            Roo.applyIf(f, o);
40255         });
40256         return this;
40257     }
40258 });
40259
40260 // back compat
40261 Roo.BasicForm = Roo.form.BasicForm;/*
40262  * Based on:
40263  * Ext JS Library 1.1.1
40264  * Copyright(c) 2006-2007, Ext JS, LLC.
40265  *
40266  * Originally Released Under LGPL - original licence link has changed is not relivant.
40267  *
40268  * Fork - LGPL
40269  * <script type="text/javascript">
40270  */
40271
40272 /**
40273  * @class Roo.form.Form
40274  * @extends Roo.form.BasicForm
40275  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40276  * @constructor
40277  * @param {Object} config Configuration options
40278  */
40279 Roo.form.Form = function(config){
40280     var xitems =  [];
40281     if (config.items) {
40282         xitems = config.items;
40283         delete config.items;
40284     }
40285    
40286     
40287     Roo.form.Form.superclass.constructor.call(this, null, config);
40288     this.url = this.url || this.action;
40289     if(!this.root){
40290         this.root = new Roo.form.Layout(Roo.applyIf({
40291             id: Roo.id()
40292         }, config));
40293     }
40294     this.active = this.root;
40295     /**
40296      * Array of all the buttons that have been added to this form via {@link addButton}
40297      * @type Array
40298      */
40299     this.buttons = [];
40300     this.allItems = [];
40301     this.addEvents({
40302         /**
40303          * @event clientvalidation
40304          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40305          * @param {Form} this
40306          * @param {Boolean} valid true if the form has passed client-side validation
40307          */
40308         clientvalidation: true,
40309         /**
40310          * @event rendered
40311          * Fires when the form is rendered
40312          * @param {Roo.form.Form} form
40313          */
40314         rendered : true
40315     });
40316     
40317     Roo.each(xitems, this.addxtype, this);
40318     
40319     
40320     
40321 };
40322
40323 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40324     /**
40325      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40326      */
40327     /**
40328      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40329      */
40330     /**
40331      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40332      */
40333     buttonAlign:'center',
40334
40335     /**
40336      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40337      */
40338     minButtonWidth:75,
40339
40340     /**
40341      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40342      * This property cascades to child containers if not set.
40343      */
40344     labelAlign:'left',
40345
40346     /**
40347      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40348      * fires a looping event with that state. This is required to bind buttons to the valid
40349      * state using the config value formBind:true on the button.
40350      */
40351     monitorValid : false,
40352
40353     /**
40354      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40355      */
40356     monitorPoll : 200,
40357
40358   
40359     /**
40360      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40361      * fields are added and the column is closed. If no fields are passed the column remains open
40362      * until end() is called.
40363      * @param {Object} config The config to pass to the column
40364      * @param {Field} field1 (optional)
40365      * @param {Field} field2 (optional)
40366      * @param {Field} etc (optional)
40367      * @return Column The column container object
40368      */
40369     column : function(c){
40370         var col = new Roo.form.Column(c);
40371         this.start(col);
40372         if(arguments.length > 1){ // duplicate code required because of Opera
40373             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40374             this.end();
40375         }
40376         return col;
40377     },
40378
40379     /**
40380      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40381      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40382      * until end() is called.
40383      * @param {Object} config The config to pass to the fieldset
40384      * @param {Field} field1 (optional)
40385      * @param {Field} field2 (optional)
40386      * @param {Field} etc (optional)
40387      * @return FieldSet The fieldset container object
40388      */
40389     fieldset : function(c){
40390         var fs = new Roo.form.FieldSet(c);
40391         this.start(fs);
40392         if(arguments.length > 1){ // duplicate code required because of Opera
40393             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40394             this.end();
40395         }
40396         return fs;
40397     },
40398
40399     /**
40400      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
40401      * fields are added and the container is closed. If no fields are passed the container remains open
40402      * until end() is called.
40403      * @param {Object} config The config to pass to the Layout
40404      * @param {Field} field1 (optional)
40405      * @param {Field} field2 (optional)
40406      * @param {Field} etc (optional)
40407      * @return Layout The container object
40408      */
40409     container : function(c){
40410         var l = new Roo.form.Layout(c);
40411         this.start(l);
40412         if(arguments.length > 1){ // duplicate code required because of Opera
40413             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40414             this.end();
40415         }
40416         return l;
40417     },
40418
40419     /**
40420      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
40421      * @param {Object} container A Roo.form.Layout or subclass of Layout
40422      * @return {Form} this
40423      */
40424     start : function(c){
40425         // cascade label info
40426         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
40427         this.active.stack.push(c);
40428         c.ownerCt = this.active;
40429         this.active = c;
40430         return this;
40431     },
40432
40433     /**
40434      * Closes the current open container
40435      * @return {Form} this
40436      */
40437     end : function(){
40438         if(this.active == this.root){
40439             return this;
40440         }
40441         this.active = this.active.ownerCt;
40442         return this;
40443     },
40444
40445     /**
40446      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
40447      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
40448      * as the label of the field.
40449      * @param {Field} field1
40450      * @param {Field} field2 (optional)
40451      * @param {Field} etc. (optional)
40452      * @return {Form} this
40453      */
40454     add : function(){
40455         this.active.stack.push.apply(this.active.stack, arguments);
40456         this.allItems.push.apply(this.allItems,arguments);
40457         var r = [];
40458         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
40459             if(a[i].isFormField){
40460                 r.push(a[i]);
40461             }
40462         }
40463         if(r.length > 0){
40464             Roo.form.Form.superclass.add.apply(this, r);
40465         }
40466         return this;
40467     },
40468     
40469
40470     
40471     
40472     
40473      /**
40474      * Find any element that has been added to a form, using it's ID or name
40475      * This can include framesets, columns etc. along with regular fields..
40476      * @param {String} id - id or name to find.
40477      
40478      * @return {Element} e - or false if nothing found.
40479      */
40480     findbyId : function(id)
40481     {
40482         var ret = false;
40483         if (!id) {
40484             return ret;
40485         }
40486         Ext.each(this.allItems, function(f){
40487             if (f.id == id || f.name == id ){
40488                 ret = f;
40489                 return false;
40490             }
40491         });
40492         return ret;
40493     },
40494
40495     
40496     
40497     /**
40498      * Render this form into the passed container. This should only be called once!
40499      * @param {String/HTMLElement/Element} container The element this component should be rendered into
40500      * @return {Form} this
40501      */
40502     render : function(ct){
40503         ct = Roo.get(ct);
40504         var o = this.autoCreate || {
40505             tag: 'form',
40506             method : this.method || 'POST',
40507             id : this.id || Roo.id()
40508         };
40509         this.initEl(ct.createChild(o));
40510
40511         this.root.render(this.el);
40512
40513         this.items.each(function(f){
40514             f.render('x-form-el-'+f.id);
40515         });
40516
40517         if(this.buttons.length > 0){
40518             // tables are required to maintain order and for correct IE layout
40519             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
40520                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
40521                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
40522             }}, null, true);
40523             var tr = tb.getElementsByTagName('tr')[0];
40524             for(var i = 0, len = this.buttons.length; i < len; i++) {
40525                 var b = this.buttons[i];
40526                 var td = document.createElement('td');
40527                 td.className = 'x-form-btn-td';
40528                 b.render(tr.appendChild(td));
40529             }
40530         }
40531         if(this.monitorValid){ // initialize after render
40532             this.startMonitoring();
40533         }
40534         this.fireEvent('rendered', this);
40535         return this;
40536     },
40537
40538     /**
40539      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
40540      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
40541      * object or a valid Roo.DomHelper element config
40542      * @param {Function} handler The function called when the button is clicked
40543      * @param {Object} scope (optional) The scope of the handler function
40544      * @return {Roo.Button}
40545      */
40546     addButton : function(config, handler, scope){
40547         var bc = {
40548             handler: handler,
40549             scope: scope,
40550             minWidth: this.minButtonWidth,
40551             hideParent:true
40552         };
40553         if(typeof config == "string"){
40554             bc.text = config;
40555         }else{
40556             Roo.apply(bc, config);
40557         }
40558         var btn = new Roo.Button(null, bc);
40559         this.buttons.push(btn);
40560         return btn;
40561     },
40562
40563      /**
40564      * Adds a series of form elements (using the xtype property as the factory method.
40565      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
40566      * @param {Object} config 
40567      */
40568     
40569     addxtype : function()
40570     {
40571         var ar = Array.prototype.slice.call(arguments, 0);
40572         var ret = false;
40573         for(var i = 0; i < ar.length; i++) {
40574             if (!ar[i]) {
40575                 continue; // skip -- if this happends something invalid got sent, we 
40576                 // should ignore it, as basically that interface element will not show up
40577                 // and that should be pretty obvious!!
40578             }
40579             
40580             if (Roo.form[ar[i].xtype]) {
40581                 ar[i].form = this;
40582                 var fe = Roo.factory(ar[i], Roo.form);
40583                 if (!ret) {
40584                     ret = fe;
40585                 }
40586                 fe.form = this;
40587                 if (fe.store) {
40588                     fe.store.form = this;
40589                 }
40590                 if (fe.isLayout) {  
40591                          
40592                     this.start(fe);
40593                     this.allItems.push(fe);
40594                     if (fe.items && fe.addxtype) {
40595                         fe.addxtype.apply(fe, fe.items);
40596                         delete fe.items;
40597                     }
40598                      this.end();
40599                     continue;
40600                 }
40601                 
40602                 
40603                  
40604                 this.add(fe);
40605               //  console.log('adding ' + ar[i].xtype);
40606             }
40607             if (ar[i].xtype == 'Button') {  
40608                 //console.log('adding button');
40609                 //console.log(ar[i]);
40610                 this.addButton(ar[i]);
40611                 this.allItems.push(fe);
40612                 continue;
40613             }
40614             
40615             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
40616                 alert('end is not supported on xtype any more, use items');
40617             //    this.end();
40618             //    //console.log('adding end');
40619             }
40620             
40621         }
40622         return ret;
40623     },
40624     
40625     /**
40626      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
40627      * option "monitorValid"
40628      */
40629     startMonitoring : function(){
40630         if(!this.bound){
40631             this.bound = true;
40632             Roo.TaskMgr.start({
40633                 run : this.bindHandler,
40634                 interval : this.monitorPoll || 200,
40635                 scope: this
40636             });
40637         }
40638     },
40639
40640     /**
40641      * Stops monitoring of the valid state of this form
40642      */
40643     stopMonitoring : function(){
40644         this.bound = false;
40645     },
40646
40647     // private
40648     bindHandler : function(){
40649         if(!this.bound){
40650             return false; // stops binding
40651         }
40652         var valid = true;
40653         this.items.each(function(f){
40654             if(!f.isValid(true)){
40655                 valid = false;
40656                 return false;
40657             }
40658         });
40659         for(var i = 0, len = this.buttons.length; i < len; i++){
40660             var btn = this.buttons[i];
40661             if(btn.formBind === true && btn.disabled === valid){
40662                 btn.setDisabled(!valid);
40663             }
40664         }
40665         this.fireEvent('clientvalidation', this, valid);
40666     }
40667     
40668     
40669     
40670     
40671     
40672     
40673     
40674     
40675 });
40676
40677
40678 // back compat
40679 Roo.Form = Roo.form.Form;
40680 /*
40681  * Based on:
40682  * Ext JS Library 1.1.1
40683  * Copyright(c) 2006-2007, Ext JS, LLC.
40684  *
40685  * Originally Released Under LGPL - original licence link has changed is not relivant.
40686  *
40687  * Fork - LGPL
40688  * <script type="text/javascript">
40689  */
40690  
40691  /**
40692  * @class Roo.form.Action
40693  * Internal Class used to handle form actions
40694  * @constructor
40695  * @param {Roo.form.BasicForm} el The form element or its id
40696  * @param {Object} config Configuration options
40697  */
40698  
40699  
40700 // define the action interface
40701 Roo.form.Action = function(form, options){
40702     this.form = form;
40703     this.options = options || {};
40704 };
40705 /**
40706  * Client Validation Failed
40707  * @const 
40708  */
40709 Roo.form.Action.CLIENT_INVALID = 'client';
40710 /**
40711  * Server Validation Failed
40712  * @const 
40713  */
40714  Roo.form.Action.SERVER_INVALID = 'server';
40715  /**
40716  * Connect to Server Failed
40717  * @const 
40718  */
40719 Roo.form.Action.CONNECT_FAILURE = 'connect';
40720 /**
40721  * Reading Data from Server Failed
40722  * @const 
40723  */
40724 Roo.form.Action.LOAD_FAILURE = 'load';
40725
40726 Roo.form.Action.prototype = {
40727     type : 'default',
40728     failureType : undefined,
40729     response : undefined,
40730     result : undefined,
40731
40732     // interface method
40733     run : function(options){
40734
40735     },
40736
40737     // interface method
40738     success : function(response){
40739
40740     },
40741
40742     // interface method
40743     handleResponse : function(response){
40744
40745     },
40746
40747     // default connection failure
40748     failure : function(response){
40749         this.response = response;
40750         this.failureType = Roo.form.Action.CONNECT_FAILURE;
40751         this.form.afterAction(this, false);
40752     },
40753
40754     processResponse : function(response){
40755         this.response = response;
40756         if(!response.responseText){
40757             return true;
40758         }
40759         this.result = this.handleResponse(response);
40760         return this.result;
40761     },
40762
40763     // utility functions used internally
40764     getUrl : function(appendParams){
40765         var url = this.options.url || this.form.url || this.form.el.dom.action;
40766         if(appendParams){
40767             var p = this.getParams();
40768             if(p){
40769                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
40770             }
40771         }
40772         return url;
40773     },
40774
40775     getMethod : function(){
40776         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
40777     },
40778
40779     getParams : function(){
40780         var bp = this.form.baseParams;
40781         var p = this.options.params;
40782         if(p){
40783             if(typeof p == "object"){
40784                 p = Roo.urlEncode(Roo.applyIf(p, bp));
40785             }else if(typeof p == 'string' && bp){
40786                 p += '&' + Roo.urlEncode(bp);
40787             }
40788         }else if(bp){
40789             p = Roo.urlEncode(bp);
40790         }
40791         return p;
40792     },
40793
40794     createCallback : function(){
40795         return {
40796             success: this.success,
40797             failure: this.failure,
40798             scope: this,
40799             timeout: (this.form.timeout*1000),
40800             upload: this.form.fileUpload ? this.success : undefined
40801         };
40802     }
40803 };
40804
40805 Roo.form.Action.Submit = function(form, options){
40806     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
40807 };
40808
40809 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
40810     type : 'submit',
40811
40812     run : function()
40813     {
40814         // run get Values on the form, so it syncs any secondary forms.
40815         this.form.getValues();
40816         
40817         var o = this.options;
40818         var method = this.getMethod();
40819         var isPost = method == 'POST';
40820         if(o.clientValidation === false || this.form.isValid()){
40821             Roo.Ajax.request(Roo.apply(this.createCallback(), {
40822                 form:this.form.el.dom,
40823                 url:this.getUrl(!isPost),
40824                 method: method,
40825                 params:isPost ? this.getParams() : null,
40826                 isUpload: this.form.fileUpload
40827             }));
40828
40829         }else if (o.clientValidation !== false){ // client validation failed
40830             this.failureType = Roo.form.Action.CLIENT_INVALID;
40831             this.form.afterAction(this, false);
40832         }
40833     },
40834
40835     success : function(response){
40836         var result = this.processResponse(response);
40837         if(result === true || result.success){
40838             this.form.afterAction(this, true);
40839             return;
40840         }
40841         if(result.errors){
40842             this.form.markInvalid(result.errors);
40843             this.failureType = Roo.form.Action.SERVER_INVALID;
40844         }
40845         this.form.afterAction(this, false);
40846     },
40847
40848     handleResponse : function(response){
40849         if(this.form.errorReader){
40850             var rs = this.form.errorReader.read(response);
40851             var errors = [];
40852             if(rs.records){
40853                 for(var i = 0, len = rs.records.length; i < len; i++) {
40854                     var r = rs.records[i];
40855                     errors[i] = r.data;
40856                 }
40857             }
40858             if(errors.length < 1){
40859                 errors = null;
40860             }
40861             return {
40862                 success : rs.success,
40863                 errors : errors
40864             };
40865         }
40866         var ret = false;
40867         try {
40868             ret = Roo.decode(response.responseText);
40869         } catch (e) {
40870             ret = {
40871                 success: false,
40872                 errorMsg: "Failed to read server message: " + response.responseText,
40873                 errors : []
40874             };
40875         }
40876         return ret;
40877         
40878     }
40879 });
40880
40881
40882 Roo.form.Action.Load = function(form, options){
40883     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
40884     this.reader = this.form.reader;
40885 };
40886
40887 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
40888     type : 'load',
40889
40890     run : function(){
40891         Roo.Ajax.request(Roo.apply(
40892                 this.createCallback(), {
40893                     method:this.getMethod(),
40894                     url:this.getUrl(false),
40895                     params:this.getParams()
40896         }));
40897     },
40898
40899     success : function(response){
40900         var result = this.processResponse(response);
40901         if(result === true || !result.success || !result.data){
40902             this.failureType = Roo.form.Action.LOAD_FAILURE;
40903             this.form.afterAction(this, false);
40904             return;
40905         }
40906         this.form.clearInvalid();
40907         this.form.setValues(result.data);
40908         this.form.afterAction(this, true);
40909     },
40910
40911     handleResponse : function(response){
40912         if(this.form.reader){
40913             var rs = this.form.reader.read(response);
40914             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
40915             return {
40916                 success : rs.success,
40917                 data : data
40918             };
40919         }
40920         return Roo.decode(response.responseText);
40921     }
40922 });
40923
40924 Roo.form.Action.ACTION_TYPES = {
40925     'load' : Roo.form.Action.Load,
40926     'submit' : Roo.form.Action.Submit
40927 };/*
40928  * Based on:
40929  * Ext JS Library 1.1.1
40930  * Copyright(c) 2006-2007, Ext JS, LLC.
40931  *
40932  * Originally Released Under LGPL - original licence link has changed is not relivant.
40933  *
40934  * Fork - LGPL
40935  * <script type="text/javascript">
40936  */
40937  
40938 /**
40939  * @class Roo.form.Layout
40940  * @extends Roo.Component
40941  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
40942  * @constructor
40943  * @param {Object} config Configuration options
40944  */
40945 Roo.form.Layout = function(config){
40946     var xitems = [];
40947     if (config.items) {
40948         xitems = config.items;
40949         delete config.items;
40950     }
40951     Roo.form.Layout.superclass.constructor.call(this, config);
40952     this.stack = [];
40953     Roo.each(xitems, this.addxtype, this);
40954      
40955 };
40956
40957 Roo.extend(Roo.form.Layout, Roo.Component, {
40958     /**
40959      * @cfg {String/Object} autoCreate
40960      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
40961      */
40962     /**
40963      * @cfg {String/Object/Function} style
40964      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
40965      * a function which returns such a specification.
40966      */
40967     /**
40968      * @cfg {String} labelAlign
40969      * Valid values are "left," "top" and "right" (defaults to "left")
40970      */
40971     /**
40972      * @cfg {Number} labelWidth
40973      * Fixed width in pixels of all field labels (defaults to undefined)
40974      */
40975     /**
40976      * @cfg {Boolean} clear
40977      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
40978      */
40979     clear : true,
40980     /**
40981      * @cfg {String} labelSeparator
40982      * The separator to use after field labels (defaults to ':')
40983      */
40984     labelSeparator : ':',
40985     /**
40986      * @cfg {Boolean} hideLabels
40987      * True to suppress the display of field labels in this layout (defaults to false)
40988      */
40989     hideLabels : false,
40990
40991     // private
40992     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
40993     
40994     isLayout : true,
40995     
40996     // private
40997     onRender : function(ct, position){
40998         if(this.el){ // from markup
40999             this.el = Roo.get(this.el);
41000         }else {  // generate
41001             var cfg = this.getAutoCreate();
41002             this.el = ct.createChild(cfg, position);
41003         }
41004         if(this.style){
41005             this.el.applyStyles(this.style);
41006         }
41007         if(this.labelAlign){
41008             this.el.addClass('x-form-label-'+this.labelAlign);
41009         }
41010         if(this.hideLabels){
41011             this.labelStyle = "display:none";
41012             this.elementStyle = "padding-left:0;";
41013         }else{
41014             if(typeof this.labelWidth == 'number'){
41015                 this.labelStyle = "width:"+this.labelWidth+"px;";
41016                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41017             }
41018             if(this.labelAlign == 'top'){
41019                 this.labelStyle = "width:auto;";
41020                 this.elementStyle = "padding-left:0;";
41021             }
41022         }
41023         var stack = this.stack;
41024         var slen = stack.length;
41025         if(slen > 0){
41026             if(!this.fieldTpl){
41027                 var t = new Roo.Template(
41028                     '<div class="x-form-item {5}">',
41029                         '<label for="{0}" style="{2}">{1}{4}</label>',
41030                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41031                         '</div>',
41032                     '</div><div class="x-form-clear-left"></div>'
41033                 );
41034                 t.disableFormats = true;
41035                 t.compile();
41036                 Roo.form.Layout.prototype.fieldTpl = t;
41037             }
41038             for(var i = 0; i < slen; i++) {
41039                 if(stack[i].isFormField){
41040                     this.renderField(stack[i]);
41041                 }else{
41042                     this.renderComponent(stack[i]);
41043                 }
41044             }
41045         }
41046         if(this.clear){
41047             this.el.createChild({cls:'x-form-clear'});
41048         }
41049     },
41050
41051     // private
41052     renderField : function(f){
41053         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41054                f.id, //0
41055                f.fieldLabel, //1
41056                f.labelStyle||this.labelStyle||'', //2
41057                this.elementStyle||'', //3
41058                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41059                f.itemCls||this.itemCls||''  //5
41060        ], true).getPrevSibling());
41061     },
41062
41063     // private
41064     renderComponent : function(c){
41065         c.render(c.isLayout ? this.el : this.el.createChild());    
41066     },
41067     /**
41068      * Adds a object form elements (using the xtype property as the factory method.)
41069      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41070      * @param {Object} config 
41071      */
41072     addxtype : function(o)
41073     {
41074         // create the lement.
41075         o.form = this.form;
41076         var fe = Roo.factory(o, Roo.form);
41077         this.form.allItems.push(fe);
41078         this.stack.push(fe);
41079         
41080         if (fe.isFormField) {
41081             this.form.items.add(fe);
41082         }
41083          
41084         return fe;
41085     }
41086 });
41087
41088 /**
41089  * @class Roo.form.Column
41090  * @extends Roo.form.Layout
41091  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41092  * @constructor
41093  * @param {Object} config Configuration options
41094  */
41095 Roo.form.Column = function(config){
41096     Roo.form.Column.superclass.constructor.call(this, config);
41097 };
41098
41099 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41100     /**
41101      * @cfg {Number/String} width
41102      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41103      */
41104     /**
41105      * @cfg {String/Object} autoCreate
41106      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41107      */
41108
41109     // private
41110     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41111
41112     // private
41113     onRender : function(ct, position){
41114         Roo.form.Column.superclass.onRender.call(this, ct, position);
41115         if(this.width){
41116             this.el.setWidth(this.width);
41117         }
41118     }
41119 });
41120
41121
41122 /**
41123  * @class Roo.form.Row
41124  * @extends Roo.form.Layout
41125  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41126  * @constructor
41127  * @param {Object} config Configuration options
41128  */
41129
41130  
41131 Roo.form.Row = function(config){
41132     Roo.form.Row.superclass.constructor.call(this, config);
41133 };
41134  
41135 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41136       /**
41137      * @cfg {Number/String} width
41138      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41139      */
41140     /**
41141      * @cfg {Number/String} height
41142      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41143      */
41144     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41145     
41146     padWidth : 20,
41147     // private
41148     onRender : function(ct, position){
41149         //console.log('row render');
41150         if(!this.rowTpl){
41151             var t = new Roo.Template(
41152                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41153                     '<label for="{0}" style="{2}">{1}{4}</label>',
41154                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41155                     '</div>',
41156                 '</div>'
41157             );
41158             t.disableFormats = true;
41159             t.compile();
41160             Roo.form.Layout.prototype.rowTpl = t;
41161         }
41162         this.fieldTpl = this.rowTpl;
41163         
41164         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41165         var labelWidth = 100;
41166         
41167         if ((this.labelAlign != 'top')) {
41168             if (typeof this.labelWidth == 'number') {
41169                 labelWidth = this.labelWidth
41170             }
41171             this.padWidth =  20 + labelWidth;
41172             
41173         }
41174         
41175         Roo.form.Column.superclass.onRender.call(this, ct, position);
41176         if(this.width){
41177             this.el.setWidth(this.width);
41178         }
41179         if(this.height){
41180             this.el.setHeight(this.height);
41181         }
41182     },
41183     
41184     // private
41185     renderField : function(f){
41186         f.fieldEl = this.fieldTpl.append(this.el, [
41187                f.id, f.fieldLabel,
41188                f.labelStyle||this.labelStyle||'',
41189                this.elementStyle||'',
41190                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41191                f.itemCls||this.itemCls||'',
41192                f.width ? f.width + this.padWidth : 160 + this.padWidth
41193        ],true);
41194     }
41195 });
41196  
41197
41198 /**
41199  * @class Roo.form.FieldSet
41200  * @extends Roo.form.Layout
41201  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41202  * @constructor
41203  * @param {Object} config Configuration options
41204  */
41205 Roo.form.FieldSet = function(config){
41206     Roo.form.FieldSet.superclass.constructor.call(this, config);
41207 };
41208
41209 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41210     /**
41211      * @cfg {String} legend
41212      * The text to display as the legend for the FieldSet (defaults to '')
41213      */
41214     /**
41215      * @cfg {String/Object} autoCreate
41216      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41217      */
41218
41219     // private
41220     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41221
41222     // private
41223     onRender : function(ct, position){
41224         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41225         if(this.legend){
41226             this.setLegend(this.legend);
41227         }
41228     },
41229
41230     // private
41231     setLegend : function(text){
41232         if(this.rendered){
41233             this.el.child('legend').update(text);
41234         }
41235     }
41236 });/*
41237  * Based on:
41238  * Ext JS Library 1.1.1
41239  * Copyright(c) 2006-2007, Ext JS, LLC.
41240  *
41241  * Originally Released Under LGPL - original licence link has changed is not relivant.
41242  *
41243  * Fork - LGPL
41244  * <script type="text/javascript">
41245  */
41246 /**
41247  * @class Roo.form.VTypes
41248  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41249  * @singleton
41250  */
41251 Roo.form.VTypes = function(){
41252     // closure these in so they are only created once.
41253     var alpha = /^[a-zA-Z_]+$/;
41254     var alphanum = /^[a-zA-Z0-9_]+$/;
41255     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41256     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41257
41258     // All these messages and functions are configurable
41259     return {
41260         /**
41261          * The function used to validate email addresses
41262          * @param {String} value The email address
41263          */
41264         'email' : function(v){
41265             return email.test(v);
41266         },
41267         /**
41268          * The error text to display when the email validation function returns false
41269          * @type String
41270          */
41271         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41272         /**
41273          * The keystroke filter mask to be applied on email input
41274          * @type RegExp
41275          */
41276         'emailMask' : /[a-z0-9_\.\-@]/i,
41277
41278         /**
41279          * The function used to validate URLs
41280          * @param {String} value The URL
41281          */
41282         'url' : function(v){
41283             return url.test(v);
41284         },
41285         /**
41286          * The error text to display when the url validation function returns false
41287          * @type String
41288          */
41289         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41290         
41291         /**
41292          * The function used to validate alpha values
41293          * @param {String} value The value
41294          */
41295         'alpha' : function(v){
41296             return alpha.test(v);
41297         },
41298         /**
41299          * The error text to display when the alpha validation function returns false
41300          * @type String
41301          */
41302         'alphaText' : 'This field should only contain letters and _',
41303         /**
41304          * The keystroke filter mask to be applied on alpha input
41305          * @type RegExp
41306          */
41307         'alphaMask' : /[a-z_]/i,
41308
41309         /**
41310          * The function used to validate alphanumeric values
41311          * @param {String} value The value
41312          */
41313         'alphanum' : function(v){
41314             return alphanum.test(v);
41315         },
41316         /**
41317          * The error text to display when the alphanumeric validation function returns false
41318          * @type String
41319          */
41320         'alphanumText' : 'This field should only contain letters, numbers and _',
41321         /**
41322          * The keystroke filter mask to be applied on alphanumeric input
41323          * @type RegExp
41324          */
41325         'alphanumMask' : /[a-z0-9_]/i
41326     };
41327 }();//<script type="text/javascript">
41328
41329 /**
41330  * @class Roo.form.FCKeditor
41331  * @extends Roo.form.TextArea
41332  * Wrapper around the FCKEditor http://www.fckeditor.net
41333  * @constructor
41334  * Creates a new FCKeditor
41335  * @param {Object} config Configuration options
41336  */
41337 Roo.form.FCKeditor = function(config){
41338     Roo.form.FCKeditor.superclass.constructor.call(this, config);
41339     this.addEvents({
41340          /**
41341          * @event editorinit
41342          * Fired when the editor is initialized - you can add extra handlers here..
41343          * @param {FCKeditor} this
41344          * @param {Object} the FCK object.
41345          */
41346         editorinit : true
41347     });
41348     
41349     
41350 };
41351 Roo.form.FCKeditor.editors = { };
41352 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
41353 {
41354     //defaultAutoCreate : {
41355     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
41356     //},
41357     // private
41358     /**
41359      * @cfg {Object} fck options - see fck manual for details.
41360      */
41361     fckconfig : false,
41362     
41363     /**
41364      * @cfg {Object} fck toolbar set (Basic or Default)
41365      */
41366     toolbarSet : 'Basic',
41367     /**
41368      * @cfg {Object} fck BasePath
41369      */ 
41370     basePath : '/fckeditor/',
41371     
41372     
41373     frame : false,
41374     
41375     value : '',
41376     
41377    
41378     onRender : function(ct, position)
41379     {
41380         if(!this.el){
41381             this.defaultAutoCreate = {
41382                 tag: "textarea",
41383                 style:"width:300px;height:60px;",
41384                 autocomplete: "off"
41385             };
41386         }
41387         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
41388         /*
41389         if(this.grow){
41390             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
41391             if(this.preventScrollbars){
41392                 this.el.setStyle("overflow", "hidden");
41393             }
41394             this.el.setHeight(this.growMin);
41395         }
41396         */
41397         //console.log('onrender' + this.getId() );
41398         Roo.form.FCKeditor.editors[this.getId()] = this;
41399          
41400
41401         this.replaceTextarea() ;
41402         
41403     },
41404     
41405     getEditor : function() {
41406         return this.fckEditor;
41407     },
41408     /**
41409      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
41410      * @param {Mixed} value The value to set
41411      */
41412     
41413     
41414     setValue : function(value)
41415     {
41416         //console.log('setValue: ' + value);
41417         
41418         if(typeof(value) == 'undefined') { // not sure why this is happending...
41419             return;
41420         }
41421         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41422         
41423         //if(!this.el || !this.getEditor()) {
41424         //    this.value = value;
41425             //this.setValue.defer(100,this,[value]);    
41426         //    return;
41427         //} 
41428         
41429         if(!this.getEditor()) {
41430             return;
41431         }
41432         
41433         this.getEditor().SetData(value);
41434         
41435         //
41436
41437     },
41438
41439     /**
41440      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
41441      * @return {Mixed} value The field value
41442      */
41443     getValue : function()
41444     {
41445         
41446         if (this.frame && this.frame.dom.style.display == 'none') {
41447             return Roo.form.FCKeditor.superclass.getValue.call(this);
41448         }
41449         
41450         if(!this.el || !this.getEditor()) {
41451            
41452            // this.getValue.defer(100,this); 
41453             return this.value;
41454         }
41455        
41456         
41457         var value=this.getEditor().GetData();
41458         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
41459         return Roo.form.FCKeditor.superclass.getValue.call(this);
41460         
41461
41462     },
41463
41464     /**
41465      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
41466      * @return {Mixed} value The field value
41467      */
41468     getRawValue : function()
41469     {
41470         if (this.frame && this.frame.dom.style.display == 'none') {
41471             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41472         }
41473         
41474         if(!this.el || !this.getEditor()) {
41475             //this.getRawValue.defer(100,this); 
41476             return this.value;
41477             return;
41478         }
41479         
41480         
41481         
41482         var value=this.getEditor().GetData();
41483         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
41484         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
41485          
41486     },
41487     
41488     setSize : function(w,h) {
41489         
41490         
41491         
41492         //if (this.frame && this.frame.dom.style.display == 'none') {
41493         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41494         //    return;
41495         //}
41496         //if(!this.el || !this.getEditor()) {
41497         //    this.setSize.defer(100,this, [w,h]); 
41498         //    return;
41499         //}
41500         
41501         
41502         
41503         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
41504         
41505         this.frame.dom.setAttribute('width', w);
41506         this.frame.dom.setAttribute('height', h);
41507         this.frame.setSize(w,h);
41508         
41509     },
41510     
41511     toggleSourceEdit : function(value) {
41512         
41513       
41514          
41515         this.el.dom.style.display = value ? '' : 'none';
41516         this.frame.dom.style.display = value ?  'none' : '';
41517         
41518     },
41519     
41520     
41521     focus: function(tag)
41522     {
41523         if (this.frame.dom.style.display == 'none') {
41524             return Roo.form.FCKeditor.superclass.focus.call(this);
41525         }
41526         if(!this.el || !this.getEditor()) {
41527             this.focus.defer(100,this, [tag]); 
41528             return;
41529         }
41530         
41531         
41532         
41533         
41534         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
41535         this.getEditor().Focus();
41536         if (tgs.length) {
41537             if (!this.getEditor().Selection.GetSelection()) {
41538                 this.focus.defer(100,this, [tag]); 
41539                 return;
41540             }
41541             
41542             
41543             var r = this.getEditor().EditorDocument.createRange();
41544             r.setStart(tgs[0],0);
41545             r.setEnd(tgs[0],0);
41546             this.getEditor().Selection.GetSelection().removeAllRanges();
41547             this.getEditor().Selection.GetSelection().addRange(r);
41548             this.getEditor().Focus();
41549         }
41550         
41551     },
41552     
41553     
41554     
41555     replaceTextarea : function()
41556     {
41557         if ( document.getElementById( this.getId() + '___Frame' ) )
41558             return ;
41559         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
41560         //{
41561             // We must check the elements firstly using the Id and then the name.
41562         var oTextarea = document.getElementById( this.getId() );
41563         
41564         var colElementsByName = document.getElementsByName( this.getId() ) ;
41565          
41566         oTextarea.style.display = 'none' ;
41567
41568         if ( oTextarea.tabIndex ) {            
41569             this.TabIndex = oTextarea.tabIndex ;
41570         }
41571         
41572         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
41573         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
41574         this.frame = Roo.get(this.getId() + '___Frame')
41575     },
41576     
41577     _getConfigHtml : function()
41578     {
41579         var sConfig = '' ;
41580
41581         for ( var o in this.fckconfig ) {
41582             sConfig += sConfig.length > 0  ? '&amp;' : '';
41583             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
41584         }
41585
41586         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
41587     },
41588     
41589     
41590     _getIFrameHtml : function()
41591     {
41592         var sFile = 'fckeditor.html' ;
41593         /* no idea what this is about..
41594         try
41595         {
41596             if ( (/fcksource=true/i).test( window.top.location.search ) )
41597                 sFile = 'fckeditor.original.html' ;
41598         }
41599         catch (e) { 
41600         */
41601
41602         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
41603         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
41604         
41605         
41606         var html = '<iframe id="' + this.getId() +
41607             '___Frame" src="' + sLink +
41608             '" width="' + this.width +
41609             '" height="' + this.height + '"' +
41610             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
41611             ' frameborder="0" scrolling="no"></iframe>' ;
41612
41613         return html ;
41614     },
41615     
41616     _insertHtmlBefore : function( html, element )
41617     {
41618         if ( element.insertAdjacentHTML )       {
41619             // IE
41620             element.insertAdjacentHTML( 'beforeBegin', html ) ;
41621         } else { // Gecko
41622             var oRange = document.createRange() ;
41623             oRange.setStartBefore( element ) ;
41624             var oFragment = oRange.createContextualFragment( html );
41625             element.parentNode.insertBefore( oFragment, element ) ;
41626         }
41627     }
41628     
41629     
41630   
41631     
41632     
41633     
41634     
41635
41636 });
41637
41638 //Roo.reg('fckeditor', Roo.form.FCKeditor);
41639
41640 function FCKeditor_OnComplete(editorInstance){
41641     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
41642     f.fckEditor = editorInstance;
41643     //console.log("loaded");
41644     f.fireEvent('editorinit', f, editorInstance);
41645
41646   
41647
41648  
41649
41650
41651
41652
41653
41654
41655
41656
41657
41658
41659
41660
41661
41662
41663
41664 //<script type="text/javascript">
41665 /**
41666  * @class Roo.form.GridField
41667  * @extends Roo.form.Field
41668  * Embed a grid (or editable grid into a form)
41669  * STATUS ALPHA
41670  * @constructor
41671  * Creates a new GridField
41672  * @param {Object} config Configuration options
41673  */
41674 Roo.form.GridField = function(config){
41675     Roo.form.GridField.superclass.constructor.call(this, config);
41676      
41677 };
41678
41679 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
41680     /**
41681      * @cfg {Number} width  - used to restrict width of grid..
41682      */
41683     width : 100,
41684     /**
41685      * @cfg {Number} height - used to restrict height of grid..
41686      */
41687     height : 50,
41688      /**
41689      * @cfg {Object} xgrid (xtype'd description of grid) Grid or EditorGrid
41690      */
41691     xgrid : false, 
41692     /**
41693      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41694      * {tag: "input", type: "checkbox", autocomplete: "off"})
41695      */
41696    // defaultAutoCreate : { tag: 'div' },
41697     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
41698     /**
41699      * @cfg {String} addTitle Text to include for adding a title.
41700      */
41701     addTitle : false,
41702     //
41703     onResize : function(){
41704         Roo.form.Field.superclass.onResize.apply(this, arguments);
41705     },
41706
41707     initEvents : function(){
41708         // Roo.form.Checkbox.superclass.initEvents.call(this);
41709         // has no events...
41710        
41711     },
41712
41713
41714     getResizeEl : function(){
41715         return this.wrap;
41716     },
41717
41718     getPositionEl : function(){
41719         return this.wrap;
41720     },
41721
41722     // private
41723     onRender : function(ct, position){
41724         
41725         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
41726         var style = this.style;
41727         delete this.style;
41728         
41729         Roo.form.DisplayImage.superclass.onRender.call(this, ct, position);
41730         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
41731         this.viewEl = this.wrap.createChild({ tag: 'div' });
41732         if (style) {
41733             this.viewEl.applyStyles(style);
41734         }
41735         if (this.width) {
41736             this.viewEl.setWidth(this.width);
41737         }
41738         if (this.height) {
41739             this.viewEl.setHeight(this.height);
41740         }
41741         //if(this.inputValue !== undefined){
41742         //this.setValue(this.value);
41743         
41744         
41745         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
41746         
41747         
41748         this.grid.render();
41749         this.grid.getDataSource().on('remove', this.refreshValue, this);
41750         this.grid.getDataSource().on('update', this.refreshValue, this);
41751         this.grid.on('afteredit', this.refreshValue, this);
41752  
41753     },
41754      
41755     
41756     /**
41757      * Sets the value of the item. 
41758      * @param {String} either an object  or a string..
41759      */
41760     setValue : function(v){
41761         //this.value = v;
41762         v = v || []; // empty set..
41763         // this does not seem smart - it really only affects memoryproxy grids..
41764         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
41765             var ds = this.grid.getDataSource();
41766             // assumes a json reader..
41767             var data = {}
41768             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
41769             ds.loadData( data);
41770         }
41771         Roo.form.GridField.superclass.setValue.call(this, v);
41772         this.refreshValue();
41773         // should load data in the grid really....
41774     },
41775     
41776     // private
41777     refreshValue: function() {
41778          var val = [];
41779         this.grid.getDataSource().each(function(r) {
41780             val.push(r.data);
41781         });
41782         this.el.dom.value = Roo.encode(val);
41783     }
41784     
41785      
41786     
41787     
41788 });//<script type="text/javasscript">
41789  
41790
41791 /**
41792  * @class Roo.DDView
41793  * A DnD enabled version of Roo.View.
41794  * @param {Element/String} container The Element in which to create the View.
41795  * @param {String} tpl The template string used to create the markup for each element of the View
41796  * @param {Object} config The configuration properties. These include all the config options of
41797  * {@link Roo.View} plus some specific to this class.<br>
41798  * <p>
41799  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
41800  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
41801  * <p>
41802  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
41803 .x-view-drag-insert-above {
41804         border-top:1px dotted #3366cc;
41805 }
41806 .x-view-drag-insert-below {
41807         border-bottom:1px dotted #3366cc;
41808 }
41809 </code></pre>
41810  * 
41811  */
41812  
41813 Roo.DDView = function(container, tpl, config) {
41814     Roo.DDView.superclass.constructor.apply(this, arguments);
41815     this.getEl().setStyle("outline", "0px none");
41816     this.getEl().unselectable();
41817     if (this.dragGroup) {
41818                 this.setDraggable(this.dragGroup.split(","));
41819     }
41820     if (this.dropGroup) {
41821                 this.setDroppable(this.dropGroup.split(","));
41822     }
41823     if (this.deletable) {
41824         this.setDeletable();
41825     }
41826     this.isDirtyFlag = false;
41827         this.addEvents({
41828                 "drop" : true
41829         });
41830 };
41831
41832 Roo.extend(Roo.DDView, Roo.View, {
41833 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
41834 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
41835 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
41836 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
41837
41838         isFormField: true,
41839
41840         reset: Roo.emptyFn,
41841         
41842         clearInvalid: Roo.form.Field.prototype.clearInvalid,
41843
41844         validate: function() {
41845                 return true;
41846         },
41847         
41848         destroy: function() {
41849                 this.purgeListeners();
41850                 this.getEl.removeAllListeners();
41851                 this.getEl().remove();
41852                 if (this.dragZone) {
41853                         if (this.dragZone.destroy) {
41854                                 this.dragZone.destroy();
41855                         }
41856                 }
41857                 if (this.dropZone) {
41858                         if (this.dropZone.destroy) {
41859                                 this.dropZone.destroy();
41860                         }
41861                 }
41862         },
41863
41864 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
41865         getName: function() {
41866                 return this.name;
41867         },
41868
41869 /**     Loads the View from a JSON string representing the Records to put into the Store. */
41870         setValue: function(v) {
41871                 if (!this.store) {
41872                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
41873                 }
41874                 var data = {};
41875                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
41876                 this.store.proxy = new Roo.data.MemoryProxy(data);
41877                 this.store.load();
41878         },
41879
41880 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
41881         getValue: function() {
41882                 var result = '(';
41883                 this.store.each(function(rec) {
41884                         result += rec.id + ',';
41885                 });
41886                 return result.substr(0, result.length - 1) + ')';
41887         },
41888         
41889         getIds: function() {
41890                 var i = 0, result = new Array(this.store.getCount());
41891                 this.store.each(function(rec) {
41892                         result[i++] = rec.id;
41893                 });
41894                 return result;
41895         },
41896         
41897         isDirty: function() {
41898                 return this.isDirtyFlag;
41899         },
41900
41901 /**
41902  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
41903  *      whole Element becomes the target, and this causes the drop gesture to append.
41904  */
41905     getTargetFromEvent : function(e) {
41906                 var target = e.getTarget();
41907                 while ((target !== null) && (target.parentNode != this.el.dom)) {
41908                 target = target.parentNode;
41909                 }
41910                 if (!target) {
41911                         target = this.el.dom.lastChild || this.el.dom;
41912                 }
41913                 return target;
41914     },
41915
41916 /**
41917  *      Create the drag data which consists of an object which has the property "ddel" as
41918  *      the drag proxy element. 
41919  */
41920     getDragData : function(e) {
41921         var target = this.findItemFromChild(e.getTarget());
41922                 if(target) {
41923                         this.handleSelection(e);
41924                         var selNodes = this.getSelectedNodes();
41925             var dragData = {
41926                 source: this,
41927                 copy: this.copy || (this.allowCopy && e.ctrlKey),
41928                 nodes: selNodes,
41929                 records: []
41930                         };
41931                         var selectedIndices = this.getSelectedIndexes();
41932                         for (var i = 0; i < selectedIndices.length; i++) {
41933                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
41934                         }
41935                         if (selNodes.length == 1) {
41936                                 dragData.ddel = target.cloneNode(true); // the div element
41937                         } else {
41938                                 var div = document.createElement('div'); // create the multi element drag "ghost"
41939                                 div.className = 'multi-proxy';
41940                                 for (var i = 0, len = selNodes.length; i < len; i++) {
41941                                         div.appendChild(selNodes[i].cloneNode(true));
41942                                 }
41943                                 dragData.ddel = div;
41944                         }
41945             //console.log(dragData)
41946             //console.log(dragData.ddel.innerHTML)
41947                         return dragData;
41948                 }
41949         //console.log('nodragData')
41950                 return false;
41951     },
41952     
41953 /**     Specify to which ddGroup items in this DDView may be dragged. */
41954     setDraggable: function(ddGroup) {
41955         if (ddGroup instanceof Array) {
41956                 Roo.each(ddGroup, this.setDraggable, this);
41957                 return;
41958         }
41959         if (this.dragZone) {
41960                 this.dragZone.addToGroup(ddGroup);
41961         } else {
41962                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
41963                                 containerScroll: true,
41964                                 ddGroup: ddGroup 
41965
41966                         });
41967 //                      Draggability implies selection. DragZone's mousedown selects the element.
41968                         if (!this.multiSelect) { this.singleSelect = true; }
41969
41970 //                      Wire the DragZone's handlers up to methods in *this*
41971                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
41972                 }
41973     },
41974
41975 /**     Specify from which ddGroup this DDView accepts drops. */
41976     setDroppable: function(ddGroup) {
41977         if (ddGroup instanceof Array) {
41978                 Roo.each(ddGroup, this.setDroppable, this);
41979                 return;
41980         }
41981         if (this.dropZone) {
41982                 this.dropZone.addToGroup(ddGroup);
41983         } else {
41984                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
41985                                 containerScroll: true,
41986                                 ddGroup: ddGroup
41987                         });
41988
41989 //                      Wire the DropZone's handlers up to methods in *this*
41990                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
41991                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
41992                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
41993                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
41994                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
41995                 }
41996     },
41997
41998 /**     Decide whether to drop above or below a View node. */
41999     getDropPoint : function(e, n, dd){
42000         if (n == this.el.dom) { return "above"; }
42001                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42002                 var c = t + (b - t) / 2;
42003                 var y = Roo.lib.Event.getPageY(e);
42004                 if(y <= c) {
42005                         return "above";
42006                 }else{
42007                         return "below";
42008                 }
42009     },
42010
42011     onNodeEnter : function(n, dd, e, data){
42012                 return false;
42013     },
42014     
42015     onNodeOver : function(n, dd, e, data){
42016                 var pt = this.getDropPoint(e, n, dd);
42017                 // set the insert point style on the target node
42018                 var dragElClass = this.dropNotAllowed;
42019                 if (pt) {
42020                         var targetElClass;
42021                         if (pt == "above"){
42022                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42023                                 targetElClass = "x-view-drag-insert-above";
42024                         } else {
42025                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42026                                 targetElClass = "x-view-drag-insert-below";
42027                         }
42028                         if (this.lastInsertClass != targetElClass){
42029                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42030                                 this.lastInsertClass = targetElClass;
42031                         }
42032                 }
42033                 return dragElClass;
42034         },
42035
42036     onNodeOut : function(n, dd, e, data){
42037                 this.removeDropIndicators(n);
42038     },
42039
42040     onNodeDrop : function(n, dd, e, data){
42041         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42042                 return false;
42043         }
42044         var pt = this.getDropPoint(e, n, dd);
42045                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42046                 if (pt == "below") { insertAt++; }
42047                 for (var i = 0; i < data.records.length; i++) {
42048                         var r = data.records[i];
42049                         var dup = this.store.getById(r.id);
42050                         if (dup && (dd != this.dragZone)) {
42051                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42052                         } else {
42053                                 if (data.copy) {
42054                                         this.store.insert(insertAt++, r.copy());
42055                                 } else {
42056                                         data.source.isDirtyFlag = true;
42057                                         r.store.remove(r);
42058                                         this.store.insert(insertAt++, r);
42059                                 }
42060                                 this.isDirtyFlag = true;
42061                         }
42062                 }
42063                 this.dragZone.cachedTarget = null;
42064                 return true;
42065     },
42066
42067     removeDropIndicators : function(n){
42068                 if(n){
42069                         Roo.fly(n).removeClass([
42070                                 "x-view-drag-insert-above",
42071                                 "x-view-drag-insert-below"]);
42072                         this.lastInsertClass = "_noclass";
42073                 }
42074     },
42075
42076 /**
42077  *      Utility method. Add a delete option to the DDView's context menu.
42078  *      @param {String} imageUrl The URL of the "delete" icon image.
42079  */
42080         setDeletable: function(imageUrl) {
42081                 if (!this.singleSelect && !this.multiSelect) {
42082                         this.singleSelect = true;
42083                 }
42084                 var c = this.getContextMenu();
42085                 this.contextMenu.on("itemclick", function(item) {
42086                         switch (item.id) {
42087                                 case "delete":
42088                                         this.remove(this.getSelectedIndexes());
42089                                         break;
42090                         }
42091                 }, this);
42092                 this.contextMenu.add({
42093                         icon: imageUrl,
42094                         id: "delete",
42095                         text: 'Delete'
42096                 });
42097         },
42098         
42099 /**     Return the context menu for this DDView. */
42100         getContextMenu: function() {
42101                 if (!this.contextMenu) {
42102 //                      Create the View's context menu
42103                         this.contextMenu = new Roo.menu.Menu({
42104                                 id: this.id + "-contextmenu"
42105                         });
42106                         this.el.on("contextmenu", this.showContextMenu, this);
42107                 }
42108                 return this.contextMenu;
42109         },
42110         
42111         disableContextMenu: function() {
42112                 if (this.contextMenu) {
42113                         this.el.un("contextmenu", this.showContextMenu, this);
42114                 }
42115         },
42116
42117         showContextMenu: function(e, item) {
42118         item = this.findItemFromChild(e.getTarget());
42119                 if (item) {
42120                         e.stopEvent();
42121                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42122                         this.contextMenu.showAt(e.getXY());
42123             }
42124     },
42125
42126 /**
42127  *      Remove {@link Roo.data.Record}s at the specified indices.
42128  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42129  */
42130     remove: function(selectedIndices) {
42131                 selectedIndices = [].concat(selectedIndices);
42132                 for (var i = 0; i < selectedIndices.length; i++) {
42133                         var rec = this.store.getAt(selectedIndices[i]);
42134                         this.store.remove(rec);
42135                 }
42136     },
42137
42138 /**
42139  *      Double click fires the event, but also, if this is draggable, and there is only one other
42140  *      related DropZone, it transfers the selected node.
42141  */
42142     onDblClick : function(e){
42143         var item = this.findItemFromChild(e.getTarget());
42144         if(item){
42145             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42146                 return false;
42147             }
42148             if (this.dragGroup) {
42149                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42150                     while (targets.indexOf(this.dropZone) > -1) {
42151                             targets.remove(this.dropZone);
42152                                 }
42153                     if (targets.length == 1) {
42154                                         this.dragZone.cachedTarget = null;
42155                         var el = Roo.get(targets[0].getEl());
42156                         var box = el.getBox(true);
42157                         targets[0].onNodeDrop(el.dom, {
42158                                 target: el.dom,
42159                                 xy: [box.x, box.y + box.height - 1]
42160                         }, null, this.getDragData(e));
42161                     }
42162                 }
42163         }
42164     },
42165     
42166     handleSelection: function(e) {
42167                 this.dragZone.cachedTarget = null;
42168         var item = this.findItemFromChild(e.getTarget());
42169         if (!item) {
42170                 this.clearSelections(true);
42171                 return;
42172         }
42173                 if (item && (this.multiSelect || this.singleSelect)){
42174                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
42175                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
42176                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
42177                                 this.unselect(item);
42178                         } else {
42179                                 this.select(item, this.multiSelect && e.ctrlKey);
42180                                 this.lastSelection = item;
42181                         }
42182                 }
42183     },
42184
42185     onItemClick : function(item, index, e){
42186                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
42187                         return false;
42188                 }
42189                 return true;
42190     },
42191
42192     unselect : function(nodeInfo, suppressEvent){
42193                 var node = this.getNode(nodeInfo);
42194                 if(node && this.isSelected(node)){
42195                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
42196                                 Roo.fly(node).removeClass(this.selectedClass);
42197                                 this.selections.remove(node);
42198                                 if(!suppressEvent){
42199                                         this.fireEvent("selectionchange", this, this.selections);
42200                                 }
42201                         }
42202                 }
42203     }
42204 });
42205 /*
42206  * Based on:
42207  * Ext JS Library 1.1.1
42208  * Copyright(c) 2006-2007, Ext JS, LLC.
42209  *
42210  * Originally Released Under LGPL - original licence link has changed is not relivant.
42211  *
42212  * Fork - LGPL
42213  * <script type="text/javascript">
42214  */
42215  
42216 /**
42217  * @class Roo.LayoutManager
42218  * @extends Roo.util.Observable
42219  * Base class for layout managers.
42220  */
42221 Roo.LayoutManager = function(container, config){
42222     Roo.LayoutManager.superclass.constructor.call(this);
42223     this.el = Roo.get(container);
42224     // ie scrollbar fix
42225     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
42226         document.body.scroll = "no";
42227     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
42228         this.el.position('relative');
42229     }
42230     this.id = this.el.id;
42231     this.el.addClass("x-layout-container");
42232     /** false to disable window resize monitoring @type Boolean */
42233     this.monitorWindowResize = true;
42234     this.regions = {};
42235     this.addEvents({
42236         /**
42237          * @event layout
42238          * Fires when a layout is performed. 
42239          * @param {Roo.LayoutManager} this
42240          */
42241         "layout" : true,
42242         /**
42243          * @event regionresized
42244          * Fires when the user resizes a region. 
42245          * @param {Roo.LayoutRegion} region The resized region
42246          * @param {Number} newSize The new size (width for east/west, height for north/south)
42247          */
42248         "regionresized" : true,
42249         /**
42250          * @event regioncollapsed
42251          * Fires when a region is collapsed. 
42252          * @param {Roo.LayoutRegion} region The collapsed region
42253          */
42254         "regioncollapsed" : true,
42255         /**
42256          * @event regionexpanded
42257          * Fires when a region is expanded.  
42258          * @param {Roo.LayoutRegion} region The expanded region
42259          */
42260         "regionexpanded" : true
42261     });
42262     this.updating = false;
42263     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
42264 };
42265
42266 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
42267     /**
42268      * Returns true if this layout is currently being updated
42269      * @return {Boolean}
42270      */
42271     isUpdating : function(){
42272         return this.updating; 
42273     },
42274     
42275     /**
42276      * Suspend the LayoutManager from doing auto-layouts while
42277      * making multiple add or remove calls
42278      */
42279     beginUpdate : function(){
42280         this.updating = true;    
42281     },
42282     
42283     /**
42284      * Restore auto-layouts and optionally disable the manager from performing a layout
42285      * @param {Boolean} noLayout true to disable a layout update 
42286      */
42287     endUpdate : function(noLayout){
42288         this.updating = false;
42289         if(!noLayout){
42290             this.layout();
42291         }    
42292     },
42293     
42294     layout: function(){
42295         
42296     },
42297     
42298     onRegionResized : function(region, newSize){
42299         this.fireEvent("regionresized", region, newSize);
42300         this.layout();
42301     },
42302     
42303     onRegionCollapsed : function(region){
42304         this.fireEvent("regioncollapsed", region);
42305     },
42306     
42307     onRegionExpanded : function(region){
42308         this.fireEvent("regionexpanded", region);
42309     },
42310         
42311     /**
42312      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
42313      * performs box-model adjustments.
42314      * @return {Object} The size as an object {width: (the width), height: (the height)}
42315      */
42316     getViewSize : function(){
42317         var size;
42318         if(this.el.dom != document.body){
42319             size = this.el.getSize();
42320         }else{
42321             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
42322         }
42323         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
42324         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
42325         return size;
42326     },
42327     
42328     /**
42329      * Returns the Element this layout is bound to.
42330      * @return {Roo.Element}
42331      */
42332     getEl : function(){
42333         return this.el;
42334     },
42335     
42336     /**
42337      * Returns the specified region.
42338      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
42339      * @return {Roo.LayoutRegion}
42340      */
42341     getRegion : function(target){
42342         return this.regions[target.toLowerCase()];
42343     },
42344     
42345     onWindowResize : function(){
42346         if(this.monitorWindowResize){
42347             this.layout();
42348         }
42349     }
42350 });/*
42351  * Based on:
42352  * Ext JS Library 1.1.1
42353  * Copyright(c) 2006-2007, Ext JS, LLC.
42354  *
42355  * Originally Released Under LGPL - original licence link has changed is not relivant.
42356  *
42357  * Fork - LGPL
42358  * <script type="text/javascript">
42359  */
42360 /**
42361  * @class Roo.BorderLayout
42362  * @extends Roo.LayoutManager
42363  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
42364  * please see: <br><br>
42365  * <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>
42366  * <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>
42367  * Example:
42368  <pre><code>
42369  var layout = new Roo.BorderLayout(document.body, {
42370     north: {
42371         initialSize: 25,
42372         titlebar: false
42373     },
42374     west: {
42375         split:true,
42376         initialSize: 200,
42377         minSize: 175,
42378         maxSize: 400,
42379         titlebar: true,
42380         collapsible: true
42381     },
42382     east: {
42383         split:true,
42384         initialSize: 202,
42385         minSize: 175,
42386         maxSize: 400,
42387         titlebar: true,
42388         collapsible: true
42389     },
42390     south: {
42391         split:true,
42392         initialSize: 100,
42393         minSize: 100,
42394         maxSize: 200,
42395         titlebar: true,
42396         collapsible: true
42397     },
42398     center: {
42399         titlebar: true,
42400         autoScroll:true,
42401         resizeTabs: true,
42402         minTabWidth: 50,
42403         preferredTabWidth: 150
42404     }
42405 });
42406
42407 // shorthand
42408 var CP = Roo.ContentPanel;
42409
42410 layout.beginUpdate();
42411 layout.add("north", new CP("north", "North"));
42412 layout.add("south", new CP("south", {title: "South", closable: true}));
42413 layout.add("west", new CP("west", {title: "West"}));
42414 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
42415 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
42416 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
42417 layout.getRegion("center").showPanel("center1");
42418 layout.endUpdate();
42419 </code></pre>
42420
42421 <b>The container the layout is rendered into can be either the body element or any other element.
42422 If it is not the body element, the container needs to either be an absolute positioned element,
42423 or you will need to add "position:relative" to the css of the container.  You will also need to specify
42424 the container size if it is not the body element.</b>
42425
42426 * @constructor
42427 * Create a new BorderLayout
42428 * @param {String/HTMLElement/Element} container The container this layout is bound to
42429 * @param {Object} config Configuration options
42430  */
42431 Roo.BorderLayout = function(container, config){
42432     config = config || {};
42433     Roo.BorderLayout.superclass.constructor.call(this, container, config);
42434     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
42435     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
42436         var target = this.factory.validRegions[i];
42437         if(config[target]){
42438             this.addRegion(target, config[target]);
42439         }
42440     }
42441 };
42442
42443 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
42444     /**
42445      * Creates and adds a new region if it doesn't already exist.
42446      * @param {String} target The target region key (north, south, east, west or center).
42447      * @param {Object} config The regions config object
42448      * @return {BorderLayoutRegion} The new region
42449      */
42450     addRegion : function(target, config){
42451         if(!this.regions[target]){
42452             var r = this.factory.create(target, this, config);
42453             this.bindRegion(target, r);
42454         }
42455         return this.regions[target];
42456     },
42457
42458     // private (kinda)
42459     bindRegion : function(name, r){
42460         this.regions[name] = r;
42461         r.on("visibilitychange", this.layout, this);
42462         r.on("paneladded", this.layout, this);
42463         r.on("panelremoved", this.layout, this);
42464         r.on("invalidated", this.layout, this);
42465         r.on("resized", this.onRegionResized, this);
42466         r.on("collapsed", this.onRegionCollapsed, this);
42467         r.on("expanded", this.onRegionExpanded, this);
42468     },
42469
42470     /**
42471      * Performs a layout update.
42472      */
42473     layout : function(){
42474         if(this.updating) return;
42475         var size = this.getViewSize();
42476         var w = size.width;
42477         var h = size.height;
42478         var centerW = w;
42479         var centerH = h;
42480         var centerY = 0;
42481         var centerX = 0;
42482         //var x = 0, y = 0;
42483
42484         var rs = this.regions;
42485         var north = rs["north"];
42486         var south = rs["south"]; 
42487         var west = rs["west"];
42488         var east = rs["east"];
42489         var center = rs["center"];
42490         //if(this.hideOnLayout){ // not supported anymore
42491             //c.el.setStyle("display", "none");
42492         //}
42493         if(north && north.isVisible()){
42494             var b = north.getBox();
42495             var m = north.getMargins();
42496             b.width = w - (m.left+m.right);
42497             b.x = m.left;
42498             b.y = m.top;
42499             centerY = b.height + b.y + m.bottom;
42500             centerH -= centerY;
42501             north.updateBox(this.safeBox(b));
42502         }
42503         if(south && south.isVisible()){
42504             var b = south.getBox();
42505             var m = south.getMargins();
42506             b.width = w - (m.left+m.right);
42507             b.x = m.left;
42508             var totalHeight = (b.height + m.top + m.bottom);
42509             b.y = h - totalHeight + m.top;
42510             centerH -= totalHeight;
42511             south.updateBox(this.safeBox(b));
42512         }
42513         if(west && west.isVisible()){
42514             var b = west.getBox();
42515             var m = west.getMargins();
42516             b.height = centerH - (m.top+m.bottom);
42517             b.x = m.left;
42518             b.y = centerY + m.top;
42519             var totalWidth = (b.width + m.left + m.right);
42520             centerX += totalWidth;
42521             centerW -= totalWidth;
42522             west.updateBox(this.safeBox(b));
42523         }
42524         if(east && east.isVisible()){
42525             var b = east.getBox();
42526             var m = east.getMargins();
42527             b.height = centerH - (m.top+m.bottom);
42528             var totalWidth = (b.width + m.left + m.right);
42529             b.x = w - totalWidth + m.left;
42530             b.y = centerY + m.top;
42531             centerW -= totalWidth;
42532             east.updateBox(this.safeBox(b));
42533         }
42534         if(center){
42535             var m = center.getMargins();
42536             var centerBox = {
42537                 x: centerX + m.left,
42538                 y: centerY + m.top,
42539                 width: centerW - (m.left+m.right),
42540                 height: centerH - (m.top+m.bottom)
42541             };
42542             //if(this.hideOnLayout){
42543                 //center.el.setStyle("display", "block");
42544             //}
42545             center.updateBox(this.safeBox(centerBox));
42546         }
42547         this.el.repaint();
42548         this.fireEvent("layout", this);
42549     },
42550
42551     // private
42552     safeBox : function(box){
42553         box.width = Math.max(0, box.width);
42554         box.height = Math.max(0, box.height);
42555         return box;
42556     },
42557
42558     /**
42559      * Adds a ContentPanel (or subclass) to this layout.
42560      * @param {String} target The target region key (north, south, east, west or center).
42561      * @param {Roo.ContentPanel} panel The panel to add
42562      * @return {Roo.ContentPanel} The added panel
42563      */
42564     add : function(target, panel){
42565          
42566         target = target.toLowerCase();
42567         return this.regions[target].add(panel);
42568     },
42569
42570     /**
42571      * Remove a ContentPanel (or subclass) to this layout.
42572      * @param {String} target The target region key (north, south, east, west or center).
42573      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
42574      * @return {Roo.ContentPanel} The removed panel
42575      */
42576     remove : function(target, panel){
42577         target = target.toLowerCase();
42578         return this.regions[target].remove(panel);
42579     },
42580
42581     /**
42582      * Searches all regions for a panel with the specified id
42583      * @param {String} panelId
42584      * @return {Roo.ContentPanel} The panel or null if it wasn't found
42585      */
42586     findPanel : function(panelId){
42587         var rs = this.regions;
42588         for(var target in rs){
42589             if(typeof rs[target] != "function"){
42590                 var p = rs[target].getPanel(panelId);
42591                 if(p){
42592                     return p;
42593                 }
42594             }
42595         }
42596         return null;
42597     },
42598
42599     /**
42600      * Searches all regions for a panel with the specified id and activates (shows) it.
42601      * @param {String/ContentPanel} panelId The panels id or the panel itself
42602      * @return {Roo.ContentPanel} The shown panel or null
42603      */
42604     showPanel : function(panelId) {
42605       var rs = this.regions;
42606       for(var target in rs){
42607          var r = rs[target];
42608          if(typeof r != "function"){
42609             if(r.hasPanel(panelId)){
42610                return r.showPanel(panelId);
42611             }
42612          }
42613       }
42614       return null;
42615    },
42616
42617    /**
42618      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
42619      * @param {Roo.state.Provider} provider (optional) An alternate state provider
42620      */
42621     restoreState : function(provider){
42622         if(!provider){
42623             provider = Roo.state.Manager;
42624         }
42625         var sm = new Roo.LayoutStateManager();
42626         sm.init(this, provider);
42627     },
42628
42629     /**
42630      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
42631      * object should contain properties for each region to add ContentPanels to, and each property's value should be
42632      * a valid ContentPanel config object.  Example:
42633      * <pre><code>
42634 // Create the main layout
42635 var layout = new Roo.BorderLayout('main-ct', {
42636     west: {
42637         split:true,
42638         minSize: 175,
42639         titlebar: true
42640     },
42641     center: {
42642         title:'Components'
42643     }
42644 }, 'main-ct');
42645
42646 // Create and add multiple ContentPanels at once via configs
42647 layout.batchAdd({
42648    west: {
42649        id: 'source-files',
42650        autoCreate:true,
42651        title:'Ext Source Files',
42652        autoScroll:true,
42653        fitToFrame:true
42654    },
42655    center : {
42656        el: cview,
42657        autoScroll:true,
42658        fitToFrame:true,
42659        toolbar: tb,
42660        resizeEl:'cbody'
42661    }
42662 });
42663 </code></pre>
42664      * @param {Object} regions An object containing ContentPanel configs by region name
42665      */
42666     batchAdd : function(regions){
42667         this.beginUpdate();
42668         for(var rname in regions){
42669             var lr = this.regions[rname];
42670             if(lr){
42671                 this.addTypedPanels(lr, regions[rname]);
42672             }
42673         }
42674         this.endUpdate();
42675     },
42676
42677     // private
42678     addTypedPanels : function(lr, ps){
42679         if(typeof ps == 'string'){
42680             lr.add(new Roo.ContentPanel(ps));
42681         }
42682         else if(ps instanceof Array){
42683             for(var i =0, len = ps.length; i < len; i++){
42684                 this.addTypedPanels(lr, ps[i]);
42685             }
42686         }
42687         else if(!ps.events){ // raw config?
42688             var el = ps.el;
42689             delete ps.el; // prevent conflict
42690             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
42691         }
42692         else {  // panel object assumed!
42693             lr.add(ps);
42694         }
42695     },
42696     /**
42697      * Adds a xtype elements to the layout.
42698      * <pre><code>
42699
42700 layout.addxtype({
42701        xtype : 'ContentPanel',
42702        region: 'west',
42703        items: [ .... ]
42704    }
42705 );
42706
42707 layout.addxtype({
42708         xtype : 'NestedLayoutPanel',
42709         region: 'west',
42710         layout: {
42711            center: { },
42712            west: { }   
42713         },
42714         items : [ ... list of content panels or nested layout panels.. ]
42715    }
42716 );
42717 </code></pre>
42718      * @param {Object} cfg Xtype definition of item to add.
42719      */
42720     addxtype : function(cfg)
42721     {
42722         // basically accepts a pannel...
42723         // can accept a layout region..!?!?
42724        // console.log('BorderLayout add ' + cfg.xtype)
42725         
42726         if (!cfg.xtype.match(/Panel$/)) {
42727             return false;
42728         }
42729         var ret = false;
42730         var region = cfg.region;
42731         delete cfg.region;
42732         
42733           
42734         var xitems = [];
42735         if (cfg.items) {
42736             xitems = cfg.items;
42737             delete cfg.items;
42738         }
42739         
42740         
42741         switch(cfg.xtype) 
42742         {
42743             case 'ContentPanel':  // ContentPanel (el, cfg)
42744                 if(cfg.autoCreate) {
42745                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42746                 } else {
42747                     var el = this.el.createChild();
42748                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
42749                 }
42750                 
42751                 this.add(region, ret);
42752                 break;
42753             
42754             
42755             case 'TreePanel': // our new panel!
42756                 cfg.el = this.el.createChild();
42757                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
42758                 this.add(region, ret);
42759                 break;
42760             
42761             case 'NestedLayoutPanel': 
42762                 // create a new Layout (which is  a Border Layout...
42763                 var el = this.el.createChild();
42764                 var clayout = cfg.layout;
42765                 delete cfg.layout;
42766                 clayout.items   = clayout.items  || [];
42767                 // replace this exitems with the clayout ones..
42768                 xitems = clayout.items;
42769                  
42770                 
42771                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
42772                     cfg.background = false;
42773                 }
42774                 var layout = new Roo.BorderLayout(el, clayout);
42775                 
42776                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
42777                 //console.log('adding nested layout panel '  + cfg.toSource());
42778                 this.add(region, ret);
42779                 
42780                 break;
42781                 
42782             case 'GridPanel': 
42783             
42784                 // needs grid and region
42785                 
42786                 //var el = this.getRegion(region).el.createChild();
42787                 var el = this.el.createChild();
42788                 // create the grid first...
42789                 
42790                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
42791                 delete cfg.grid;
42792                 if (region == 'center' && this.active ) {
42793                     cfg.background = false;
42794                 }
42795                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
42796                 
42797                 this.add(region, ret);
42798                 if (cfg.background) {
42799                     ret.on('activate', function(gp) {
42800                         if (!gp.grid.rendered) {
42801                             gp.grid.render();
42802                         }
42803                     });
42804                 } else {
42805                     grid.render();
42806                 }
42807                 break;
42808            
42809                
42810                 
42811                 
42812             default: 
42813                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
42814                 return;
42815              // GridPanel (grid, cfg)
42816             
42817         }
42818         this.beginUpdate();
42819         // add children..
42820         Roo.each(xitems, function(i)  {
42821             ret.addxtype(i);
42822         });
42823         this.endUpdate();
42824         return ret;
42825         
42826     }
42827 });
42828
42829 /**
42830  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
42831  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
42832  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
42833  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
42834  * <pre><code>
42835 // shorthand
42836 var CP = Roo.ContentPanel;
42837
42838 var layout = Roo.BorderLayout.create({
42839     north: {
42840         initialSize: 25,
42841         titlebar: false,
42842         panels: [new CP("north", "North")]
42843     },
42844     west: {
42845         split:true,
42846         initialSize: 200,
42847         minSize: 175,
42848         maxSize: 400,
42849         titlebar: true,
42850         collapsible: true,
42851         panels: [new CP("west", {title: "West"})]
42852     },
42853     east: {
42854         split:true,
42855         initialSize: 202,
42856         minSize: 175,
42857         maxSize: 400,
42858         titlebar: true,
42859         collapsible: true,
42860         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
42861     },
42862     south: {
42863         split:true,
42864         initialSize: 100,
42865         minSize: 100,
42866         maxSize: 200,
42867         titlebar: true,
42868         collapsible: true,
42869         panels: [new CP("south", {title: "South", closable: true})]
42870     },
42871     center: {
42872         titlebar: true,
42873         autoScroll:true,
42874         resizeTabs: true,
42875         minTabWidth: 50,
42876         preferredTabWidth: 150,
42877         panels: [
42878             new CP("center1", {title: "Close Me", closable: true}),
42879             new CP("center2", {title: "Center Panel", closable: false})
42880         ]
42881     }
42882 }, document.body);
42883
42884 layout.getRegion("center").showPanel("center1");
42885 </code></pre>
42886  * @param config
42887  * @param targetEl
42888  */
42889 Roo.BorderLayout.create = function(config, targetEl){
42890     var layout = new Roo.BorderLayout(targetEl || document.body, config);
42891     layout.beginUpdate();
42892     var regions = Roo.BorderLayout.RegionFactory.validRegions;
42893     for(var j = 0, jlen = regions.length; j < jlen; j++){
42894         var lr = regions[j];
42895         if(layout.regions[lr] && config[lr].panels){
42896             var r = layout.regions[lr];
42897             var ps = config[lr].panels;
42898             layout.addTypedPanels(r, ps);
42899         }
42900     }
42901     layout.endUpdate();
42902     return layout;
42903 };
42904
42905 // private
42906 Roo.BorderLayout.RegionFactory = {
42907     // private
42908     validRegions : ["north","south","east","west","center"],
42909
42910     // private
42911     create : function(target, mgr, config){
42912         target = target.toLowerCase();
42913         if(config.lightweight || config.basic){
42914             return new Roo.BasicLayoutRegion(mgr, config, target);
42915         }
42916         switch(target){
42917             case "north":
42918                 return new Roo.NorthLayoutRegion(mgr, config);
42919             case "south":
42920                 return new Roo.SouthLayoutRegion(mgr, config);
42921             case "east":
42922                 return new Roo.EastLayoutRegion(mgr, config);
42923             case "west":
42924                 return new Roo.WestLayoutRegion(mgr, config);
42925             case "center":
42926                 return new Roo.CenterLayoutRegion(mgr, config);
42927         }
42928         throw 'Layout region "'+target+'" not supported.';
42929     }
42930 };/*
42931  * Based on:
42932  * Ext JS Library 1.1.1
42933  * Copyright(c) 2006-2007, Ext JS, LLC.
42934  *
42935  * Originally Released Under LGPL - original licence link has changed is not relivant.
42936  *
42937  * Fork - LGPL
42938  * <script type="text/javascript">
42939  */
42940  
42941 /**
42942  * @class Roo.BasicLayoutRegion
42943  * @extends Roo.util.Observable
42944  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
42945  * and does not have a titlebar, tabs or any other features. All it does is size and position 
42946  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
42947  */
42948 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
42949     this.mgr = mgr;
42950     this.position  = pos;
42951     this.events = {
42952         /**
42953          * @scope Roo.BasicLayoutRegion
42954          */
42955         
42956         /**
42957          * @event beforeremove
42958          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
42959          * @param {Roo.LayoutRegion} this
42960          * @param {Roo.ContentPanel} panel The panel
42961          * @param {Object} e The cancel event object
42962          */
42963         "beforeremove" : true,
42964         /**
42965          * @event invalidated
42966          * Fires when the layout for this region is changed.
42967          * @param {Roo.LayoutRegion} this
42968          */
42969         "invalidated" : true,
42970         /**
42971          * @event visibilitychange
42972          * Fires when this region is shown or hidden 
42973          * @param {Roo.LayoutRegion} this
42974          * @param {Boolean} visibility true or false
42975          */
42976         "visibilitychange" : true,
42977         /**
42978          * @event paneladded
42979          * Fires when a panel is added. 
42980          * @param {Roo.LayoutRegion} this
42981          * @param {Roo.ContentPanel} panel The panel
42982          */
42983         "paneladded" : true,
42984         /**
42985          * @event panelremoved
42986          * Fires when a panel is removed. 
42987          * @param {Roo.LayoutRegion} this
42988          * @param {Roo.ContentPanel} panel The panel
42989          */
42990         "panelremoved" : true,
42991         /**
42992          * @event collapsed
42993          * Fires when this region is collapsed.
42994          * @param {Roo.LayoutRegion} this
42995          */
42996         "collapsed" : true,
42997         /**
42998          * @event expanded
42999          * Fires when this region is expanded.
43000          * @param {Roo.LayoutRegion} this
43001          */
43002         "expanded" : true,
43003         /**
43004          * @event slideshow
43005          * Fires when this region is slid into view.
43006          * @param {Roo.LayoutRegion} this
43007          */
43008         "slideshow" : true,
43009         /**
43010          * @event slidehide
43011          * Fires when this region slides out of view. 
43012          * @param {Roo.LayoutRegion} this
43013          */
43014         "slidehide" : true,
43015         /**
43016          * @event panelactivated
43017          * Fires when a panel is activated. 
43018          * @param {Roo.LayoutRegion} this
43019          * @param {Roo.ContentPanel} panel The activated panel
43020          */
43021         "panelactivated" : true,
43022         /**
43023          * @event resized
43024          * Fires when the user resizes this region. 
43025          * @param {Roo.LayoutRegion} this
43026          * @param {Number} newSize The new size (width for east/west, height for north/south)
43027          */
43028         "resized" : true
43029     };
43030     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43031     this.panels = new Roo.util.MixedCollection();
43032     this.panels.getKey = this.getPanelId.createDelegate(this);
43033     this.box = null;
43034     this.activePanel = null;
43035     // ensure listeners are added...
43036     
43037     if (config.listeners || config.events) {
43038         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43039             listeners : config.listeners || {},
43040             events : config.events || {}
43041         });
43042     }
43043     
43044     if(skipConfig !== true){
43045         this.applyConfig(config);
43046     }
43047 };
43048
43049 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43050     getPanelId : function(p){
43051         return p.getId();
43052     },
43053     
43054     applyConfig : function(config){
43055         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43056         this.config = config;
43057         
43058     },
43059     
43060     /**
43061      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43062      * the width, for horizontal (north, south) the height.
43063      * @param {Number} newSize The new width or height
43064      */
43065     resizeTo : function(newSize){
43066         var el = this.el ? this.el :
43067                  (this.activePanel ? this.activePanel.getEl() : null);
43068         if(el){
43069             switch(this.position){
43070                 case "east":
43071                 case "west":
43072                     el.setWidth(newSize);
43073                     this.fireEvent("resized", this, newSize);
43074                 break;
43075                 case "north":
43076                 case "south":
43077                     el.setHeight(newSize);
43078                     this.fireEvent("resized", this, newSize);
43079                 break;                
43080             }
43081         }
43082     },
43083     
43084     getBox : function(){
43085         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43086     },
43087     
43088     getMargins : function(){
43089         return this.margins;
43090     },
43091     
43092     updateBox : function(box){
43093         this.box = box;
43094         var el = this.activePanel.getEl();
43095         el.dom.style.left = box.x + "px";
43096         el.dom.style.top = box.y + "px";
43097         this.activePanel.setSize(box.width, box.height);
43098     },
43099     
43100     /**
43101      * Returns the container element for this region.
43102      * @return {Roo.Element}
43103      */
43104     getEl : function(){
43105         return this.activePanel;
43106     },
43107     
43108     /**
43109      * Returns true if this region is currently visible.
43110      * @return {Boolean}
43111      */
43112     isVisible : function(){
43113         return this.activePanel ? true : false;
43114     },
43115     
43116     setActivePanel : function(panel){
43117         panel = this.getPanel(panel);
43118         if(this.activePanel && this.activePanel != panel){
43119             this.activePanel.setActiveState(false);
43120             this.activePanel.getEl().setLeftTop(-10000,-10000);
43121         }
43122         this.activePanel = panel;
43123         panel.setActiveState(true);
43124         if(this.box){
43125             panel.setSize(this.box.width, this.box.height);
43126         }
43127         this.fireEvent("panelactivated", this, panel);
43128         this.fireEvent("invalidated");
43129     },
43130     
43131     /**
43132      * Show the specified panel.
43133      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43134      * @return {Roo.ContentPanel} The shown panel or null
43135      */
43136     showPanel : function(panel){
43137         if(panel = this.getPanel(panel)){
43138             this.setActivePanel(panel);
43139         }
43140         return panel;
43141     },
43142     
43143     /**
43144      * Get the active panel for this region.
43145      * @return {Roo.ContentPanel} The active panel or null
43146      */
43147     getActivePanel : function(){
43148         return this.activePanel;
43149     },
43150     
43151     /**
43152      * Add the passed ContentPanel(s)
43153      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43154      * @return {Roo.ContentPanel} The panel added (if only one was added)
43155      */
43156     add : function(panel){
43157         if(arguments.length > 1){
43158             for(var i = 0, len = arguments.length; i < len; i++) {
43159                 this.add(arguments[i]);
43160             }
43161             return null;
43162         }
43163         if(this.hasPanel(panel)){
43164             this.showPanel(panel);
43165             return panel;
43166         }
43167         var el = panel.getEl();
43168         if(el.dom.parentNode != this.mgr.el.dom){
43169             this.mgr.el.dom.appendChild(el.dom);
43170         }
43171         if(panel.setRegion){
43172             panel.setRegion(this);
43173         }
43174         this.panels.add(panel);
43175         el.setStyle("position", "absolute");
43176         if(!panel.background){
43177             this.setActivePanel(panel);
43178             if(this.config.initialSize && this.panels.getCount()==1){
43179                 this.resizeTo(this.config.initialSize);
43180             }
43181         }
43182         this.fireEvent("paneladded", this, panel);
43183         return panel;
43184     },
43185     
43186     /**
43187      * Returns true if the panel is in this region.
43188      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43189      * @return {Boolean}
43190      */
43191     hasPanel : function(panel){
43192         if(typeof panel == "object"){ // must be panel obj
43193             panel = panel.getId();
43194         }
43195         return this.getPanel(panel) ? true : false;
43196     },
43197     
43198     /**
43199      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43200      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43201      * @param {Boolean} preservePanel Overrides the config preservePanel option
43202      * @return {Roo.ContentPanel} The panel that was removed
43203      */
43204     remove : function(panel, preservePanel){
43205         panel = this.getPanel(panel);
43206         if(!panel){
43207             return null;
43208         }
43209         var e = {};
43210         this.fireEvent("beforeremove", this, panel, e);
43211         if(e.cancel === true){
43212             return null;
43213         }
43214         var panelId = panel.getId();
43215         this.panels.removeKey(panelId);
43216         return panel;
43217     },
43218     
43219     /**
43220      * Returns the panel specified or null if it's not in this region.
43221      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
43222      * @return {Roo.ContentPanel}
43223      */
43224     getPanel : function(id){
43225         if(typeof id == "object"){ // must be panel obj
43226             return id;
43227         }
43228         return this.panels.get(id);
43229     },
43230     
43231     /**
43232      * Returns this regions position (north/south/east/west/center).
43233      * @return {String} 
43234      */
43235     getPosition: function(){
43236         return this.position;    
43237     }
43238 });/*
43239  * Based on:
43240  * Ext JS Library 1.1.1
43241  * Copyright(c) 2006-2007, Ext JS, LLC.
43242  *
43243  * Originally Released Under LGPL - original licence link has changed is not relivant.
43244  *
43245  * Fork - LGPL
43246  * <script type="text/javascript">
43247  */
43248  
43249 /**
43250  * @class Roo.LayoutRegion
43251  * @extends Roo.BasicLayoutRegion
43252  * This class represents a region in a layout manager.
43253  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
43254  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
43255  * @cfg {Boolean} floatable False to disable floating (defaults to true)
43256  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
43257  * @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})
43258  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
43259  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
43260  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
43261  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
43262  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
43263  * @cfg {String} title The title for the region (overrides panel titles)
43264  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
43265  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
43266  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
43267  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
43268  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
43269  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
43270  * the space available, similar to FireFox 1.5 tabs (defaults to false)
43271  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
43272  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
43273  * @cfg {Boolean} showPin True to show a pin button
43274 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
43275 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
43276 * @cfg {Boolean} disableTabTips True to disable tab tooltips
43277 * @cfg {Number} width  For East/West panels
43278 * @cfg {Number} height For North/South panels
43279 * @cfg {Boolean} split To show the splitter
43280  */
43281 Roo.LayoutRegion = function(mgr, config, pos){
43282     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
43283     var dh = Roo.DomHelper;
43284     /** This region's container element 
43285     * @type Roo.Element */
43286     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
43287     /** This region's title element 
43288     * @type Roo.Element */
43289
43290     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
43291         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
43292         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
43293     ]}, true);
43294     this.titleEl.enableDisplayMode();
43295     /** This region's title text element 
43296     * @type HTMLElement */
43297     this.titleTextEl = this.titleEl.dom.firstChild;
43298     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
43299     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
43300     this.closeBtn.enableDisplayMode();
43301     this.closeBtn.on("click", this.closeClicked, this);
43302     this.closeBtn.hide();
43303
43304     this.createBody(config);
43305     this.visible = true;
43306     this.collapsed = false;
43307
43308     if(config.hideWhenEmpty){
43309         this.hide();
43310         this.on("paneladded", this.validateVisibility, this);
43311         this.on("panelremoved", this.validateVisibility, this);
43312     }
43313     this.applyConfig(config);
43314 };
43315
43316 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
43317
43318     createBody : function(){
43319         /** This region's body element 
43320         * @type Roo.Element */
43321         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
43322     },
43323
43324     applyConfig : function(c){
43325         if(c.collapsible && this.position != "center" && !this.collapsedEl){
43326             var dh = Roo.DomHelper;
43327             if(c.titlebar !== false){
43328                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
43329                 this.collapseBtn.on("click", this.collapse, this);
43330                 this.collapseBtn.enableDisplayMode();
43331
43332                 if(c.showPin === true || this.showPin){
43333                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
43334                     this.stickBtn.enableDisplayMode();
43335                     this.stickBtn.on("click", this.expand, this);
43336                     this.stickBtn.hide();
43337                 }
43338             }
43339             /** This region's collapsed element
43340             * @type Roo.Element */
43341             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
43342                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
43343             ]}, true);
43344             if(c.floatable !== false){
43345                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
43346                this.collapsedEl.on("click", this.collapseClick, this);
43347             }
43348
43349             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
43350                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
43351                    id: "message", unselectable: "on", style:{"float":"left"}});
43352                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
43353              }
43354             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
43355             this.expandBtn.on("click", this.expand, this);
43356         }
43357         if(this.collapseBtn){
43358             this.collapseBtn.setVisible(c.collapsible == true);
43359         }
43360         this.cmargins = c.cmargins || this.cmargins ||
43361                          (this.position == "west" || this.position == "east" ?
43362                              {top: 0, left: 2, right:2, bottom: 0} :
43363                              {top: 2, left: 0, right:0, bottom: 2});
43364         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43365         this.bottomTabs = c.tabPosition != "top";
43366         this.autoScroll = c.autoScroll || false;
43367         if(this.autoScroll){
43368             this.bodyEl.setStyle("overflow", "auto");
43369         }else{
43370             this.bodyEl.setStyle("overflow", "hidden");
43371         }
43372         //if(c.titlebar !== false){
43373             if((!c.titlebar && !c.title) || c.titlebar === false){
43374                 this.titleEl.hide();
43375             }else{
43376                 this.titleEl.show();
43377                 if(c.title){
43378                     this.titleTextEl.innerHTML = c.title;
43379                 }
43380             }
43381         //}
43382         this.duration = c.duration || .30;
43383         this.slideDuration = c.slideDuration || .45;
43384         this.config = c;
43385         if(c.collapsed){
43386             this.collapse(true);
43387         }
43388         if(c.hidden){
43389             this.hide();
43390         }
43391     },
43392     /**
43393      * Returns true if this region is currently visible.
43394      * @return {Boolean}
43395      */
43396     isVisible : function(){
43397         return this.visible;
43398     },
43399
43400     /**
43401      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
43402      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
43403      */
43404     setCollapsedTitle : function(title){
43405         title = title || "&#160;";
43406         if(this.collapsedTitleTextEl){
43407             this.collapsedTitleTextEl.innerHTML = title;
43408         }
43409     },
43410
43411     getBox : function(){
43412         var b;
43413         if(!this.collapsed){
43414             b = this.el.getBox(false, true);
43415         }else{
43416             b = this.collapsedEl.getBox(false, true);
43417         }
43418         return b;
43419     },
43420
43421     getMargins : function(){
43422         return this.collapsed ? this.cmargins : this.margins;
43423     },
43424
43425     highlight : function(){
43426         this.el.addClass("x-layout-panel-dragover");
43427     },
43428
43429     unhighlight : function(){
43430         this.el.removeClass("x-layout-panel-dragover");
43431     },
43432
43433     updateBox : function(box){
43434         this.box = box;
43435         if(!this.collapsed){
43436             this.el.dom.style.left = box.x + "px";
43437             this.el.dom.style.top = box.y + "px";
43438             this.updateBody(box.width, box.height);
43439         }else{
43440             this.collapsedEl.dom.style.left = box.x + "px";
43441             this.collapsedEl.dom.style.top = box.y + "px";
43442             this.collapsedEl.setSize(box.width, box.height);
43443         }
43444         if(this.tabs){
43445             this.tabs.autoSizeTabs();
43446         }
43447     },
43448
43449     updateBody : function(w, h){
43450         if(w !== null){
43451             this.el.setWidth(w);
43452             w -= this.el.getBorderWidth("rl");
43453             if(this.config.adjustments){
43454                 w += this.config.adjustments[0];
43455             }
43456         }
43457         if(h !== null){
43458             this.el.setHeight(h);
43459             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
43460             h -= this.el.getBorderWidth("tb");
43461             if(this.config.adjustments){
43462                 h += this.config.adjustments[1];
43463             }
43464             this.bodyEl.setHeight(h);
43465             if(this.tabs){
43466                 h = this.tabs.syncHeight(h);
43467             }
43468         }
43469         if(this.panelSize){
43470             w = w !== null ? w : this.panelSize.width;
43471             h = h !== null ? h : this.panelSize.height;
43472         }
43473         if(this.activePanel){
43474             var el = this.activePanel.getEl();
43475             w = w !== null ? w : el.getWidth();
43476             h = h !== null ? h : el.getHeight();
43477             this.panelSize = {width: w, height: h};
43478             this.activePanel.setSize(w, h);
43479         }
43480         if(Roo.isIE && this.tabs){
43481             this.tabs.el.repaint();
43482         }
43483     },
43484
43485     /**
43486      * Returns the container element for this region.
43487      * @return {Roo.Element}
43488      */
43489     getEl : function(){
43490         return this.el;
43491     },
43492
43493     /**
43494      * Hides this region.
43495      */
43496     hide : function(){
43497         if(!this.collapsed){
43498             this.el.dom.style.left = "-2000px";
43499             this.el.hide();
43500         }else{
43501             this.collapsedEl.dom.style.left = "-2000px";
43502             this.collapsedEl.hide();
43503         }
43504         this.visible = false;
43505         this.fireEvent("visibilitychange", this, false);
43506     },
43507
43508     /**
43509      * Shows this region if it was previously hidden.
43510      */
43511     show : function(){
43512         if(!this.collapsed){
43513             this.el.show();
43514         }else{
43515             this.collapsedEl.show();
43516         }
43517         this.visible = true;
43518         this.fireEvent("visibilitychange", this, true);
43519     },
43520
43521     closeClicked : function(){
43522         if(this.activePanel){
43523             this.remove(this.activePanel);
43524         }
43525     },
43526
43527     collapseClick : function(e){
43528         if(this.isSlid){
43529            e.stopPropagation();
43530            this.slideIn();
43531         }else{
43532            e.stopPropagation();
43533            this.slideOut();
43534         }
43535     },
43536
43537     /**
43538      * Collapses this region.
43539      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
43540      */
43541     collapse : function(skipAnim){
43542         if(this.collapsed) return;
43543         this.collapsed = true;
43544         if(this.split){
43545             this.split.el.hide();
43546         }
43547         if(this.config.animate && skipAnim !== true){
43548             this.fireEvent("invalidated", this);
43549             this.animateCollapse();
43550         }else{
43551             this.el.setLocation(-20000,-20000);
43552             this.el.hide();
43553             this.collapsedEl.show();
43554             this.fireEvent("collapsed", this);
43555             this.fireEvent("invalidated", this);
43556         }
43557     },
43558
43559     animateCollapse : function(){
43560         // overridden
43561     },
43562
43563     /**
43564      * Expands this region if it was previously collapsed.
43565      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
43566      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
43567      */
43568     expand : function(e, skipAnim){
43569         if(e) e.stopPropagation();
43570         if(!this.collapsed || this.el.hasActiveFx()) return;
43571         if(this.isSlid){
43572             this.afterSlideIn();
43573             skipAnim = true;
43574         }
43575         this.collapsed = false;
43576         if(this.config.animate && skipAnim !== true){
43577             this.animateExpand();
43578         }else{
43579             this.el.show();
43580             if(this.split){
43581                 this.split.el.show();
43582             }
43583             this.collapsedEl.setLocation(-2000,-2000);
43584             this.collapsedEl.hide();
43585             this.fireEvent("invalidated", this);
43586             this.fireEvent("expanded", this);
43587         }
43588     },
43589
43590     animateExpand : function(){
43591         // overridden
43592     },
43593
43594     initTabs : function(){
43595         this.bodyEl.setStyle("overflow", "hidden");
43596         var ts = new Roo.TabPanel(this.bodyEl.dom, {
43597             tabPosition: this.bottomTabs ? 'bottom' : 'top',
43598             disableTooltips: this.config.disableTabTips
43599         });
43600         if(this.config.hideTabs){
43601             ts.stripWrap.setDisplayed(false);
43602         }
43603         this.tabs = ts;
43604         ts.resizeTabs = this.config.resizeTabs === true;
43605         ts.minTabWidth = this.config.minTabWidth || 40;
43606         ts.maxTabWidth = this.config.maxTabWidth || 250;
43607         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
43608         ts.monitorResize = false;
43609         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43610         ts.bodyEl.addClass('x-layout-tabs-body');
43611         this.panels.each(this.initPanelAsTab, this);
43612     },
43613
43614     initPanelAsTab : function(panel){
43615         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
43616                     this.config.closeOnTab && panel.isClosable());
43617         if(panel.tabTip !== undefined){
43618             ti.setTooltip(panel.tabTip);
43619         }
43620         ti.on("activate", function(){
43621               this.setActivePanel(panel);
43622         }, this);
43623         if(this.config.closeOnTab){
43624             ti.on("beforeclose", function(t, e){
43625                 e.cancel = true;
43626                 this.remove(panel);
43627             }, this);
43628         }
43629         return ti;
43630     },
43631
43632     updatePanelTitle : function(panel, title){
43633         if(this.activePanel == panel){
43634             this.updateTitle(title);
43635         }
43636         if(this.tabs){
43637             var ti = this.tabs.getTab(panel.getEl().id);
43638             ti.setText(title);
43639             if(panel.tabTip !== undefined){
43640                 ti.setTooltip(panel.tabTip);
43641             }
43642         }
43643     },
43644
43645     updateTitle : function(title){
43646         if(this.titleTextEl && !this.config.title){
43647             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
43648         }
43649     },
43650
43651     setActivePanel : function(panel){
43652         panel = this.getPanel(panel);
43653         if(this.activePanel && this.activePanel != panel){
43654             this.activePanel.setActiveState(false);
43655         }
43656         this.activePanel = panel;
43657         panel.setActiveState(true);
43658         if(this.panelSize){
43659             panel.setSize(this.panelSize.width, this.panelSize.height);
43660         }
43661         if(this.closeBtn){
43662             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
43663         }
43664         this.updateTitle(panel.getTitle());
43665         if(this.tabs){
43666             this.fireEvent("invalidated", this);
43667         }
43668         this.fireEvent("panelactivated", this, panel);
43669     },
43670
43671     /**
43672      * Shows the specified panel.
43673      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
43674      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
43675      */
43676     showPanel : function(panel){
43677         if(panel = this.getPanel(panel)){
43678             if(this.tabs){
43679                 var tab = this.tabs.getTab(panel.getEl().id);
43680                 if(tab.isHidden()){
43681                     this.tabs.unhideTab(tab.id);
43682                 }
43683                 tab.activate();
43684             }else{
43685                 this.setActivePanel(panel);
43686             }
43687         }
43688         return panel;
43689     },
43690
43691     /**
43692      * Get the active panel for this region.
43693      * @return {Roo.ContentPanel} The active panel or null
43694      */
43695     getActivePanel : function(){
43696         return this.activePanel;
43697     },
43698
43699     validateVisibility : function(){
43700         if(this.panels.getCount() < 1){
43701             this.updateTitle("&#160;");
43702             this.closeBtn.hide();
43703             this.hide();
43704         }else{
43705             if(!this.isVisible()){
43706                 this.show();
43707             }
43708         }
43709     },
43710
43711     /**
43712      * Adds the passed ContentPanel(s) to this region.
43713      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43714      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
43715      */
43716     add : function(panel){
43717         if(arguments.length > 1){
43718             for(var i = 0, len = arguments.length; i < len; i++) {
43719                 this.add(arguments[i]);
43720             }
43721             return null;
43722         }
43723         if(this.hasPanel(panel)){
43724             this.showPanel(panel);
43725             return panel;
43726         }
43727         panel.setRegion(this);
43728         this.panels.add(panel);
43729         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
43730             this.bodyEl.dom.appendChild(panel.getEl().dom);
43731             if(panel.background !== true){
43732                 this.setActivePanel(panel);
43733             }
43734             this.fireEvent("paneladded", this, panel);
43735             return panel;
43736         }
43737         if(!this.tabs){
43738             this.initTabs();
43739         }else{
43740             this.initPanelAsTab(panel);
43741         }
43742         if(panel.background !== true){
43743             this.tabs.activate(panel.getEl().id);
43744         }
43745         this.fireEvent("paneladded", this, panel);
43746         return panel;
43747     },
43748
43749     /**
43750      * Hides the tab for the specified panel.
43751      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43752      */
43753     hidePanel : function(panel){
43754         if(this.tabs && (panel = this.getPanel(panel))){
43755             this.tabs.hideTab(panel.getEl().id);
43756         }
43757     },
43758
43759     /**
43760      * Unhides the tab for a previously hidden panel.
43761      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43762      */
43763     unhidePanel : function(panel){
43764         if(this.tabs && (panel = this.getPanel(panel))){
43765             this.tabs.unhideTab(panel.getEl().id);
43766         }
43767     },
43768
43769     clearPanels : function(){
43770         while(this.panels.getCount() > 0){
43771              this.remove(this.panels.first());
43772         }
43773     },
43774
43775     /**
43776      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
43777      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
43778      * @param {Boolean} preservePanel Overrides the config preservePanel option
43779      * @return {Roo.ContentPanel} The panel that was removed
43780      */
43781     remove : function(panel, preservePanel){
43782         panel = this.getPanel(panel);
43783         if(!panel){
43784             return null;
43785         }
43786         var e = {};
43787         this.fireEvent("beforeremove", this, panel, e);
43788         if(e.cancel === true){
43789             return null;
43790         }
43791         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
43792         var panelId = panel.getId();
43793         this.panels.removeKey(panelId);
43794         if(preservePanel){
43795             document.body.appendChild(panel.getEl().dom);
43796         }
43797         if(this.tabs){
43798             this.tabs.removeTab(panel.getEl().id);
43799         }else if (!preservePanel){
43800             this.bodyEl.dom.removeChild(panel.getEl().dom);
43801         }
43802         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
43803             var p = this.panels.first();
43804             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
43805             tempEl.appendChild(p.getEl().dom);
43806             this.bodyEl.update("");
43807             this.bodyEl.dom.appendChild(p.getEl().dom);
43808             tempEl = null;
43809             this.updateTitle(p.getTitle());
43810             this.tabs = null;
43811             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
43812             this.setActivePanel(p);
43813         }
43814         panel.setRegion(null);
43815         if(this.activePanel == panel){
43816             this.activePanel = null;
43817         }
43818         if(this.config.autoDestroy !== false && preservePanel !== true){
43819             try{panel.destroy();}catch(e){}
43820         }
43821         this.fireEvent("panelremoved", this, panel);
43822         return panel;
43823     },
43824
43825     /**
43826      * Returns the TabPanel component used by this region
43827      * @return {Roo.TabPanel}
43828      */
43829     getTabs : function(){
43830         return this.tabs;
43831     },
43832
43833     createTool : function(parentEl, className){
43834         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
43835             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
43836         btn.addClassOnOver("x-layout-tools-button-over");
43837         return btn;
43838     }
43839 });/*
43840  * Based on:
43841  * Ext JS Library 1.1.1
43842  * Copyright(c) 2006-2007, Ext JS, LLC.
43843  *
43844  * Originally Released Under LGPL - original licence link has changed is not relivant.
43845  *
43846  * Fork - LGPL
43847  * <script type="text/javascript">
43848  */
43849  
43850
43851
43852 /**
43853  * @class Roo.SplitLayoutRegion
43854  * @extends Roo.LayoutRegion
43855  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
43856  */
43857 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
43858     this.cursor = cursor;
43859     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
43860 };
43861
43862 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
43863     splitTip : "Drag to resize.",
43864     collapsibleSplitTip : "Drag to resize. Double click to hide.",
43865     useSplitTips : false,
43866
43867     applyConfig : function(config){
43868         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
43869         if(config.split){
43870             if(!this.split){
43871                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
43872                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
43873                 /** The SplitBar for this region 
43874                 * @type Roo.SplitBar */
43875                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
43876                 this.split.on("moved", this.onSplitMove, this);
43877                 this.split.useShim = config.useShim === true;
43878                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
43879                 if(this.useSplitTips){
43880                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
43881                 }
43882                 if(config.collapsible){
43883                     this.split.el.on("dblclick", this.collapse,  this);
43884                 }
43885             }
43886             if(typeof config.minSize != "undefined"){
43887                 this.split.minSize = config.minSize;
43888             }
43889             if(typeof config.maxSize != "undefined"){
43890                 this.split.maxSize = config.maxSize;
43891             }
43892             if(config.hideWhenEmpty || config.hidden || config.collapsed){
43893                 this.hideSplitter();
43894             }
43895         }
43896     },
43897
43898     getHMaxSize : function(){
43899          var cmax = this.config.maxSize || 10000;
43900          var center = this.mgr.getRegion("center");
43901          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
43902     },
43903
43904     getVMaxSize : function(){
43905          var cmax = this.config.maxSize || 10000;
43906          var center = this.mgr.getRegion("center");
43907          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
43908     },
43909
43910     onSplitMove : function(split, newSize){
43911         this.fireEvent("resized", this, newSize);
43912     },
43913     
43914     /** 
43915      * Returns the {@link Roo.SplitBar} for this region.
43916      * @return {Roo.SplitBar}
43917      */
43918     getSplitBar : function(){
43919         return this.split;
43920     },
43921     
43922     hide : function(){
43923         this.hideSplitter();
43924         Roo.SplitLayoutRegion.superclass.hide.call(this);
43925     },
43926
43927     hideSplitter : function(){
43928         if(this.split){
43929             this.split.el.setLocation(-2000,-2000);
43930             this.split.el.hide();
43931         }
43932     },
43933
43934     show : function(){
43935         if(this.split){
43936             this.split.el.show();
43937         }
43938         Roo.SplitLayoutRegion.superclass.show.call(this);
43939     },
43940     
43941     beforeSlide: function(){
43942         if(Roo.isGecko){// firefox overflow auto bug workaround
43943             this.bodyEl.clip();
43944             if(this.tabs) this.tabs.bodyEl.clip();
43945             if(this.activePanel){
43946                 this.activePanel.getEl().clip();
43947                 
43948                 if(this.activePanel.beforeSlide){
43949                     this.activePanel.beforeSlide();
43950                 }
43951             }
43952         }
43953     },
43954     
43955     afterSlide : function(){
43956         if(Roo.isGecko){// firefox overflow auto bug workaround
43957             this.bodyEl.unclip();
43958             if(this.tabs) this.tabs.bodyEl.unclip();
43959             if(this.activePanel){
43960                 this.activePanel.getEl().unclip();
43961                 if(this.activePanel.afterSlide){
43962                     this.activePanel.afterSlide();
43963                 }
43964             }
43965         }
43966     },
43967
43968     initAutoHide : function(){
43969         if(this.autoHide !== false){
43970             if(!this.autoHideHd){
43971                 var st = new Roo.util.DelayedTask(this.slideIn, this);
43972                 this.autoHideHd = {
43973                     "mouseout": function(e){
43974                         if(!e.within(this.el, true)){
43975                             st.delay(500);
43976                         }
43977                     },
43978                     "mouseover" : function(e){
43979                         st.cancel();
43980                     },
43981                     scope : this
43982                 };
43983             }
43984             this.el.on(this.autoHideHd);
43985         }
43986     },
43987
43988     clearAutoHide : function(){
43989         if(this.autoHide !== false){
43990             this.el.un("mouseout", this.autoHideHd.mouseout);
43991             this.el.un("mouseover", this.autoHideHd.mouseover);
43992         }
43993     },
43994
43995     clearMonitor : function(){
43996         Roo.get(document).un("click", this.slideInIf, this);
43997     },
43998
43999     // these names are backwards but not changed for compat
44000     slideOut : function(){
44001         if(this.isSlid || this.el.hasActiveFx()){
44002             return;
44003         }
44004         this.isSlid = true;
44005         if(this.collapseBtn){
44006             this.collapseBtn.hide();
44007         }
44008         this.closeBtnState = this.closeBtn.getStyle('display');
44009         this.closeBtn.hide();
44010         if(this.stickBtn){
44011             this.stickBtn.show();
44012         }
44013         this.el.show();
44014         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44015         this.beforeSlide();
44016         this.el.setStyle("z-index", 10001);
44017         this.el.slideIn(this.getSlideAnchor(), {
44018             callback: function(){
44019                 this.afterSlide();
44020                 this.initAutoHide();
44021                 Roo.get(document).on("click", this.slideInIf, this);
44022                 this.fireEvent("slideshow", this);
44023             },
44024             scope: this,
44025             block: true
44026         });
44027     },
44028
44029     afterSlideIn : function(){
44030         this.clearAutoHide();
44031         this.isSlid = false;
44032         this.clearMonitor();
44033         this.el.setStyle("z-index", "");
44034         if(this.collapseBtn){
44035             this.collapseBtn.show();
44036         }
44037         this.closeBtn.setStyle('display', this.closeBtnState);
44038         if(this.stickBtn){
44039             this.stickBtn.hide();
44040         }
44041         this.fireEvent("slidehide", this);
44042     },
44043
44044     slideIn : function(cb){
44045         if(!this.isSlid || this.el.hasActiveFx()){
44046             Roo.callback(cb);
44047             return;
44048         }
44049         this.isSlid = false;
44050         this.beforeSlide();
44051         this.el.slideOut(this.getSlideAnchor(), {
44052             callback: function(){
44053                 this.el.setLeftTop(-10000, -10000);
44054                 this.afterSlide();
44055                 this.afterSlideIn();
44056                 Roo.callback(cb);
44057             },
44058             scope: this,
44059             block: true
44060         });
44061     },
44062     
44063     slideInIf : function(e){
44064         if(!e.within(this.el)){
44065             this.slideIn();
44066         }
44067     },
44068
44069     animateCollapse : function(){
44070         this.beforeSlide();
44071         this.el.setStyle("z-index", 20000);
44072         var anchor = this.getSlideAnchor();
44073         this.el.slideOut(anchor, {
44074             callback : function(){
44075                 this.el.setStyle("z-index", "");
44076                 this.collapsedEl.slideIn(anchor, {duration:.3});
44077                 this.afterSlide();
44078                 this.el.setLocation(-10000,-10000);
44079                 this.el.hide();
44080                 this.fireEvent("collapsed", this);
44081             },
44082             scope: this,
44083             block: true
44084         });
44085     },
44086
44087     animateExpand : function(){
44088         this.beforeSlide();
44089         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44090         this.el.setStyle("z-index", 20000);
44091         this.collapsedEl.hide({
44092             duration:.1
44093         });
44094         this.el.slideIn(this.getSlideAnchor(), {
44095             callback : function(){
44096                 this.el.setStyle("z-index", "");
44097                 this.afterSlide();
44098                 if(this.split){
44099                     this.split.el.show();
44100                 }
44101                 this.fireEvent("invalidated", this);
44102                 this.fireEvent("expanded", this);
44103             },
44104             scope: this,
44105             block: true
44106         });
44107     },
44108
44109     anchors : {
44110         "west" : "left",
44111         "east" : "right",
44112         "north" : "top",
44113         "south" : "bottom"
44114     },
44115
44116     sanchors : {
44117         "west" : "l",
44118         "east" : "r",
44119         "north" : "t",
44120         "south" : "b"
44121     },
44122
44123     canchors : {
44124         "west" : "tl-tr",
44125         "east" : "tr-tl",
44126         "north" : "tl-bl",
44127         "south" : "bl-tl"
44128     },
44129
44130     getAnchor : function(){
44131         return this.anchors[this.position];
44132     },
44133
44134     getCollapseAnchor : function(){
44135         return this.canchors[this.position];
44136     },
44137
44138     getSlideAnchor : function(){
44139         return this.sanchors[this.position];
44140     },
44141
44142     getAlignAdj : function(){
44143         var cm = this.cmargins;
44144         switch(this.position){
44145             case "west":
44146                 return [0, 0];
44147             break;
44148             case "east":
44149                 return [0, 0];
44150             break;
44151             case "north":
44152                 return [0, 0];
44153             break;
44154             case "south":
44155                 return [0, 0];
44156             break;
44157         }
44158     },
44159
44160     getExpandAdj : function(){
44161         var c = this.collapsedEl, cm = this.cmargins;
44162         switch(this.position){
44163             case "west":
44164                 return [-(cm.right+c.getWidth()+cm.left), 0];
44165             break;
44166             case "east":
44167                 return [cm.right+c.getWidth()+cm.left, 0];
44168             break;
44169             case "north":
44170                 return [0, -(cm.top+cm.bottom+c.getHeight())];
44171             break;
44172             case "south":
44173                 return [0, cm.top+cm.bottom+c.getHeight()];
44174             break;
44175         }
44176     }
44177 });/*
44178  * Based on:
44179  * Ext JS Library 1.1.1
44180  * Copyright(c) 2006-2007, Ext JS, LLC.
44181  *
44182  * Originally Released Under LGPL - original licence link has changed is not relivant.
44183  *
44184  * Fork - LGPL
44185  * <script type="text/javascript">
44186  */
44187 /*
44188  * These classes are private internal classes
44189  */
44190 Roo.CenterLayoutRegion = function(mgr, config){
44191     Roo.LayoutRegion.call(this, mgr, config, "center");
44192     this.visible = true;
44193     this.minWidth = config.minWidth || 20;
44194     this.minHeight = config.minHeight || 20;
44195 };
44196
44197 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
44198     hide : function(){
44199         // center panel can't be hidden
44200     },
44201     
44202     show : function(){
44203         // center panel can't be hidden
44204     },
44205     
44206     getMinWidth: function(){
44207         return this.minWidth;
44208     },
44209     
44210     getMinHeight: function(){
44211         return this.minHeight;
44212     }
44213 });
44214
44215
44216 Roo.NorthLayoutRegion = function(mgr, config){
44217     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
44218     if(this.split){
44219         this.split.placement = Roo.SplitBar.TOP;
44220         this.split.orientation = Roo.SplitBar.VERTICAL;
44221         this.split.el.addClass("x-layout-split-v");
44222     }
44223     var size = config.initialSize || config.height;
44224     if(typeof size != "undefined"){
44225         this.el.setHeight(size);
44226     }
44227 };
44228 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
44229     orientation: Roo.SplitBar.VERTICAL,
44230     getBox : function(){
44231         if(this.collapsed){
44232             return this.collapsedEl.getBox();
44233         }
44234         var box = this.el.getBox();
44235         if(this.split){
44236             box.height += this.split.el.getHeight();
44237         }
44238         return box;
44239     },
44240     
44241     updateBox : function(box){
44242         if(this.split && !this.collapsed){
44243             box.height -= this.split.el.getHeight();
44244             this.split.el.setLeft(box.x);
44245             this.split.el.setTop(box.y+box.height);
44246             this.split.el.setWidth(box.width);
44247         }
44248         if(this.collapsed){
44249             this.updateBody(box.width, null);
44250         }
44251         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44252     }
44253 });
44254
44255 Roo.SouthLayoutRegion = function(mgr, config){
44256     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
44257     if(this.split){
44258         this.split.placement = Roo.SplitBar.BOTTOM;
44259         this.split.orientation = Roo.SplitBar.VERTICAL;
44260         this.split.el.addClass("x-layout-split-v");
44261     }
44262     var size = config.initialSize || config.height;
44263     if(typeof size != "undefined"){
44264         this.el.setHeight(size);
44265     }
44266 };
44267 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
44268     orientation: Roo.SplitBar.VERTICAL,
44269     getBox : function(){
44270         if(this.collapsed){
44271             return this.collapsedEl.getBox();
44272         }
44273         var box = this.el.getBox();
44274         if(this.split){
44275             var sh = this.split.el.getHeight();
44276             box.height += sh;
44277             box.y -= sh;
44278         }
44279         return box;
44280     },
44281     
44282     updateBox : function(box){
44283         if(this.split && !this.collapsed){
44284             var sh = this.split.el.getHeight();
44285             box.height -= sh;
44286             box.y += sh;
44287             this.split.el.setLeft(box.x);
44288             this.split.el.setTop(box.y-sh);
44289             this.split.el.setWidth(box.width);
44290         }
44291         if(this.collapsed){
44292             this.updateBody(box.width, null);
44293         }
44294         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44295     }
44296 });
44297
44298 Roo.EastLayoutRegion = function(mgr, config){
44299     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
44300     if(this.split){
44301         this.split.placement = Roo.SplitBar.RIGHT;
44302         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44303         this.split.el.addClass("x-layout-split-h");
44304     }
44305     var size = config.initialSize || config.width;
44306     if(typeof size != "undefined"){
44307         this.el.setWidth(size);
44308     }
44309 };
44310 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
44311     orientation: Roo.SplitBar.HORIZONTAL,
44312     getBox : function(){
44313         if(this.collapsed){
44314             return this.collapsedEl.getBox();
44315         }
44316         var box = this.el.getBox();
44317         if(this.split){
44318             var sw = this.split.el.getWidth();
44319             box.width += sw;
44320             box.x -= sw;
44321         }
44322         return box;
44323     },
44324
44325     updateBox : function(box){
44326         if(this.split && !this.collapsed){
44327             var sw = this.split.el.getWidth();
44328             box.width -= sw;
44329             this.split.el.setLeft(box.x);
44330             this.split.el.setTop(box.y);
44331             this.split.el.setHeight(box.height);
44332             box.x += sw;
44333         }
44334         if(this.collapsed){
44335             this.updateBody(null, box.height);
44336         }
44337         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44338     }
44339 });
44340
44341 Roo.WestLayoutRegion = function(mgr, config){
44342     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
44343     if(this.split){
44344         this.split.placement = Roo.SplitBar.LEFT;
44345         this.split.orientation = Roo.SplitBar.HORIZONTAL;
44346         this.split.el.addClass("x-layout-split-h");
44347     }
44348     var size = config.initialSize || config.width;
44349     if(typeof size != "undefined"){
44350         this.el.setWidth(size);
44351     }
44352 };
44353 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
44354     orientation: Roo.SplitBar.HORIZONTAL,
44355     getBox : function(){
44356         if(this.collapsed){
44357             return this.collapsedEl.getBox();
44358         }
44359         var box = this.el.getBox();
44360         if(this.split){
44361             box.width += this.split.el.getWidth();
44362         }
44363         return box;
44364     },
44365     
44366     updateBox : function(box){
44367         if(this.split && !this.collapsed){
44368             var sw = this.split.el.getWidth();
44369             box.width -= sw;
44370             this.split.el.setLeft(box.x+box.width);
44371             this.split.el.setTop(box.y);
44372             this.split.el.setHeight(box.height);
44373         }
44374         if(this.collapsed){
44375             this.updateBody(null, box.height);
44376         }
44377         Roo.LayoutRegion.prototype.updateBox.call(this, box);
44378     }
44379 });
44380 /*
44381  * Based on:
44382  * Ext JS Library 1.1.1
44383  * Copyright(c) 2006-2007, Ext JS, LLC.
44384  *
44385  * Originally Released Under LGPL - original licence link has changed is not relivant.
44386  *
44387  * Fork - LGPL
44388  * <script type="text/javascript">
44389  */
44390  
44391  
44392 /*
44393  * Private internal class for reading and applying state
44394  */
44395 Roo.LayoutStateManager = function(layout){
44396      // default empty state
44397      this.state = {
44398         north: {},
44399         south: {},
44400         east: {},
44401         west: {}       
44402     };
44403 };
44404
44405 Roo.LayoutStateManager.prototype = {
44406     init : function(layout, provider){
44407         this.provider = provider;
44408         var state = provider.get(layout.id+"-layout-state");
44409         if(state){
44410             var wasUpdating = layout.isUpdating();
44411             if(!wasUpdating){
44412                 layout.beginUpdate();
44413             }
44414             for(var key in state){
44415                 if(typeof state[key] != "function"){
44416                     var rstate = state[key];
44417                     var r = layout.getRegion(key);
44418                     if(r && rstate){
44419                         if(rstate.size){
44420                             r.resizeTo(rstate.size);
44421                         }
44422                         if(rstate.collapsed == true){
44423                             r.collapse(true);
44424                         }else{
44425                             r.expand(null, true);
44426                         }
44427                     }
44428                 }
44429             }
44430             if(!wasUpdating){
44431                 layout.endUpdate();
44432             }
44433             this.state = state; 
44434         }
44435         this.layout = layout;
44436         layout.on("regionresized", this.onRegionResized, this);
44437         layout.on("regioncollapsed", this.onRegionCollapsed, this);
44438         layout.on("regionexpanded", this.onRegionExpanded, this);
44439     },
44440     
44441     storeState : function(){
44442         this.provider.set(this.layout.id+"-layout-state", this.state);
44443     },
44444     
44445     onRegionResized : function(region, newSize){
44446         this.state[region.getPosition()].size = newSize;
44447         this.storeState();
44448     },
44449     
44450     onRegionCollapsed : function(region){
44451         this.state[region.getPosition()].collapsed = true;
44452         this.storeState();
44453     },
44454     
44455     onRegionExpanded : function(region){
44456         this.state[region.getPosition()].collapsed = false;
44457         this.storeState();
44458     }
44459 };/*
44460  * Based on:
44461  * Ext JS Library 1.1.1
44462  * Copyright(c) 2006-2007, Ext JS, LLC.
44463  *
44464  * Originally Released Under LGPL - original licence link has changed is not relivant.
44465  *
44466  * Fork - LGPL
44467  * <script type="text/javascript">
44468  */
44469 /**
44470  * @class Roo.ContentPanel
44471  * @extends Roo.util.Observable
44472  * A basic ContentPanel element.
44473  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
44474  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
44475  * @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
44476  * @cfg {Boolean} closable True if the panel can be closed/removed
44477  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
44478  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
44479  * @cfg {Toolbar} toolbar A toolbar for this panel
44480  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
44481  * @cfg {String} title The title for this panel
44482  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
44483  * @cfg {String} url Calls {@link #setUrl} with this value
44484  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
44485  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
44486  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
44487  * @constructor
44488  * Create a new ContentPanel.
44489  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
44490  * @param {String/Object} config A string to set only the title or a config object
44491  * @param {String} content (optional) Set the HTML content for this panel
44492  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
44493  */
44494 Roo.ContentPanel = function(el, config, content){
44495     
44496      
44497     /*
44498     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
44499         config = el;
44500         el = Roo.id();
44501     }
44502     if (config && config.parentLayout) { 
44503         el = config.parentLayout.el.createChild(); 
44504     }
44505     */
44506     if(el.autoCreate){ // xtype is available if this is called from factory
44507         config = el;
44508         el = Roo.id();
44509     }
44510     this.el = Roo.get(el);
44511     if(!this.el && config && config.autoCreate){
44512         if(typeof config.autoCreate == "object"){
44513             if(!config.autoCreate.id){
44514                 config.autoCreate.id = config.id||el;
44515             }
44516             this.el = Roo.DomHelper.append(document.body,
44517                         config.autoCreate, true);
44518         }else{
44519             this.el = Roo.DomHelper.append(document.body,
44520                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
44521         }
44522     }
44523     this.closable = false;
44524     this.loaded = false;
44525     this.active = false;
44526     if(typeof config == "string"){
44527         this.title = config;
44528     }else{
44529         Roo.apply(this, config);
44530     }
44531     
44532     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
44533         this.wrapEl = this.el.wrap();    
44534         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
44535         
44536     }
44537     
44538     
44539     
44540     if(this.resizeEl){
44541         this.resizeEl = Roo.get(this.resizeEl, true);
44542     }else{
44543         this.resizeEl = this.el;
44544     }
44545     this.addEvents({
44546         /**
44547          * @event activate
44548          * Fires when this panel is activated. 
44549          * @param {Roo.ContentPanel} this
44550          */
44551         "activate" : true,
44552         /**
44553          * @event deactivate
44554          * Fires when this panel is activated. 
44555          * @param {Roo.ContentPanel} this
44556          */
44557         "deactivate" : true,
44558
44559         /**
44560          * @event resize
44561          * Fires when this panel is resized if fitToFrame is true.
44562          * @param {Roo.ContentPanel} this
44563          * @param {Number} width The width after any component adjustments
44564          * @param {Number} height The height after any component adjustments
44565          */
44566         "resize" : true
44567     });
44568     if(this.autoScroll){
44569         this.resizeEl.setStyle("overflow", "auto");
44570     }
44571     content = content || this.content;
44572     if(content){
44573         this.setContent(content);
44574     }
44575     if(config && config.url){
44576         this.setUrl(this.url, this.params, this.loadOnce);
44577     }
44578     
44579     
44580     
44581     Roo.ContentPanel.superclass.constructor.call(this);
44582 };
44583
44584 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
44585     tabTip:'',
44586     setRegion : function(region){
44587         this.region = region;
44588         if(region){
44589            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
44590         }else{
44591            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
44592         } 
44593     },
44594     
44595     /**
44596      * Returns the toolbar for this Panel if one was configured. 
44597      * @return {Roo.Toolbar} 
44598      */
44599     getToolbar : function(){
44600         return this.toolbar;
44601     },
44602     
44603     setActiveState : function(active){
44604         this.active = active;
44605         if(!active){
44606             this.fireEvent("deactivate", this);
44607         }else{
44608             this.fireEvent("activate", this);
44609         }
44610     },
44611     /**
44612      * Updates this panel's element
44613      * @param {String} content The new content
44614      * @param {Boolean} loadScripts (optional) true to look for and process scripts
44615     */
44616     setContent : function(content, loadScripts){
44617         this.el.update(content, loadScripts);
44618     },
44619
44620     ignoreResize : function(w, h){
44621         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
44622             return true;
44623         }else{
44624             this.lastSize = {width: w, height: h};
44625             return false;
44626         }
44627     },
44628     /**
44629      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
44630      * @return {Roo.UpdateManager} The UpdateManager
44631      */
44632     getUpdateManager : function(){
44633         return this.el.getUpdateManager();
44634     },
44635      /**
44636      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
44637      * @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:
44638 <pre><code>
44639 panel.load({
44640     url: "your-url.php",
44641     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
44642     callback: yourFunction,
44643     scope: yourObject, //(optional scope)
44644     discardUrl: false,
44645     nocache: false,
44646     text: "Loading...",
44647     timeout: 30,
44648     scripts: false
44649 });
44650 </code></pre>
44651      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
44652      * 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.
44653      * @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}
44654      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
44655      * @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.
44656      * @return {Roo.ContentPanel} this
44657      */
44658     load : function(){
44659         var um = this.el.getUpdateManager();
44660         um.update.apply(um, arguments);
44661         return this;
44662     },
44663
44664
44665     /**
44666      * 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.
44667      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
44668      * @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)
44669      * @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)
44670      * @return {Roo.UpdateManager} The UpdateManager
44671      */
44672     setUrl : function(url, params, loadOnce){
44673         if(this.refreshDelegate){
44674             this.removeListener("activate", this.refreshDelegate);
44675         }
44676         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
44677         this.on("activate", this.refreshDelegate);
44678         return this.el.getUpdateManager();
44679     },
44680     
44681     _handleRefresh : function(url, params, loadOnce){
44682         if(!loadOnce || !this.loaded){
44683             var updater = this.el.getUpdateManager();
44684             updater.update(url, params, this._setLoaded.createDelegate(this));
44685         }
44686     },
44687     
44688     _setLoaded : function(){
44689         this.loaded = true;
44690     }, 
44691     
44692     /**
44693      * Returns this panel's id
44694      * @return {String} 
44695      */
44696     getId : function(){
44697         return this.el.id;
44698     },
44699     
44700     /** 
44701      * Returns this panel's element - used by regiosn to add.
44702      * @return {Roo.Element} 
44703      */
44704     getEl : function(){
44705         return this.wrapEl || this.el;
44706     },
44707     
44708     adjustForComponents : function(width, height){
44709         if(this.resizeEl != this.el){
44710             width -= this.el.getFrameWidth('lr');
44711             height -= this.el.getFrameWidth('tb');
44712         }
44713         if(this.toolbar){
44714             var te = this.toolbar.getEl();
44715             height -= te.getHeight();
44716             te.setWidth(width);
44717         }
44718         if(this.adjustments){
44719             width += this.adjustments[0];
44720             height += this.adjustments[1];
44721         }
44722         return {"width": width, "height": height};
44723     },
44724     
44725     setSize : function(width, height){
44726         if(this.fitToFrame && !this.ignoreResize(width, height)){
44727             if(this.fitContainer && this.resizeEl != this.el){
44728                 this.el.setSize(width, height);
44729             }
44730             var size = this.adjustForComponents(width, height);
44731             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
44732             this.fireEvent('resize', this, size.width, size.height);
44733         }
44734     },
44735     
44736     /**
44737      * Returns this panel's title
44738      * @return {String} 
44739      */
44740     getTitle : function(){
44741         return this.title;
44742     },
44743     
44744     /**
44745      * Set this panel's title
44746      * @param {String} title
44747      */
44748     setTitle : function(title){
44749         this.title = title;
44750         if(this.region){
44751             this.region.updatePanelTitle(this, title);
44752         }
44753     },
44754     
44755     /**
44756      * Returns true is this panel was configured to be closable
44757      * @return {Boolean} 
44758      */
44759     isClosable : function(){
44760         return this.closable;
44761     },
44762     
44763     beforeSlide : function(){
44764         this.el.clip();
44765         this.resizeEl.clip();
44766     },
44767     
44768     afterSlide : function(){
44769         this.el.unclip();
44770         this.resizeEl.unclip();
44771     },
44772     
44773     /**
44774      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
44775      *   Will fail silently if the {@link #setUrl} method has not been called.
44776      *   This does not activate the panel, just updates its content.
44777      */
44778     refresh : function(){
44779         if(this.refreshDelegate){
44780            this.loaded = false;
44781            this.refreshDelegate();
44782         }
44783     },
44784     
44785     /**
44786      * Destroys this panel
44787      */
44788     destroy : function(){
44789         this.el.removeAllListeners();
44790         var tempEl = document.createElement("span");
44791         tempEl.appendChild(this.el.dom);
44792         tempEl.innerHTML = "";
44793         this.el.remove();
44794         this.el = null;
44795     },
44796     
44797       /**
44798      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
44799      * <pre><code>
44800
44801 layout.addxtype({
44802        xtype : 'Form',
44803        items: [ .... ]
44804    }
44805 );
44806
44807 </code></pre>
44808      * @param {Object} cfg Xtype definition of item to add.
44809      */
44810     
44811     addxtype : function(cfg) {
44812         // add form..
44813         if (cfg.xtype.match(/^Form$/)) {
44814             var el = this.el.createChild();
44815
44816             this.form = new  Roo.form.Form(cfg);
44817             
44818             
44819             if ( this.form.allItems.length) this.form.render(el.dom);
44820             return this.form;
44821         }
44822         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
44823             // views..
44824             cfg.el = this.el.appendChild(document.createElement("div"));
44825             // factory?
44826             var ret = new Roo[cfg.xtype](cfg);
44827             ret.render(false, ''); // render blank..
44828             return ret;
44829             
44830         }
44831         return false;
44832         
44833     }
44834 });
44835
44836 /**
44837  * @class Roo.GridPanel
44838  * @extends Roo.ContentPanel
44839  * @constructor
44840  * Create a new GridPanel.
44841  * @param {Roo.grid.Grid} grid The grid for this panel
44842  * @param {String/Object} config A string to set only the panel's title, or a config object
44843  */
44844 Roo.GridPanel = function(grid, config){
44845     
44846   
44847     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
44848         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
44849         
44850     this.wrapper.dom.appendChild(grid.getGridEl().dom);
44851     
44852     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
44853     
44854     if(this.toolbar){
44855         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
44856     }
44857     // xtype created footer. - not sure if will work as we normally have to render first..
44858     if (this.footer && !this.footer.el && this.footer.xtype) {
44859         
44860         this.footer.container = this.grid.getView().getFooterPanel(true);
44861         this.footer.dataSource = this.grid.dataSource;
44862         this.footer = Roo.factory(this.footer, Roo);
44863         
44864     }
44865     
44866     grid.monitorWindowResize = false; // turn off autosizing
44867     grid.autoHeight = false;
44868     grid.autoWidth = false;
44869     this.grid = grid;
44870     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
44871 };
44872
44873 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
44874     getId : function(){
44875         return this.grid.id;
44876     },
44877     
44878     /**
44879      * Returns the grid for this panel
44880      * @return {Roo.grid.Grid} 
44881      */
44882     getGrid : function(){
44883         return this.grid;    
44884     },
44885     
44886     setSize : function(width, height){
44887         if(!this.ignoreResize(width, height)){
44888             var grid = this.grid;
44889             var size = this.adjustForComponents(width, height);
44890             grid.getGridEl().setSize(size.width, size.height);
44891             grid.autoSize();
44892         }
44893     },
44894     
44895     beforeSlide : function(){
44896         this.grid.getView().scroller.clip();
44897     },
44898     
44899     afterSlide : function(){
44900         this.grid.getView().scroller.unclip();
44901     },
44902     
44903     destroy : function(){
44904         this.grid.destroy();
44905         delete this.grid;
44906         Roo.GridPanel.superclass.destroy.call(this); 
44907     }
44908 });
44909
44910
44911 /**
44912  * @class Roo.NestedLayoutPanel
44913  * @extends Roo.ContentPanel
44914  * @constructor
44915  * Create a new NestedLayoutPanel.
44916  * 
44917  * 
44918  * @param {Roo.BorderLayout} layout The layout for this panel
44919  * @param {String/Object} config A string to set only the title or a config object
44920  */
44921 Roo.NestedLayoutPanel = function(layout, config)
44922 {
44923     // construct with only one argument..
44924     /* FIXME - implement nicer consturctors
44925     if (layout.layout) {
44926         config = layout;
44927         layout = config.layout;
44928         delete config.layout;
44929     }
44930     if (layout.xtype && !layout.getEl) {
44931         // then layout needs constructing..
44932         layout = Roo.factory(layout, Roo);
44933     }
44934     */
44935     
44936     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
44937     
44938     layout.monitorWindowResize = false; // turn off autosizing
44939     this.layout = layout;
44940     this.layout.getEl().addClass("x-layout-nested-layout");
44941     
44942     
44943     
44944 };
44945
44946 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
44947
44948     setSize : function(width, height){
44949         if(!this.ignoreResize(width, height)){
44950             var size = this.adjustForComponents(width, height);
44951             var el = this.layout.getEl();
44952             el.setSize(size.width, size.height);
44953             var touch = el.dom.offsetWidth;
44954             this.layout.layout();
44955             // ie requires a double layout on the first pass
44956             if(Roo.isIE && !this.initialized){
44957                 this.initialized = true;
44958                 this.layout.layout();
44959             }
44960         }
44961     },
44962     
44963     // activate all subpanels if not currently active..
44964     
44965     setActiveState : function(active){
44966         this.active = active;
44967         if(!active){
44968             this.fireEvent("deactivate", this);
44969             return;
44970         }
44971         
44972         this.fireEvent("activate", this);
44973         // not sure if this should happen before or after..
44974         if (!this.layout) {
44975             return; // should not happen..
44976         }
44977         var reg = false;
44978         for (var r in this.layout.regions) {
44979             reg = this.layout.getRegion(r);
44980             if (reg.getActivePanel()) {
44981                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
44982                 reg.setActivePanel(reg.getActivePanel());
44983                 continue;
44984             }
44985             if (!reg.panels.length) {
44986                 continue;
44987             }
44988             reg.showPanel(reg.getPanel(0));
44989         }
44990         
44991         
44992         
44993         
44994     },
44995     
44996     /**
44997      * Returns the nested BorderLayout for this panel
44998      * @return {Roo.BorderLayout} 
44999      */
45000     getLayout : function(){
45001         return this.layout;
45002     },
45003     
45004      /**
45005      * Adds a xtype elements to the layout of the nested panel
45006      * <pre><code>
45007
45008 panel.addxtype({
45009        xtype : 'ContentPanel',
45010        region: 'west',
45011        items: [ .... ]
45012    }
45013 );
45014
45015 panel.addxtype({
45016         xtype : 'NestedLayoutPanel',
45017         region: 'west',
45018         layout: {
45019            center: { },
45020            west: { }   
45021         },
45022         items : [ ... list of content panels or nested layout panels.. ]
45023    }
45024 );
45025 </code></pre>
45026      * @param {Object} cfg Xtype definition of item to add.
45027      */
45028     addxtype : function(cfg) {
45029         return this.layout.addxtype(cfg);
45030     
45031     }
45032 });
45033
45034 Roo.ScrollPanel = function(el, config, content){
45035     config = config || {};
45036     config.fitToFrame = true;
45037     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45038     
45039     this.el.dom.style.overflow = "hidden";
45040     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45041     this.el.removeClass("x-layout-inactive-content");
45042     this.el.on("mousewheel", this.onWheel, this);
45043
45044     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45045     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45046     up.unselectable(); down.unselectable();
45047     up.on("click", this.scrollUp, this);
45048     down.on("click", this.scrollDown, this);
45049     up.addClassOnOver("x-scroller-btn-over");
45050     down.addClassOnOver("x-scroller-btn-over");
45051     up.addClassOnClick("x-scroller-btn-click");
45052     down.addClassOnClick("x-scroller-btn-click");
45053     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45054
45055     this.resizeEl = this.el;
45056     this.el = wrap; this.up = up; this.down = down;
45057 };
45058
45059 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45060     increment : 100,
45061     wheelIncrement : 5,
45062     scrollUp : function(){
45063         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45064     },
45065
45066     scrollDown : function(){
45067         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45068     },
45069
45070     afterScroll : function(){
45071         var el = this.resizeEl;
45072         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45073         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45074         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45075     },
45076
45077     setSize : function(){
45078         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45079         this.afterScroll();
45080     },
45081
45082     onWheel : function(e){
45083         var d = e.getWheelDelta();
45084         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45085         this.afterScroll();
45086         e.stopEvent();
45087     },
45088
45089     setContent : function(content, loadScripts){
45090         this.resizeEl.update(content, loadScripts);
45091     }
45092
45093 });
45094
45095
45096
45097
45098
45099
45100
45101
45102
45103 /**
45104  * @class Roo.TreePanel
45105  * @extends Roo.ContentPanel
45106  * @constructor
45107  * Create a new TreePanel. - defaults to fit/scoll contents.
45108  * @param {String/Object} config A string to set only the panel's title, or a config object
45109  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45110  */
45111 Roo.TreePanel = function(config){
45112     var el = config.el;
45113     var tree = config.tree;
45114     delete config.tree; 
45115     delete config.el; // hopefull!
45116     
45117     // wrapper for IE7 strict & safari scroll issue
45118     
45119     var treeEl = el.createChild();
45120     config.resizeEl = treeEl;
45121     
45122     
45123     
45124     Roo.TreePanel.superclass.constructor.call(this, el, config);
45125  
45126  
45127     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45128     //console.log(tree);
45129     this.on('activate', function()
45130     {
45131         if (this.tree.rendered) {
45132             return;
45133         }
45134         //console.log('render tree');
45135         this.tree.render();
45136     });
45137     
45138     this.on('resize',  function (cp, w, h) {
45139             this.tree.innerCt.setWidth(w);
45140             this.tree.innerCt.setHeight(h);
45141             this.tree.innerCt.setStyle('overflow-y', 'auto');
45142     });
45143
45144         
45145     
45146 };
45147
45148 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
45149     fitToFrame : true,
45150     autoScroll : true
45151 });
45152
45153
45154
45155
45156
45157
45158
45159
45160
45161
45162
45163 /*
45164  * Based on:
45165  * Ext JS Library 1.1.1
45166  * Copyright(c) 2006-2007, Ext JS, LLC.
45167  *
45168  * Originally Released Under LGPL - original licence link has changed is not relivant.
45169  *
45170  * Fork - LGPL
45171  * <script type="text/javascript">
45172  */
45173  
45174
45175 /**
45176  * @class Roo.ReaderLayout
45177  * @extends Roo.BorderLayout
45178  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
45179  * center region containing two nested regions (a top one for a list view and one for item preview below),
45180  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
45181  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
45182  * expedites the setup of the overall layout and regions for this common application style.
45183  * Example:
45184  <pre><code>
45185 var reader = new Roo.ReaderLayout();
45186 var CP = Roo.ContentPanel;  // shortcut for adding
45187
45188 reader.beginUpdate();
45189 reader.add("north", new CP("north", "North"));
45190 reader.add("west", new CP("west", {title: "West"}));
45191 reader.add("east", new CP("east", {title: "East"}));
45192
45193 reader.regions.listView.add(new CP("listView", "List"));
45194 reader.regions.preview.add(new CP("preview", "Preview"));
45195 reader.endUpdate();
45196 </code></pre>
45197 * @constructor
45198 * Create a new ReaderLayout
45199 * @param {Object} config Configuration options
45200 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
45201 * document.body if omitted)
45202 */
45203 Roo.ReaderLayout = function(config, renderTo){
45204     var c = config || {size:{}};
45205     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
45206         north: c.north !== false ? Roo.apply({
45207             split:false,
45208             initialSize: 32,
45209             titlebar: false
45210         }, c.north) : false,
45211         west: c.west !== false ? Roo.apply({
45212             split:true,
45213             initialSize: 200,
45214             minSize: 175,
45215             maxSize: 400,
45216             titlebar: true,
45217             collapsible: true,
45218             animate: true,
45219             margins:{left:5,right:0,bottom:5,top:5},
45220             cmargins:{left:5,right:5,bottom:5,top:5}
45221         }, c.west) : false,
45222         east: c.east !== false ? Roo.apply({
45223             split:true,
45224             initialSize: 200,
45225             minSize: 175,
45226             maxSize: 400,
45227             titlebar: true,
45228             collapsible: true,
45229             animate: true,
45230             margins:{left:0,right:5,bottom:5,top:5},
45231             cmargins:{left:5,right:5,bottom:5,top:5}
45232         }, c.east) : false,
45233         center: Roo.apply({
45234             tabPosition: 'top',
45235             autoScroll:false,
45236             closeOnTab: true,
45237             titlebar:false,
45238             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
45239         }, c.center)
45240     });
45241
45242     this.el.addClass('x-reader');
45243
45244     this.beginUpdate();
45245
45246     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
45247         south: c.preview !== false ? Roo.apply({
45248             split:true,
45249             initialSize: 200,
45250             minSize: 100,
45251             autoScroll:true,
45252             collapsible:true,
45253             titlebar: true,
45254             cmargins:{top:5,left:0, right:0, bottom:0}
45255         }, c.preview) : false,
45256         center: Roo.apply({
45257             autoScroll:false,
45258             titlebar:false,
45259             minHeight:200
45260         }, c.listView)
45261     });
45262     this.add('center', new Roo.NestedLayoutPanel(inner,
45263             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
45264
45265     this.endUpdate();
45266
45267     this.regions.preview = inner.getRegion('south');
45268     this.regions.listView = inner.getRegion('center');
45269 };
45270
45271 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
45272  * Based on:
45273  * Ext JS Library 1.1.1
45274  * Copyright(c) 2006-2007, Ext JS, LLC.
45275  *
45276  * Originally Released Under LGPL - original licence link has changed is not relivant.
45277  *
45278  * Fork - LGPL
45279  * <script type="text/javascript">
45280  */
45281  
45282 /**
45283  * @class Roo.grid.Grid
45284  * @extends Roo.util.Observable
45285  * This class represents the primary interface of a component based grid control.
45286  * <br><br>Usage:<pre><code>
45287  var grid = new Roo.grid.Grid("my-container-id", {
45288      ds: myDataStore,
45289      cm: myColModel,
45290      selModel: mySelectionModel,
45291      autoSizeColumns: true,
45292      monitorWindowResize: false,
45293      trackMouseOver: true
45294  });
45295  // set any options
45296  grid.render();
45297  * </code></pre>
45298  * <b>Common Problems:</b><br/>
45299  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
45300  * element will correct this<br/>
45301  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
45302  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
45303  * are unpredictable.<br/>
45304  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
45305  * grid to calculate dimensions/offsets.<br/>
45306   * @constructor
45307  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
45308  * The container MUST have some type of size defined for the grid to fill. The container will be
45309  * automatically set to position relative if it isn't already.
45310  * @param {Object} config A config object that sets properties on this grid.
45311  */
45312 Roo.grid.Grid = function(container, config){
45313         // initialize the container
45314         this.container = Roo.get(container);
45315         this.container.update("");
45316         this.container.setStyle("overflow", "hidden");
45317     this.container.addClass('x-grid-container');
45318
45319     this.id = this.container.id;
45320
45321     Roo.apply(this, config);
45322     // check and correct shorthanded configs
45323     if(this.ds){
45324         this.dataSource = this.ds;
45325         delete this.ds;
45326     }
45327     if(this.cm){
45328         this.colModel = this.cm;
45329         delete this.cm;
45330     }
45331     if(this.sm){
45332         this.selModel = this.sm;
45333         delete this.sm;
45334     }
45335
45336     if (this.selModel) {
45337         this.selModel = Roo.factory(this.selModel, Roo.grid);
45338         this.sm = this.selModel;
45339         this.sm.xmodule = this.xmodule || false;
45340     }
45341     if (typeof(this.colModel.config) == 'undefined') {
45342         this.colModel = new Roo.grid.ColumnModel(this.colModel);
45343         this.cm = this.colModel;
45344         this.cm.xmodule = this.xmodule || false;
45345     }
45346     if (this.dataSource) {
45347         this.dataSource= Roo.factory(this.dataSource, Roo.data);
45348         this.ds = this.dataSource;
45349         this.ds.xmodule = this.xmodule || false;
45350         
45351     }
45352     
45353     
45354     
45355     if(this.width){
45356         this.container.setWidth(this.width);
45357     }
45358
45359     if(this.height){
45360         this.container.setHeight(this.height);
45361     }
45362     /** @private */
45363         this.addEvents({
45364             // raw events
45365             /**
45366              * @event click
45367              * The raw click event for the entire grid.
45368              * @param {Roo.EventObject} e
45369              */
45370             "click" : true,
45371             /**
45372              * @event dblclick
45373              * The raw dblclick event for the entire grid.
45374              * @param {Roo.EventObject} e
45375              */
45376             "dblclick" : true,
45377             /**
45378              * @event contextmenu
45379              * The raw contextmenu event for the entire grid.
45380              * @param {Roo.EventObject} e
45381              */
45382             "contextmenu" : true,
45383             /**
45384              * @event mousedown
45385              * The raw mousedown event for the entire grid.
45386              * @param {Roo.EventObject} e
45387              */
45388             "mousedown" : true,
45389             /**
45390              * @event mouseup
45391              * The raw mouseup event for the entire grid.
45392              * @param {Roo.EventObject} e
45393              */
45394             "mouseup" : true,
45395             /**
45396              * @event mouseover
45397              * The raw mouseover event for the entire grid.
45398              * @param {Roo.EventObject} e
45399              */
45400             "mouseover" : true,
45401             /**
45402              * @event mouseout
45403              * The raw mouseout event for the entire grid.
45404              * @param {Roo.EventObject} e
45405              */
45406             "mouseout" : true,
45407             /**
45408              * @event keypress
45409              * The raw keypress event for the entire grid.
45410              * @param {Roo.EventObject} e
45411              */
45412             "keypress" : true,
45413             /**
45414              * @event keydown
45415              * The raw keydown event for the entire grid.
45416              * @param {Roo.EventObject} e
45417              */
45418             "keydown" : true,
45419
45420             // custom events
45421
45422             /**
45423              * @event cellclick
45424              * Fires when a cell is clicked
45425              * @param {Grid} this
45426              * @param {Number} rowIndex
45427              * @param {Number} columnIndex
45428              * @param {Roo.EventObject} e
45429              */
45430             "cellclick" : true,
45431             /**
45432              * @event celldblclick
45433              * Fires when a cell is double clicked
45434              * @param {Grid} this
45435              * @param {Number} rowIndex
45436              * @param {Number} columnIndex
45437              * @param {Roo.EventObject} e
45438              */
45439             "celldblclick" : true,
45440             /**
45441              * @event rowclick
45442              * Fires when a row is clicked
45443              * @param {Grid} this
45444              * @param {Number} rowIndex
45445              * @param {Roo.EventObject} e
45446              */
45447             "rowclick" : true,
45448             /**
45449              * @event rowdblclick
45450              * Fires when a row is double clicked
45451              * @param {Grid} this
45452              * @param {Number} rowIndex
45453              * @param {Roo.EventObject} e
45454              */
45455             "rowdblclick" : true,
45456             /**
45457              * @event headerclick
45458              * Fires when a header is clicked
45459              * @param {Grid} this
45460              * @param {Number} columnIndex
45461              * @param {Roo.EventObject} e
45462              */
45463             "headerclick" : true,
45464             /**
45465              * @event headerdblclick
45466              * Fires when a header cell is double clicked
45467              * @param {Grid} this
45468              * @param {Number} columnIndex
45469              * @param {Roo.EventObject} e
45470              */
45471             "headerdblclick" : true,
45472             /**
45473              * @event rowcontextmenu
45474              * Fires when a row is right clicked
45475              * @param {Grid} this
45476              * @param {Number} rowIndex
45477              * @param {Roo.EventObject} e
45478              */
45479             "rowcontextmenu" : true,
45480             /**
45481          * @event cellcontextmenu
45482          * Fires when a cell is right clicked
45483          * @param {Grid} this
45484          * @param {Number} rowIndex
45485          * @param {Number} cellIndex
45486          * @param {Roo.EventObject} e
45487          */
45488          "cellcontextmenu" : true,
45489             /**
45490              * @event headercontextmenu
45491              * Fires when a header is right clicked
45492              * @param {Grid} this
45493              * @param {Number} columnIndex
45494              * @param {Roo.EventObject} e
45495              */
45496             "headercontextmenu" : true,
45497             /**
45498              * @event bodyscroll
45499              * Fires when the body element is scrolled
45500              * @param {Number} scrollLeft
45501              * @param {Number} scrollTop
45502              */
45503             "bodyscroll" : true,
45504             /**
45505              * @event columnresize
45506              * Fires when the user resizes a column
45507              * @param {Number} columnIndex
45508              * @param {Number} newSize
45509              */
45510             "columnresize" : true,
45511             /**
45512              * @event columnmove
45513              * Fires when the user moves a column
45514              * @param {Number} oldIndex
45515              * @param {Number} newIndex
45516              */
45517             "columnmove" : true,
45518             /**
45519              * @event startdrag
45520              * Fires when row(s) start being dragged
45521              * @param {Grid} this
45522              * @param {Roo.GridDD} dd The drag drop object
45523              * @param {event} e The raw browser event
45524              */
45525             "startdrag" : true,
45526             /**
45527              * @event enddrag
45528              * Fires when a drag operation is complete
45529              * @param {Grid} this
45530              * @param {Roo.GridDD} dd The drag drop object
45531              * @param {event} e The raw browser event
45532              */
45533             "enddrag" : true,
45534             /**
45535              * @event dragdrop
45536              * Fires when dragged row(s) are dropped on a valid DD target
45537              * @param {Grid} this
45538              * @param {Roo.GridDD} dd The drag drop object
45539              * @param {String} targetId The target drag drop object
45540              * @param {event} e The raw browser event
45541              */
45542             "dragdrop" : true,
45543             /**
45544              * @event dragover
45545              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
45546              * @param {Grid} this
45547              * @param {Roo.GridDD} dd The drag drop object
45548              * @param {String} targetId The target drag drop object
45549              * @param {event} e The raw browser event
45550              */
45551             "dragover" : true,
45552             /**
45553              * @event dragenter
45554              *  Fires when the dragged row(s) first cross another DD target while being dragged
45555              * @param {Grid} this
45556              * @param {Roo.GridDD} dd The drag drop object
45557              * @param {String} targetId The target drag drop object
45558              * @param {event} e The raw browser event
45559              */
45560             "dragenter" : true,
45561             /**
45562              * @event dragout
45563              * Fires when the dragged row(s) leave another DD target while being dragged
45564              * @param {Grid} this
45565              * @param {Roo.GridDD} dd The drag drop object
45566              * @param {String} targetId The target drag drop object
45567              * @param {event} e The raw browser event
45568              */
45569             "dragout" : true,
45570         /**
45571          * @event render
45572          * Fires when the grid is rendered
45573          * @param {Grid} grid
45574          */
45575         render : true
45576     });
45577
45578     Roo.grid.Grid.superclass.constructor.call(this);
45579 };
45580 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
45581     
45582     /**
45583      * @cfg {String} ddGroup - drag drop group.
45584          */
45585     
45586     /**
45587      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
45588          */
45589         minColumnWidth : 25,
45590
45591     /**
45592          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
45593          * <b>on initial render.</b> It is more efficient to explicitly size the columns
45594          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
45595          */
45596         autoSizeColumns : false,
45597
45598         /**
45599          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
45600          */
45601         autoSizeHeaders : true,
45602
45603         /**
45604          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
45605          */
45606         monitorWindowResize : true,
45607
45608         /**
45609          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
45610          * rows measured to get a columns size. Default is 0 (all rows).
45611          */
45612         maxRowsToMeasure : 0,
45613
45614         /**
45615          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
45616          */
45617         trackMouseOver : true,
45618
45619     /**
45620          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
45621          */
45622     
45623         /**
45624          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
45625          */
45626         enableDragDrop : false,
45627
45628         /**
45629          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
45630          */
45631         enableColumnMove : true,
45632
45633         /**
45634          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
45635          */
45636         enableColumnHide : true,
45637
45638         /**
45639          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
45640          */
45641         enableRowHeightSync : false,
45642
45643         /**
45644          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
45645          */
45646         stripeRows : true,
45647
45648         /**
45649          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
45650          */
45651         autoHeight : false,
45652
45653     /**
45654      * @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.
45655      */
45656     autoExpandColumn : false,
45657
45658     /**
45659     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
45660     * Default is 50.
45661     */
45662     autoExpandMin : 50,
45663
45664     /**
45665     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
45666     */
45667     autoExpandMax : 1000,
45668
45669     /**
45670          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
45671          */
45672         view : null,
45673
45674         /**
45675      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
45676          */
45677         loadMask : false,
45678
45679     // private
45680     rendered : false,
45681
45682     /**
45683     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
45684     * of a fixed width. Default is false.
45685     */
45686     /**
45687     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
45688     */
45689     /**
45690      * Called once after all setup has been completed and the grid is ready to be rendered.
45691      * @return {Roo.grid.Grid} this
45692      */
45693     render : function(){
45694         var c = this.container;
45695         // try to detect autoHeight/width mode
45696         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
45697             this.autoHeight = true;
45698         }
45699         var view = this.getView();
45700         view.init(this);
45701
45702         c.on("click", this.onClick, this);
45703         c.on("dblclick", this.onDblClick, this);
45704         c.on("contextmenu", this.onContextMenu, this);
45705         c.on("keydown", this.onKeyDown, this);
45706
45707         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
45708
45709         this.getSelectionModel().init(this);
45710
45711         view.render();
45712
45713         if(this.loadMask){
45714             this.loadMask = new Roo.LoadMask(this.container,
45715                     Roo.apply({store:this.dataSource}, this.loadMask));
45716         }
45717         
45718         
45719         if (this.toolbar && this.toolbar.xtype) {
45720             this.toolbar.container = this.getView().getHeaderPanel(true);
45721             this.toolbar = new Ext.Toolbar(this.toolbar);
45722         }
45723         if (this.footer && this.footer.xtype) {
45724             this.footer.dataSource = this.getDataSource();
45725             this.footer.container = this.getView().getFooterPanel(true);
45726             this.footer = Roo.factory(this.footer, Roo);
45727         }
45728         this.rendered = true;
45729         this.fireEvent('render', this);
45730         return this;
45731     },
45732
45733         /**
45734          * Reconfigures the grid to use a different Store and Column Model.
45735          * The View will be bound to the new objects and refreshed.
45736          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
45737          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
45738          */
45739     reconfigure : function(dataSource, colModel){
45740         if(this.loadMask){
45741             this.loadMask.destroy();
45742             this.loadMask = new Roo.LoadMask(this.container,
45743                     Roo.apply({store:dataSource}, this.loadMask));
45744         }
45745         this.view.bind(dataSource, colModel);
45746         this.dataSource = dataSource;
45747         this.colModel = colModel;
45748         this.view.refresh(true);
45749     },
45750
45751     // private
45752     onKeyDown : function(e){
45753         this.fireEvent("keydown", e);
45754     },
45755
45756     /**
45757      * Destroy this grid.
45758      * @param {Boolean} removeEl True to remove the element
45759      */
45760     destroy : function(removeEl, keepListeners){
45761         if(this.loadMask){
45762             this.loadMask.destroy();
45763         }
45764         var c = this.container;
45765         c.removeAllListeners();
45766         this.view.destroy();
45767         this.colModel.purgeListeners();
45768         if(!keepListeners){
45769             this.purgeListeners();
45770         }
45771         c.update("");
45772         if(removeEl === true){
45773             c.remove();
45774         }
45775     },
45776
45777     // private
45778     processEvent : function(name, e){
45779         this.fireEvent(name, e);
45780         var t = e.getTarget();
45781         var v = this.view;
45782         var header = v.findHeaderIndex(t);
45783         if(header !== false){
45784             this.fireEvent("header" + name, this, header, e);
45785         }else{
45786             var row = v.findRowIndex(t);
45787             var cell = v.findCellIndex(t);
45788             if(row !== false){
45789                 this.fireEvent("row" + name, this, row, e);
45790                 if(cell !== false){
45791                     this.fireEvent("cell" + name, this, row, cell, e);
45792                 }
45793             }
45794         }
45795     },
45796
45797     // private
45798     onClick : function(e){
45799         this.processEvent("click", e);
45800     },
45801
45802     // private
45803     onContextMenu : function(e, t){
45804         this.processEvent("contextmenu", e);
45805     },
45806
45807     // private
45808     onDblClick : function(e){
45809         this.processEvent("dblclick", e);
45810     },
45811
45812     // private
45813     walkCells : function(row, col, step, fn, scope){
45814         var cm = this.colModel, clen = cm.getColumnCount();
45815         var ds = this.dataSource, rlen = ds.getCount(), first = true;
45816         if(step < 0){
45817             if(col < 0){
45818                 row--;
45819                 first = false;
45820             }
45821             while(row >= 0){
45822                 if(!first){
45823                     col = clen-1;
45824                 }
45825                 first = false;
45826                 while(col >= 0){
45827                     if(fn.call(scope || this, row, col, cm) === true){
45828                         return [row, col];
45829                     }
45830                     col--;
45831                 }
45832                 row--;
45833             }
45834         } else {
45835             if(col >= clen){
45836                 row++;
45837                 first = false;
45838             }
45839             while(row < rlen){
45840                 if(!first){
45841                     col = 0;
45842                 }
45843                 first = false;
45844                 while(col < clen){
45845                     if(fn.call(scope || this, row, col, cm) === true){
45846                         return [row, col];
45847                     }
45848                     col++;
45849                 }
45850                 row++;
45851             }
45852         }
45853         return null;
45854     },
45855
45856     // private
45857     getSelections : function(){
45858         return this.selModel.getSelections();
45859     },
45860
45861     /**
45862      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
45863      * but if manual update is required this method will initiate it.
45864      */
45865     autoSize : function(){
45866         if(this.rendered){
45867             this.view.layout();
45868             if(this.view.adjustForScroll){
45869                 this.view.adjustForScroll();
45870             }
45871         }
45872     },
45873
45874     /**
45875      * Returns the grid's underlying element.
45876      * @return {Element} The element
45877      */
45878     getGridEl : function(){
45879         return this.container;
45880     },
45881
45882     // private for compatibility, overridden by editor grid
45883     stopEditing : function(){},
45884
45885     /**
45886      * Returns the grid's SelectionModel.
45887      * @return {SelectionModel}
45888      */
45889     getSelectionModel : function(){
45890         if(!this.selModel){
45891             this.selModel = new Roo.grid.RowSelectionModel();
45892         }
45893         return this.selModel;
45894     },
45895
45896     /**
45897      * Returns the grid's DataSource.
45898      * @return {DataSource}
45899      */
45900     getDataSource : function(){
45901         return this.dataSource;
45902     },
45903
45904     /**
45905      * Returns the grid's ColumnModel.
45906      * @return {ColumnModel}
45907      */
45908     getColumnModel : function(){
45909         return this.colModel;
45910     },
45911
45912     /**
45913      * Returns the grid's GridView object.
45914      * @return {GridView}
45915      */
45916     getView : function(){
45917         if(!this.view){
45918             this.view = new Roo.grid.GridView(this.viewConfig);
45919         }
45920         return this.view;
45921     },
45922     /**
45923      * Called to get grid's drag proxy text, by default returns this.ddText.
45924      * @return {String}
45925      */
45926     getDragDropText : function(){
45927         var count = this.selModel.getCount();
45928         return String.format(this.ddText, count, count == 1 ? '' : 's');
45929     }
45930 });
45931 /**
45932  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
45933  * %0 is replaced with the number of selected rows.
45934  * @type String
45935  */
45936 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
45937  * Based on:
45938  * Ext JS Library 1.1.1
45939  * Copyright(c) 2006-2007, Ext JS, LLC.
45940  *
45941  * Originally Released Under LGPL - original licence link has changed is not relivant.
45942  *
45943  * Fork - LGPL
45944  * <script type="text/javascript">
45945  */
45946  
45947 Roo.grid.AbstractGridView = function(){
45948         this.grid = null;
45949         
45950         this.events = {
45951             "beforerowremoved" : true,
45952             "beforerowsinserted" : true,
45953             "beforerefresh" : true,
45954             "rowremoved" : true,
45955             "rowsinserted" : true,
45956             "rowupdated" : true,
45957             "refresh" : true
45958         };
45959     Roo.grid.AbstractGridView.superclass.constructor.call(this);
45960 };
45961
45962 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
45963     rowClass : "x-grid-row",
45964     cellClass : "x-grid-cell",
45965     tdClass : "x-grid-td",
45966     hdClass : "x-grid-hd",
45967     splitClass : "x-grid-hd-split",
45968     
45969         init: function(grid){
45970         this.grid = grid;
45971                 var cid = this.grid.getGridEl().id;
45972         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
45973         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
45974         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
45975         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
45976         },
45977         
45978         getColumnRenderers : function(){
45979         var renderers = [];
45980         var cm = this.grid.colModel;
45981         var colCount = cm.getColumnCount();
45982         for(var i = 0; i < colCount; i++){
45983             renderers[i] = cm.getRenderer(i);
45984         }
45985         return renderers;
45986     },
45987     
45988     getColumnIds : function(){
45989         var ids = [];
45990         var cm = this.grid.colModel;
45991         var colCount = cm.getColumnCount();
45992         for(var i = 0; i < colCount; i++){
45993             ids[i] = cm.getColumnId(i);
45994         }
45995         return ids;
45996     },
45997     
45998     getDataIndexes : function(){
45999         if(!this.indexMap){
46000             this.indexMap = this.buildIndexMap();
46001         }
46002         return this.indexMap.colToData;
46003     },
46004     
46005     getColumnIndexByDataIndex : function(dataIndex){
46006         if(!this.indexMap){
46007             this.indexMap = this.buildIndexMap();
46008         }
46009         return this.indexMap.dataToCol[dataIndex];
46010     },
46011     
46012     /**
46013      * Set a css style for a column dynamically. 
46014      * @param {Number} colIndex The index of the column
46015      * @param {String} name The css property name
46016      * @param {String} value The css value
46017      */
46018     setCSSStyle : function(colIndex, name, value){
46019         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46020         Roo.util.CSS.updateRule(selector, name, value);
46021     },
46022     
46023     generateRules : function(cm){
46024         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46025         Roo.util.CSS.removeStyleSheet(rulesId);
46026         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46027             var cid = cm.getColumnId(i);
46028             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46029                          this.tdSelector, cid, " {\n}\n",
46030                          this.hdSelector, cid, " {\n}\n",
46031                          this.splitSelector, cid, " {\n}\n");
46032         }
46033         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46034     }
46035 });/*
46036  * Based on:
46037  * Ext JS Library 1.1.1
46038  * Copyright(c) 2006-2007, Ext JS, LLC.
46039  *
46040  * Originally Released Under LGPL - original licence link has changed is not relivant.
46041  *
46042  * Fork - LGPL
46043  * <script type="text/javascript">
46044  */
46045
46046 // private
46047 // This is a support class used internally by the Grid components
46048 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46049     this.grid = grid;
46050     this.view = grid.getView();
46051     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46052     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46053     if(hd2){
46054         this.setHandleElId(Roo.id(hd));
46055         this.setOuterHandleElId(Roo.id(hd2));
46056     }
46057     this.scroll = false;
46058 };
46059 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46060     maxDragWidth: 120,
46061     getDragData : function(e){
46062         var t = Roo.lib.Event.getTarget(e);
46063         var h = this.view.findHeaderCell(t);
46064         if(h){
46065             return {ddel: h.firstChild, header:h};
46066         }
46067         return false;
46068     },
46069
46070     onInitDrag : function(e){
46071         this.view.headersDisabled = true;
46072         var clone = this.dragData.ddel.cloneNode(true);
46073         clone.id = Roo.id();
46074         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46075         this.proxy.update(clone);
46076         return true;
46077     },
46078
46079     afterValidDrop : function(){
46080         var v = this.view;
46081         setTimeout(function(){
46082             v.headersDisabled = false;
46083         }, 50);
46084     },
46085
46086     afterInvalidDrop : function(){
46087         var v = this.view;
46088         setTimeout(function(){
46089             v.headersDisabled = false;
46090         }, 50);
46091     }
46092 });
46093 /*
46094  * Based on:
46095  * Ext JS Library 1.1.1
46096  * Copyright(c) 2006-2007, Ext JS, LLC.
46097  *
46098  * Originally Released Under LGPL - original licence link has changed is not relivant.
46099  *
46100  * Fork - LGPL
46101  * <script type="text/javascript">
46102  */
46103 // private
46104 // This is a support class used internally by the Grid components
46105 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46106     this.grid = grid;
46107     this.view = grid.getView();
46108     // split the proxies so they don't interfere with mouse events
46109     this.proxyTop = Roo.DomHelper.append(document.body, {
46110         cls:"col-move-top", html:"&#160;"
46111     }, true);
46112     this.proxyBottom = Roo.DomHelper.append(document.body, {
46113         cls:"col-move-bottom", html:"&#160;"
46114     }, true);
46115     this.proxyTop.hide = this.proxyBottom.hide = function(){
46116         this.setLeftTop(-100,-100);
46117         this.setStyle("visibility", "hidden");
46118     };
46119     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46120     // temporarily disabled
46121     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46122     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46123 };
46124 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46125     proxyOffsets : [-4, -9],
46126     fly: Roo.Element.fly,
46127
46128     getTargetFromEvent : function(e){
46129         var t = Roo.lib.Event.getTarget(e);
46130         var cindex = this.view.findCellIndex(t);
46131         if(cindex !== false){
46132             return this.view.getHeaderCell(cindex);
46133         }
46134     },
46135
46136     nextVisible : function(h){
46137         var v = this.view, cm = this.grid.colModel;
46138         h = h.nextSibling;
46139         while(h){
46140             if(!cm.isHidden(v.getCellIndex(h))){
46141                 return h;
46142             }
46143             h = h.nextSibling;
46144         }
46145         return null;
46146     },
46147
46148     prevVisible : function(h){
46149         var v = this.view, cm = this.grid.colModel;
46150         h = h.prevSibling;
46151         while(h){
46152             if(!cm.isHidden(v.getCellIndex(h))){
46153                 return h;
46154             }
46155             h = h.prevSibling;
46156         }
46157         return null;
46158     },
46159
46160     positionIndicator : function(h, n, e){
46161         var x = Roo.lib.Event.getPageX(e);
46162         var r = Roo.lib.Dom.getRegion(n.firstChild);
46163         var px, pt, py = r.top + this.proxyOffsets[1];
46164         if((r.right - x) <= (r.right-r.left)/2){
46165             px = r.right+this.view.borderWidth;
46166             pt = "after";
46167         }else{
46168             px = r.left;
46169             pt = "before";
46170         }
46171         var oldIndex = this.view.getCellIndex(h);
46172         var newIndex = this.view.getCellIndex(n);
46173
46174         if(this.grid.colModel.isFixed(newIndex)){
46175             return false;
46176         }
46177
46178         var locked = this.grid.colModel.isLocked(newIndex);
46179
46180         if(pt == "after"){
46181             newIndex++;
46182         }
46183         if(oldIndex < newIndex){
46184             newIndex--;
46185         }
46186         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
46187             return false;
46188         }
46189         px +=  this.proxyOffsets[0];
46190         this.proxyTop.setLeftTop(px, py);
46191         this.proxyTop.show();
46192         if(!this.bottomOffset){
46193             this.bottomOffset = this.view.mainHd.getHeight();
46194         }
46195         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
46196         this.proxyBottom.show();
46197         return pt;
46198     },
46199
46200     onNodeEnter : function(n, dd, e, data){
46201         if(data.header != n){
46202             this.positionIndicator(data.header, n, e);
46203         }
46204     },
46205
46206     onNodeOver : function(n, dd, e, data){
46207         var result = false;
46208         if(data.header != n){
46209             result = this.positionIndicator(data.header, n, e);
46210         }
46211         if(!result){
46212             this.proxyTop.hide();
46213             this.proxyBottom.hide();
46214         }
46215         return result ? this.dropAllowed : this.dropNotAllowed;
46216     },
46217
46218     onNodeOut : function(n, dd, e, data){
46219         this.proxyTop.hide();
46220         this.proxyBottom.hide();
46221     },
46222
46223     onNodeDrop : function(n, dd, e, data){
46224         var h = data.header;
46225         if(h != n){
46226             var cm = this.grid.colModel;
46227             var x = Roo.lib.Event.getPageX(e);
46228             var r = Roo.lib.Dom.getRegion(n.firstChild);
46229             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
46230             var oldIndex = this.view.getCellIndex(h);
46231             var newIndex = this.view.getCellIndex(n);
46232             var locked = cm.isLocked(newIndex);
46233             if(pt == "after"){
46234                 newIndex++;
46235             }
46236             if(oldIndex < newIndex){
46237                 newIndex--;
46238             }
46239             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
46240                 return false;
46241             }
46242             cm.setLocked(oldIndex, locked, true);
46243             cm.moveColumn(oldIndex, newIndex);
46244             this.grid.fireEvent("columnmove", oldIndex, newIndex);
46245             return true;
46246         }
46247         return false;
46248     }
46249 });
46250 /*
46251  * Based on:
46252  * Ext JS Library 1.1.1
46253  * Copyright(c) 2006-2007, Ext JS, LLC.
46254  *
46255  * Originally Released Under LGPL - original licence link has changed is not relivant.
46256  *
46257  * Fork - LGPL
46258  * <script type="text/javascript">
46259  */
46260   
46261 /**
46262  * @class Roo.grid.GridView
46263  * @extends Roo.util.Observable
46264  *
46265  * @constructor
46266  * @param {Object} config
46267  */
46268 Roo.grid.GridView = function(config){
46269     Roo.grid.GridView.superclass.constructor.call(this);
46270     this.el = null;
46271
46272     Roo.apply(this, config);
46273 };
46274
46275 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
46276
46277     /**
46278      * Override this function to apply custom css classes to rows during rendering
46279      * @param {Record} record The record
46280      * @param {Number} index
46281      * @method getRowClass
46282      */
46283     rowClass : "x-grid-row",
46284
46285     cellClass : "x-grid-col",
46286
46287     tdClass : "x-grid-td",
46288
46289     hdClass : "x-grid-hd",
46290
46291     splitClass : "x-grid-split",
46292
46293     sortClasses : ["sort-asc", "sort-desc"],
46294
46295     enableMoveAnim : false,
46296
46297     hlColor: "C3DAF9",
46298
46299     dh : Roo.DomHelper,
46300
46301     fly : Roo.Element.fly,
46302
46303     css : Roo.util.CSS,
46304
46305     borderWidth: 1,
46306
46307     splitOffset: 3,
46308
46309     scrollIncrement : 22,
46310
46311     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
46312
46313     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
46314
46315     bind : function(ds, cm){
46316         if(this.ds){
46317             this.ds.un("load", this.onLoad, this);
46318             this.ds.un("datachanged", this.onDataChange, this);
46319             this.ds.un("add", this.onAdd, this);
46320             this.ds.un("remove", this.onRemove, this);
46321             this.ds.un("update", this.onUpdate, this);
46322             this.ds.un("clear", this.onClear, this);
46323         }
46324         if(ds){
46325             ds.on("load", this.onLoad, this);
46326             ds.on("datachanged", this.onDataChange, this);
46327             ds.on("add", this.onAdd, this);
46328             ds.on("remove", this.onRemove, this);
46329             ds.on("update", this.onUpdate, this);
46330             ds.on("clear", this.onClear, this);
46331         }
46332         this.ds = ds;
46333
46334         if(this.cm){
46335             this.cm.un("widthchange", this.onColWidthChange, this);
46336             this.cm.un("headerchange", this.onHeaderChange, this);
46337             this.cm.un("hiddenchange", this.onHiddenChange, this);
46338             this.cm.un("columnmoved", this.onColumnMove, this);
46339             this.cm.un("columnlockchange", this.onColumnLock, this);
46340         }
46341         if(cm){
46342             this.generateRules(cm);
46343             cm.on("widthchange", this.onColWidthChange, this);
46344             cm.on("headerchange", this.onHeaderChange, this);
46345             cm.on("hiddenchange", this.onHiddenChange, this);
46346             cm.on("columnmoved", this.onColumnMove, this);
46347             cm.on("columnlockchange", this.onColumnLock, this);
46348         }
46349         this.cm = cm;
46350     },
46351
46352     init: function(grid){
46353                 Roo.grid.GridView.superclass.init.call(this, grid);
46354
46355                 this.bind(grid.dataSource, grid.colModel);
46356
46357             grid.on("headerclick", this.handleHeaderClick, this);
46358
46359         if(grid.trackMouseOver){
46360             grid.on("mouseover", this.onRowOver, this);
46361                 grid.on("mouseout", this.onRowOut, this);
46362             }
46363             grid.cancelTextSelection = function(){};
46364                 this.gridId = grid.id;
46365
46366                 var tpls = this.templates || {};
46367
46368                 if(!tpls.master){
46369                     tpls.master = new Roo.Template(
46370                        '<div class="x-grid" hidefocus="true">',
46371                           '<div class="x-grid-topbar"></div>',
46372                           '<div class="x-grid-scroller"><div></div></div>',
46373                           '<div class="x-grid-locked">',
46374                               '<div class="x-grid-header">{lockedHeader}</div>',
46375                               '<div class="x-grid-body">{lockedBody}</div>',
46376                           "</div>",
46377                           '<div class="x-grid-viewport">',
46378                               '<div class="x-grid-header">{header}</div>',
46379                               '<div class="x-grid-body">{body}</div>',
46380                           "</div>",
46381                           '<div class="x-grid-bottombar"></div>',
46382                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
46383                           '<div class="x-grid-resize-proxy">&#160;</div>',
46384                        "</div>"
46385                     );
46386                     tpls.master.disableformats = true;
46387                 }
46388
46389                 if(!tpls.header){
46390                     tpls.header = new Roo.Template(
46391                        '<table border="0" cellspacing="0" cellpadding="0">',
46392                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
46393                        "</table>{splits}"
46394                     );
46395                     tpls.header.disableformats = true;
46396                 }
46397                 tpls.header.compile();
46398
46399                 if(!tpls.hcell){
46400                     tpls.hcell = new Roo.Template(
46401                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
46402                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
46403                         "</div></td>"
46404                      );
46405                      tpls.hcell.disableFormats = true;
46406                 }
46407                 tpls.hcell.compile();
46408
46409                 if(!tpls.hsplit){
46410                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
46411                     tpls.hsplit.disableFormats = true;
46412                 }
46413                 tpls.hsplit.compile();
46414
46415                 if(!tpls.body){
46416                     tpls.body = new Roo.Template(
46417                        '<table border="0" cellspacing="0" cellpadding="0">',
46418                        "<tbody>{rows}</tbody>",
46419                        "</table>"
46420                     );
46421                     tpls.body.disableFormats = true;
46422                 }
46423                 tpls.body.compile();
46424
46425                 if(!tpls.row){
46426                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
46427                     tpls.row.disableFormats = true;
46428                 }
46429                 tpls.row.compile();
46430
46431                 if(!tpls.cell){
46432                     tpls.cell = new Roo.Template(
46433                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
46434                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
46435                         "</td>"
46436                     );
46437             tpls.cell.disableFormats = true;
46438         }
46439                 tpls.cell.compile();
46440
46441                 this.templates = tpls;
46442         },
46443
46444         // remap these for backwards compat
46445     onColWidthChange : function(){
46446         this.updateColumns.apply(this, arguments);
46447     },
46448     onHeaderChange : function(){
46449         this.updateHeaders.apply(this, arguments);
46450     }, 
46451     onHiddenChange : function(){
46452         this.handleHiddenChange.apply(this, arguments);
46453     },
46454     onColumnMove : function(){
46455         this.handleColumnMove.apply(this, arguments);
46456     },
46457     onColumnLock : function(){
46458         this.handleLockChange.apply(this, arguments);
46459     },
46460
46461     onDataChange : function(){
46462         this.refresh();
46463         this.updateHeaderSortState();
46464     },
46465
46466         onClear : function(){
46467         this.refresh();
46468     },
46469
46470         onUpdate : function(ds, record){
46471         this.refreshRow(record);
46472     },
46473
46474     refreshRow : function(record){
46475         var ds = this.ds, index;
46476         if(typeof record == 'number'){
46477             index = record;
46478             record = ds.getAt(index);
46479         }else{
46480             index = ds.indexOf(record);
46481         }
46482         this.insertRows(ds, index, index, true);
46483         this.onRemove(ds, record, index+1, true);
46484         this.syncRowHeights(index, index);
46485         this.layout();
46486         this.fireEvent("rowupdated", this, index, record);
46487     },
46488
46489     onAdd : function(ds, records, index){
46490         this.insertRows(ds, index, index + (records.length-1));
46491     },
46492
46493     onRemove : function(ds, record, index, isUpdate){
46494         if(isUpdate !== true){
46495             this.fireEvent("beforerowremoved", this, index, record);
46496         }
46497         var bt = this.getBodyTable(), lt = this.getLockedTable();
46498         if(bt.rows[index]){
46499             bt.firstChild.removeChild(bt.rows[index]);
46500         }
46501         if(lt.rows[index]){
46502             lt.firstChild.removeChild(lt.rows[index]);
46503         }
46504         if(isUpdate !== true){
46505             this.stripeRows(index);
46506             this.syncRowHeights(index, index);
46507             this.layout();
46508             this.fireEvent("rowremoved", this, index, record);
46509         }
46510     },
46511
46512     onLoad : function(){
46513         this.scrollToTop();
46514     },
46515
46516     /**
46517      * Scrolls the grid to the top
46518      */
46519     scrollToTop : function(){
46520         if(this.scroller){
46521             this.scroller.dom.scrollTop = 0;
46522             this.syncScroll();
46523         }
46524     },
46525
46526     /**
46527      * Gets a panel in the header of the grid that can be used for toolbars etc.
46528      * After modifying the contents of this panel a call to grid.autoSize() may be
46529      * required to register any changes in size.
46530      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
46531      * @return Roo.Element
46532      */
46533     getHeaderPanel : function(doShow){
46534         if(doShow){
46535             this.headerPanel.show();
46536         }
46537         return this.headerPanel;
46538         },
46539
46540         /**
46541      * Gets a panel in the footer of the grid that can be used for toolbars etc.
46542      * After modifying the contents of this panel a call to grid.autoSize() may be
46543      * required to register any changes in size.
46544      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
46545      * @return Roo.Element
46546      */
46547     getFooterPanel : function(doShow){
46548         if(doShow){
46549             this.footerPanel.show();
46550         }
46551         return this.footerPanel;
46552         },
46553
46554         initElements : function(){
46555             var E = Roo.Element;
46556             var el = this.grid.getGridEl().dom.firstChild;
46557             var cs = el.childNodes;
46558
46559             this.el = new E(el);
46560             this.headerPanel = new E(el.firstChild);
46561             this.headerPanel.enableDisplayMode("block");
46562
46563         this.scroller = new E(cs[1]);
46564             this.scrollSizer = new E(this.scroller.dom.firstChild);
46565
46566             this.lockedWrap = new E(cs[2]);
46567             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
46568             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
46569
46570             this.mainWrap = new E(cs[3]);
46571             this.mainHd = new E(this.mainWrap.dom.firstChild);
46572             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
46573
46574             this.footerPanel = new E(cs[4]);
46575             this.footerPanel.enableDisplayMode("block");
46576
46577         this.focusEl = new E(cs[5]);
46578         this.focusEl.swallowEvent("click", true);
46579         this.resizeProxy = new E(cs[6]);
46580
46581             this.headerSelector = String.format(
46582                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
46583                this.lockedHd.id, this.mainHd.id
46584             );
46585
46586             this.splitterSelector = String.format(
46587                '#{0} div.x-grid-split, #{1} div.x-grid-split',
46588                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
46589             );
46590     },
46591     idToCssName : function(s)
46592     {
46593         return s.replace(/[^a-z0-9]+/ig, '-');
46594     },
46595
46596         getHeaderCell : function(index){
46597             return Roo.DomQuery.select(this.headerSelector)[index];
46598         },
46599
46600         getHeaderCellMeasure : function(index){
46601             return this.getHeaderCell(index).firstChild;
46602         },
46603
46604         getHeaderCellText : function(index){
46605             return this.getHeaderCell(index).firstChild.firstChild;
46606         },
46607
46608         getLockedTable : function(){
46609             return this.lockedBody.dom.firstChild;
46610         },
46611
46612         getBodyTable : function(){
46613             return this.mainBody.dom.firstChild;
46614         },
46615
46616         getLockedRow : function(index){
46617             return this.getLockedTable().rows[index];
46618         },
46619
46620         getRow : function(index){
46621             return this.getBodyTable().rows[index];
46622         },
46623
46624         getRowComposite : function(index){
46625             if(!this.rowEl){
46626                 this.rowEl = new Roo.CompositeElementLite();
46627             }
46628         var els = [], lrow, mrow;
46629         if(lrow = this.getLockedRow(index)){
46630             els.push(lrow);
46631         }
46632         if(mrow = this.getRow(index)){
46633             els.push(mrow);
46634         }
46635         this.rowEl.elements = els;
46636             return this.rowEl;
46637         },
46638
46639         getCell : function(rowIndex, colIndex){
46640             var locked = this.cm.getLockedCount();
46641             var source;
46642             if(colIndex < locked){
46643                 source = this.lockedBody.dom.firstChild;
46644             }else{
46645                 source = this.mainBody.dom.firstChild;
46646                 colIndex -= locked;
46647             }
46648         return source.rows[rowIndex].childNodes[colIndex];
46649         },
46650
46651         getCellText : function(rowIndex, colIndex){
46652             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
46653         },
46654
46655         getCellBox : function(cell){
46656             var b = this.fly(cell).getBox();
46657         if(Roo.isOpera){ // opera fails to report the Y
46658             b.y = cell.offsetTop + this.mainBody.getY();
46659         }
46660         return b;
46661     },
46662
46663     getCellIndex : function(cell){
46664         var id = String(cell.className).match(this.cellRE);
46665         if(id){
46666             return parseInt(id[1], 10);
46667         }
46668         return 0;
46669     },
46670
46671     findHeaderIndex : function(n){
46672         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46673         return r ? this.getCellIndex(r) : false;
46674     },
46675
46676     findHeaderCell : function(n){
46677         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
46678         return r ? r : false;
46679     },
46680
46681     findRowIndex : function(n){
46682         if(!n){
46683             return false;
46684         }
46685         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
46686         return r ? r.rowIndex : false;
46687     },
46688
46689     findCellIndex : function(node){
46690         var stop = this.el.dom;
46691         while(node && node != stop){
46692             if(this.findRE.test(node.className)){
46693                 return this.getCellIndex(node);
46694             }
46695             node = node.parentNode;
46696         }
46697         return false;
46698     },
46699
46700     getColumnId : function(index){
46701             return this.cm.getColumnId(index);
46702         },
46703
46704         getSplitters : function(){
46705             if(this.splitterSelector){
46706                return Roo.DomQuery.select(this.splitterSelector);
46707             }else{
46708                 return null;
46709             }
46710         },
46711
46712         getSplitter : function(index){
46713             return this.getSplitters()[index];
46714         },
46715
46716     onRowOver : function(e, t){
46717         var row;
46718         if((row = this.findRowIndex(t)) !== false){
46719             this.getRowComposite(row).addClass("x-grid-row-over");
46720         }
46721     },
46722
46723     onRowOut : function(e, t){
46724         var row;
46725         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
46726             this.getRowComposite(row).removeClass("x-grid-row-over");
46727         }
46728     },
46729
46730     renderHeaders : function(){
46731             var cm = this.cm;
46732         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
46733         var cb = [], lb = [], sb = [], lsb = [], p = {};
46734         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46735             p.cellId = "x-grid-hd-0-" + i;
46736             p.splitId = "x-grid-csplit-0-" + i;
46737             p.id = cm.getColumnId(i);
46738             p.title = cm.getColumnTooltip(i) || "";
46739             p.value = cm.getColumnHeader(i) || "";
46740             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
46741             if(!cm.isLocked(i)){
46742                 cb[cb.length] = ct.apply(p);
46743                 sb[sb.length] = st.apply(p);
46744             }else{
46745                 lb[lb.length] = ct.apply(p);
46746                 lsb[lsb.length] = st.apply(p);
46747             }
46748         }
46749         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
46750                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
46751         },
46752
46753         updateHeaders : function(){
46754         var html = this.renderHeaders();
46755         this.lockedHd.update(html[0]);
46756         this.mainHd.update(html[1]);
46757     },
46758
46759     /**
46760      * Focuses the specified row.
46761      * @param {Number} row The row index
46762      */
46763     focusRow : function(row){
46764         var x = this.scroller.dom.scrollLeft;
46765         this.focusCell(row, 0, false);
46766         this.scroller.dom.scrollLeft = x;
46767     },
46768
46769     /**
46770      * Focuses the specified cell.
46771      * @param {Number} row The row index
46772      * @param {Number} col The column index
46773      * @param {Boolean} hscroll false to disable horizontal scrolling
46774      */
46775     focusCell : function(row, col, hscroll){
46776         var el = this.ensureVisible(row, col, hscroll);
46777         this.focusEl.alignTo(el, "tl-tl");
46778         if(Roo.isGecko){
46779             this.focusEl.focus();
46780         }else{
46781             this.focusEl.focus.defer(1, this.focusEl);
46782         }
46783     },
46784
46785     /**
46786      * Scrolls the specified cell into view
46787      * @param {Number} row The row index
46788      * @param {Number} col The column index
46789      * @param {Boolean} hscroll false to disable horizontal scrolling
46790      */
46791     ensureVisible : function(row, col, hscroll){
46792         if(typeof row != "number"){
46793             row = row.rowIndex;
46794         }
46795         if(row < 0 && row >= this.ds.getCount()){
46796             return;
46797         }
46798         col = (col !== undefined ? col : 0);
46799         var cm = this.grid.colModel;
46800         while(cm.isHidden(col)){
46801             col++;
46802         }
46803
46804         var el = this.getCell(row, col);
46805         if(!el){
46806             return;
46807         }
46808         var c = this.scroller.dom;
46809
46810         var ctop = parseInt(el.offsetTop, 10);
46811         var cleft = parseInt(el.offsetLeft, 10);
46812         var cbot = ctop + el.offsetHeight;
46813         var cright = cleft + el.offsetWidth;
46814
46815         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
46816         var stop = parseInt(c.scrollTop, 10);
46817         var sleft = parseInt(c.scrollLeft, 10);
46818         var sbot = stop + ch;
46819         var sright = sleft + c.clientWidth;
46820
46821         if(ctop < stop){
46822                 c.scrollTop = ctop;
46823         }else if(cbot > sbot){
46824             c.scrollTop = cbot-ch;
46825         }
46826
46827         if(hscroll !== false){
46828             if(cleft < sleft){
46829                 c.scrollLeft = cleft;
46830             }else if(cright > sright){
46831                 c.scrollLeft = cright-c.clientWidth;
46832             }
46833         }
46834         return el;
46835     },
46836
46837     updateColumns : function(){
46838         this.grid.stopEditing();
46839         var cm = this.grid.colModel, colIds = this.getColumnIds();
46840         //var totalWidth = cm.getTotalWidth();
46841         var pos = 0;
46842         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46843             //if(cm.isHidden(i)) continue;
46844             var w = cm.getColumnWidth(i);
46845             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46846             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
46847         }
46848         this.updateSplitters();
46849     },
46850
46851     generateRules : function(cm){
46852         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
46853         Roo.util.CSS.removeStyleSheet(rulesId);
46854         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46855             var cid = cm.getColumnId(i);
46856             var align = '';
46857             if(cm.config[i].align){
46858                 align = 'text-align:'+cm.config[i].align+';';
46859             }
46860             var hidden = '';
46861             if(cm.isHidden(i)){
46862                 hidden = 'display:none;';
46863             }
46864             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
46865             ruleBuf.push(
46866                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
46867                     this.hdSelector, cid, " {\n", align, width, "}\n",
46868                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
46869                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
46870         }
46871         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46872     },
46873
46874     updateSplitters : function(){
46875         var cm = this.cm, s = this.getSplitters();
46876         if(s){ // splitters not created yet
46877             var pos = 0, locked = true;
46878             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46879                 if(cm.isHidden(i)) continue;
46880                 var w = cm.getColumnWidth(i);
46881                 if(!cm.isLocked(i) && locked){
46882                     pos = 0;
46883                     locked = false;
46884                 }
46885                 pos += w;
46886                 s[i].style.left = (pos-this.splitOffset) + "px";
46887             }
46888         }
46889     },
46890
46891     handleHiddenChange : function(colModel, colIndex, hidden){
46892         if(hidden){
46893             this.hideColumn(colIndex);
46894         }else{
46895             this.unhideColumn(colIndex);
46896         }
46897     },
46898
46899     hideColumn : function(colIndex){
46900         var cid = this.getColumnId(colIndex);
46901         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
46902         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
46903         if(Roo.isSafari){
46904             this.updateHeaders();
46905         }
46906         this.updateSplitters();
46907         this.layout();
46908     },
46909
46910     unhideColumn : function(colIndex){
46911         var cid = this.getColumnId(colIndex);
46912         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
46913         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
46914
46915         if(Roo.isSafari){
46916             this.updateHeaders();
46917         }
46918         this.updateSplitters();
46919         this.layout();
46920     },
46921
46922     insertRows : function(dm, firstRow, lastRow, isUpdate){
46923         if(firstRow == 0 && lastRow == dm.getCount()-1){
46924             this.refresh();
46925         }else{
46926             if(!isUpdate){
46927                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
46928             }
46929             var s = this.getScrollState();
46930             var markup = this.renderRows(firstRow, lastRow);
46931             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
46932             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
46933             this.restoreScroll(s);
46934             if(!isUpdate){
46935                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
46936                 this.syncRowHeights(firstRow, lastRow);
46937                 this.stripeRows(firstRow);
46938                 this.layout();
46939             }
46940         }
46941     },
46942
46943     bufferRows : function(markup, target, index){
46944         var before = null, trows = target.rows, tbody = target.tBodies[0];
46945         if(index < trows.length){
46946             before = trows[index];
46947         }
46948         var b = document.createElement("div");
46949         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
46950         var rows = b.firstChild.rows;
46951         for(var i = 0, len = rows.length; i < len; i++){
46952             if(before){
46953                 tbody.insertBefore(rows[0], before);
46954             }else{
46955                 tbody.appendChild(rows[0]);
46956             }
46957         }
46958         b.innerHTML = "";
46959         b = null;
46960     },
46961
46962     deleteRows : function(dm, firstRow, lastRow){
46963         if(dm.getRowCount()<1){
46964             this.fireEvent("beforerefresh", this);
46965             this.mainBody.update("");
46966             this.lockedBody.update("");
46967             this.fireEvent("refresh", this);
46968         }else{
46969             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
46970             var bt = this.getBodyTable();
46971             var tbody = bt.firstChild;
46972             var rows = bt.rows;
46973             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
46974                 tbody.removeChild(rows[firstRow]);
46975             }
46976             this.stripeRows(firstRow);
46977             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
46978         }
46979     },
46980
46981     updateRows : function(dataSource, firstRow, lastRow){
46982         var s = this.getScrollState();
46983         this.refresh();
46984         this.restoreScroll(s);
46985     },
46986
46987     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
46988         if(!noRefresh){
46989            this.refresh();
46990         }
46991         this.updateHeaderSortState();
46992     },
46993
46994     getScrollState : function(){
46995         var sb = this.scroller.dom;
46996         return {left: sb.scrollLeft, top: sb.scrollTop};
46997     },
46998
46999     stripeRows : function(startRow){
47000         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47001             return;
47002         }
47003         startRow = startRow || 0;
47004         var rows = this.getBodyTable().rows;
47005         var lrows = this.getLockedTable().rows;
47006         var cls = ' x-grid-row-alt ';
47007         for(var i = startRow, len = rows.length; i < len; i++){
47008             var row = rows[i], lrow = lrows[i];
47009             var isAlt = ((i+1) % 2 == 0);
47010             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47011             if(isAlt == hasAlt){
47012                 continue;
47013             }
47014             if(isAlt){
47015                 row.className += " x-grid-row-alt";
47016             }else{
47017                 row.className = row.className.replace("x-grid-row-alt", "");
47018             }
47019             if(lrow){
47020                 lrow.className = row.className;
47021             }
47022         }
47023     },
47024
47025     restoreScroll : function(state){
47026         var sb = this.scroller.dom;
47027         sb.scrollLeft = state.left;
47028         sb.scrollTop = state.top;
47029         this.syncScroll();
47030     },
47031
47032     syncScroll : function(){
47033         var sb = this.scroller.dom;
47034         var sh = this.mainHd.dom;
47035         var bs = this.mainBody.dom;
47036         var lv = this.lockedBody.dom;
47037         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47038         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47039     },
47040
47041     handleScroll : function(e){
47042         this.syncScroll();
47043         var sb = this.scroller.dom;
47044         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47045         e.stopEvent();
47046     },
47047
47048     handleWheel : function(e){
47049         var d = e.getWheelDelta();
47050         this.scroller.dom.scrollTop -= d*22;
47051         // set this here to prevent jumpy scrolling on large tables
47052         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47053         e.stopEvent();
47054     },
47055
47056     renderRows : function(startRow, endRow){
47057         // pull in all the crap needed to render rows
47058         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47059         var colCount = cm.getColumnCount();
47060
47061         if(ds.getCount() < 1){
47062             return ["", ""];
47063         }
47064
47065         // build a map for all the columns
47066         var cs = [];
47067         for(var i = 0; i < colCount; i++){
47068             var name = cm.getDataIndex(i);
47069             cs[i] = {
47070                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47071                 renderer : cm.getRenderer(i),
47072                 id : cm.getColumnId(i),
47073                 locked : cm.isLocked(i)
47074             };
47075         }
47076
47077         startRow = startRow || 0;
47078         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47079
47080         // records to render
47081         var rs = ds.getRange(startRow, endRow);
47082
47083         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47084     },
47085
47086     // As much as I hate to duplicate code, this was branched because FireFox really hates
47087     // [].join("") on strings. The performance difference was substantial enough to
47088     // branch this function
47089     doRender : Roo.isGecko ?
47090             function(cs, rs, ds, startRow, colCount, stripe){
47091                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47092                 // buffers
47093                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47094                 for(var j = 0, len = rs.length; j < len; j++){
47095                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47096                     for(var i = 0; i < colCount; i++){
47097                         c = cs[i];
47098                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47099                         p.id = c.id;
47100                         p.css = p.attr = "";
47101                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47102                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47103                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47104                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47105                         }
47106                         var markup = ct.apply(p);
47107                         if(!c.locked){
47108                             cb+= markup;
47109                         }else{
47110                             lcb+= markup;
47111                         }
47112                     }
47113                     var alt = [];
47114                     if(stripe && ((rowIndex+1) % 2 == 0)){
47115                         alt[0] = "x-grid-row-alt";
47116                     }
47117                     if(r.dirty){
47118                         alt[1] = " x-grid-dirty-row";
47119                     }
47120                     rp.cells = lcb;
47121                     if(this.getRowClass){
47122                         alt[2] = this.getRowClass(r, rowIndex);
47123                     }
47124                     rp.alt = alt.join(" ");
47125                     lbuf+= rt.apply(rp);
47126                     rp.cells = cb;
47127                     buf+=  rt.apply(rp);
47128                 }
47129                 return [lbuf, buf];
47130             } :
47131             function(cs, rs, ds, startRow, colCount, stripe){
47132                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47133                 // buffers
47134                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47135                 for(var j = 0, len = rs.length; j < len; j++){
47136                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47137                     for(var i = 0; i < colCount; i++){
47138                         c = cs[i];
47139                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47140                         p.id = c.id;
47141                         p.css = p.attr = "";
47142                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47143                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47144                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47145                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47146                         }
47147                         var markup = ct.apply(p);
47148                         if(!c.locked){
47149                             cb[cb.length] = markup;
47150                         }else{
47151                             lcb[lcb.length] = markup;
47152                         }
47153                     }
47154                     var alt = [];
47155                     if(stripe && ((rowIndex+1) % 2 == 0)){
47156                         alt[0] = "x-grid-row-alt";
47157                     }
47158                     if(r.dirty){
47159                         alt[1] = " x-grid-dirty-row";
47160                     }
47161                     rp.cells = lcb;
47162                     if(this.getRowClass){
47163                         alt[2] = this.getRowClass(r, rowIndex);
47164                     }
47165                     rp.alt = alt.join(" ");
47166                     rp.cells = lcb.join("");
47167                     lbuf[lbuf.length] = rt.apply(rp);
47168                     rp.cells = cb.join("");
47169                     buf[buf.length] =  rt.apply(rp);
47170                 }
47171                 return [lbuf.join(""), buf.join("")];
47172             },
47173
47174     renderBody : function(){
47175         var markup = this.renderRows();
47176         var bt = this.templates.body;
47177         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
47178     },
47179
47180     /**
47181      * Refreshes the grid
47182      * @param {Boolean} headersToo
47183      */
47184     refresh : function(headersToo){
47185         this.fireEvent("beforerefresh", this);
47186         this.grid.stopEditing();
47187         var result = this.renderBody();
47188         this.lockedBody.update(result[0]);
47189         this.mainBody.update(result[1]);
47190         if(headersToo === true){
47191             this.updateHeaders();
47192             this.updateColumns();
47193             this.updateSplitters();
47194             this.updateHeaderSortState();
47195         }
47196         this.syncRowHeights();
47197         this.layout();
47198         this.fireEvent("refresh", this);
47199     },
47200
47201     handleColumnMove : function(cm, oldIndex, newIndex){
47202         this.indexMap = null;
47203         var s = this.getScrollState();
47204         this.refresh(true);
47205         this.restoreScroll(s);
47206         this.afterMove(newIndex);
47207     },
47208
47209     afterMove : function(colIndex){
47210         if(this.enableMoveAnim && Roo.enableFx){
47211             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
47212         }
47213     },
47214
47215     updateCell : function(dm, rowIndex, dataIndex){
47216         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
47217         if(typeof colIndex == "undefined"){ // not present in grid
47218             return;
47219         }
47220         var cm = this.grid.colModel;
47221         var cell = this.getCell(rowIndex, colIndex);
47222         var cellText = this.getCellText(rowIndex, colIndex);
47223
47224         var p = {
47225             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
47226             id : cm.getColumnId(colIndex),
47227             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
47228         };
47229         var renderer = cm.getRenderer(colIndex);
47230         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
47231         if(typeof val == "undefined" || val === "") val = "&#160;";
47232         cellText.innerHTML = val;
47233         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
47234         this.syncRowHeights(rowIndex, rowIndex);
47235     },
47236
47237     calcColumnWidth : function(colIndex, maxRowsToMeasure){
47238         var maxWidth = 0;
47239         if(this.grid.autoSizeHeaders){
47240             var h = this.getHeaderCellMeasure(colIndex);
47241             maxWidth = Math.max(maxWidth, h.scrollWidth);
47242         }
47243         var tb, index;
47244         if(this.cm.isLocked(colIndex)){
47245             tb = this.getLockedTable();
47246             index = colIndex;
47247         }else{
47248             tb = this.getBodyTable();
47249             index = colIndex - this.cm.getLockedCount();
47250         }
47251         if(tb && tb.rows){
47252             var rows = tb.rows;
47253             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
47254             for(var i = 0; i < stopIndex; i++){
47255                 var cell = rows[i].childNodes[index].firstChild;
47256                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
47257             }
47258         }
47259         return maxWidth + /*margin for error in IE*/ 5;
47260     },
47261     /**
47262      * Autofit a column to its content.
47263      * @param {Number} colIndex
47264      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
47265      */
47266      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
47267          if(this.cm.isHidden(colIndex)){
47268              return; // can't calc a hidden column
47269          }
47270         if(forceMinSize){
47271             var cid = this.cm.getColumnId(colIndex);
47272             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
47273            if(this.grid.autoSizeHeaders){
47274                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
47275            }
47276         }
47277         var newWidth = this.calcColumnWidth(colIndex);
47278         this.cm.setColumnWidth(colIndex,
47279             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
47280         if(!suppressEvent){
47281             this.grid.fireEvent("columnresize", colIndex, newWidth);
47282         }
47283     },
47284
47285     /**
47286      * Autofits all columns to their content and then expands to fit any extra space in the grid
47287      */
47288      autoSizeColumns : function(){
47289         var cm = this.grid.colModel;
47290         var colCount = cm.getColumnCount();
47291         for(var i = 0; i < colCount; i++){
47292             this.autoSizeColumn(i, true, true);
47293         }
47294         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
47295             this.fitColumns();
47296         }else{
47297             this.updateColumns();
47298             this.layout();
47299         }
47300     },
47301
47302     /**
47303      * Autofits all columns to the grid's width proportionate with their current size
47304      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
47305      */
47306     fitColumns : function(reserveScrollSpace){
47307         var cm = this.grid.colModel;
47308         var colCount = cm.getColumnCount();
47309         var cols = [];
47310         var width = 0;
47311         var i, w;
47312         for (i = 0; i < colCount; i++){
47313             if(!cm.isHidden(i) && !cm.isFixed(i)){
47314                 w = cm.getColumnWidth(i);
47315                 cols.push(i);
47316                 cols.push(w);
47317                 width += w;
47318             }
47319         }
47320         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
47321         if(reserveScrollSpace){
47322             avail -= 17;
47323         }
47324         var frac = (avail - cm.getTotalWidth())/width;
47325         while (cols.length){
47326             w = cols.pop();
47327             i = cols.pop();
47328             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
47329         }
47330         this.updateColumns();
47331         this.layout();
47332     },
47333
47334     onRowSelect : function(rowIndex){
47335         var row = this.getRowComposite(rowIndex);
47336         row.addClass("x-grid-row-selected");
47337     },
47338
47339     onRowDeselect : function(rowIndex){
47340         var row = this.getRowComposite(rowIndex);
47341         row.removeClass("x-grid-row-selected");
47342     },
47343
47344     onCellSelect : function(row, col){
47345         var cell = this.getCell(row, col);
47346         if(cell){
47347             Roo.fly(cell).addClass("x-grid-cell-selected");
47348         }
47349     },
47350
47351     onCellDeselect : function(row, col){
47352         var cell = this.getCell(row, col);
47353         if(cell){
47354             Roo.fly(cell).removeClass("x-grid-cell-selected");
47355         }
47356     },
47357
47358     updateHeaderSortState : function(){
47359         var state = this.ds.getSortState();
47360         if(!state){
47361             return;
47362         }
47363         this.sortState = state;
47364         var sortColumn = this.cm.findColumnIndex(state.field);
47365         if(sortColumn != -1){
47366             var sortDir = state.direction;
47367             var sc = this.sortClasses;
47368             var hds = this.el.select(this.headerSelector).removeClass(sc);
47369             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
47370         }
47371     },
47372
47373     handleHeaderClick : function(g, index){
47374         if(this.headersDisabled){
47375             return;
47376         }
47377         var dm = g.dataSource, cm = g.colModel;
47378             if(!cm.isSortable(index)){
47379             return;
47380         }
47381             g.stopEditing();
47382         dm.sort(cm.getDataIndex(index));
47383     },
47384
47385
47386     destroy : function(){
47387         if(this.colMenu){
47388             this.colMenu.removeAll();
47389             Roo.menu.MenuMgr.unregister(this.colMenu);
47390             this.colMenu.getEl().remove();
47391             delete this.colMenu;
47392         }
47393         if(this.hmenu){
47394             this.hmenu.removeAll();
47395             Roo.menu.MenuMgr.unregister(this.hmenu);
47396             this.hmenu.getEl().remove();
47397             delete this.hmenu;
47398         }
47399         if(this.grid.enableColumnMove){
47400             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47401             if(dds){
47402                 for(var dd in dds){
47403                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
47404                         var elid = dds[dd].dragElId;
47405                         dds[dd].unreg();
47406                         Roo.get(elid).remove();
47407                     } else if(dds[dd].config.isTarget){
47408                         dds[dd].proxyTop.remove();
47409                         dds[dd].proxyBottom.remove();
47410                         dds[dd].unreg();
47411                     }
47412                     if(Roo.dd.DDM.locationCache[dd]){
47413                         delete Roo.dd.DDM.locationCache[dd];
47414                     }
47415                 }
47416                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
47417             }
47418         }
47419         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
47420         this.bind(null, null);
47421         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
47422     },
47423
47424     handleLockChange : function(){
47425         this.refresh(true);
47426     },
47427
47428     onDenyColumnLock : function(){
47429
47430     },
47431
47432     onDenyColumnHide : function(){
47433
47434     },
47435
47436     handleHdMenuClick : function(item){
47437         var index = this.hdCtxIndex;
47438         var cm = this.cm, ds = this.ds;
47439         switch(item.id){
47440             case "asc":
47441                 ds.sort(cm.getDataIndex(index), "ASC");
47442                 break;
47443             case "desc":
47444                 ds.sort(cm.getDataIndex(index), "DESC");
47445                 break;
47446             case "lock":
47447                 var lc = cm.getLockedCount();
47448                 if(cm.getColumnCount(true) <= lc+1){
47449                     this.onDenyColumnLock();
47450                     return;
47451                 }
47452                 if(lc != index){
47453                     cm.setLocked(index, true, true);
47454                     cm.moveColumn(index, lc);
47455                     this.grid.fireEvent("columnmove", index, lc);
47456                 }else{
47457                     cm.setLocked(index, true);
47458                 }
47459             break;
47460             case "unlock":
47461                 var lc = cm.getLockedCount();
47462                 if((lc-1) != index){
47463                     cm.setLocked(index, false, true);
47464                     cm.moveColumn(index, lc-1);
47465                     this.grid.fireEvent("columnmove", index, lc-1);
47466                 }else{
47467                     cm.setLocked(index, false);
47468                 }
47469             break;
47470             default:
47471                 index = cm.getIndexById(item.id.substr(4));
47472                 if(index != -1){
47473                     if(item.checked && cm.getColumnCount(true) <= 1){
47474                         this.onDenyColumnHide();
47475                         return false;
47476                     }
47477                     cm.setHidden(index, item.checked);
47478                 }
47479         }
47480         return true;
47481     },
47482
47483     beforeColMenuShow : function(){
47484         var cm = this.cm,  colCount = cm.getColumnCount();
47485         this.colMenu.removeAll();
47486         for(var i = 0; i < colCount; i++){
47487             this.colMenu.add(new Roo.menu.CheckItem({
47488                 id: "col-"+cm.getColumnId(i),
47489                 text: cm.getColumnHeader(i),
47490                 checked: !cm.isHidden(i),
47491                 hideOnClick:false
47492             }));
47493         }
47494     },
47495
47496     handleHdCtx : function(g, index, e){
47497         e.stopEvent();
47498         var hd = this.getHeaderCell(index);
47499         this.hdCtxIndex = index;
47500         var ms = this.hmenu.items, cm = this.cm;
47501         ms.get("asc").setDisabled(!cm.isSortable(index));
47502         ms.get("desc").setDisabled(!cm.isSortable(index));
47503         if(this.grid.enableColLock !== false){
47504             ms.get("lock").setDisabled(cm.isLocked(index));
47505             ms.get("unlock").setDisabled(!cm.isLocked(index));
47506         }
47507         this.hmenu.show(hd, "tl-bl");
47508     },
47509
47510     handleHdOver : function(e){
47511         var hd = this.findHeaderCell(e.getTarget());
47512         if(hd && !this.headersDisabled){
47513             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
47514                this.fly(hd).addClass("x-grid-hd-over");
47515             }
47516         }
47517     },
47518
47519     handleHdOut : function(e){
47520         var hd = this.findHeaderCell(e.getTarget());
47521         if(hd){
47522             this.fly(hd).removeClass("x-grid-hd-over");
47523         }
47524     },
47525
47526     handleSplitDblClick : function(e, t){
47527         var i = this.getCellIndex(t);
47528         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
47529             this.autoSizeColumn(i, true);
47530             this.layout();
47531         }
47532     },
47533
47534     render : function(){
47535
47536         var cm = this.cm;
47537         var colCount = cm.getColumnCount();
47538
47539         if(this.grid.monitorWindowResize === true){
47540             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
47541         }
47542         var header = this.renderHeaders();
47543         var body = this.templates.body.apply({rows:""});
47544         var html = this.templates.master.apply({
47545             lockedBody: body,
47546             body: body,
47547             lockedHeader: header[0],
47548             header: header[1]
47549         });
47550
47551         //this.updateColumns();
47552
47553         this.grid.getGridEl().dom.innerHTML = html;
47554
47555         this.initElements();
47556
47557         this.scroller.on("scroll", this.handleScroll, this);
47558         this.lockedBody.on("mousewheel", this.handleWheel, this);
47559         this.mainBody.on("mousewheel", this.handleWheel, this);
47560
47561         this.mainHd.on("mouseover", this.handleHdOver, this);
47562         this.mainHd.on("mouseout", this.handleHdOut, this);
47563         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
47564                 {delegate: "."+this.splitClass});
47565
47566         this.lockedHd.on("mouseover", this.handleHdOver, this);
47567         this.lockedHd.on("mouseout", this.handleHdOut, this);
47568         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
47569                 {delegate: "."+this.splitClass});
47570
47571         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
47572             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47573         }
47574
47575         this.updateSplitters();
47576
47577         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
47578             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47579             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
47580         }
47581
47582         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
47583             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
47584             this.hmenu.add(
47585                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
47586                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
47587             );
47588             if(this.grid.enableColLock !== false){
47589                 this.hmenu.add('-',
47590                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
47591                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
47592                 );
47593             }
47594             if(this.grid.enableColumnHide !== false){
47595
47596                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
47597                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
47598                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
47599
47600                 this.hmenu.add('-',
47601                     {id:"columns", text: this.columnsText, menu: this.colMenu}
47602                 );
47603             }
47604             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
47605
47606             this.grid.on("headercontextmenu", this.handleHdCtx, this);
47607         }
47608
47609         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
47610             this.dd = new Roo.grid.GridDragZone(this.grid, {
47611                 ddGroup : this.grid.ddGroup || 'GridDD'
47612             });
47613         }
47614
47615         /*
47616         for(var i = 0; i < colCount; i++){
47617             if(cm.isHidden(i)){
47618                 this.hideColumn(i);
47619             }
47620             if(cm.config[i].align){
47621                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
47622                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
47623             }
47624         }*/
47625         
47626         this.updateHeaderSortState();
47627
47628         this.beforeInitialResize();
47629         this.layout(true);
47630
47631         // two part rendering gives faster view to the user
47632         this.renderPhase2.defer(1, this);
47633     },
47634
47635     renderPhase2 : function(){
47636         // render the rows now
47637         this.refresh();
47638         if(this.grid.autoSizeColumns){
47639             this.autoSizeColumns();
47640         }
47641     },
47642
47643     beforeInitialResize : function(){
47644
47645     },
47646
47647     onColumnSplitterMoved : function(i, w){
47648         this.userResized = true;
47649         var cm = this.grid.colModel;
47650         cm.setColumnWidth(i, w, true);
47651         var cid = cm.getColumnId(i);
47652         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47653         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
47654         this.updateSplitters();
47655         this.layout();
47656         this.grid.fireEvent("columnresize", i, w);
47657     },
47658
47659     syncRowHeights : function(startIndex, endIndex){
47660         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
47661             startIndex = startIndex || 0;
47662             var mrows = this.getBodyTable().rows;
47663             var lrows = this.getLockedTable().rows;
47664             var len = mrows.length-1;
47665             endIndex = Math.min(endIndex || len, len);
47666             for(var i = startIndex; i <= endIndex; i++){
47667                 var m = mrows[i], l = lrows[i];
47668                 var h = Math.max(m.offsetHeight, l.offsetHeight);
47669                 m.style.height = l.style.height = h + "px";
47670             }
47671         }
47672     },
47673
47674     layout : function(initialRender, is2ndPass){
47675         var g = this.grid;
47676         var auto = g.autoHeight;
47677         var scrollOffset = 16;
47678         var c = g.getGridEl(), cm = this.cm,
47679                 expandCol = g.autoExpandColumn,
47680                 gv = this;
47681         //c.beginMeasure();
47682
47683         if(!c.dom.offsetWidth){ // display:none?
47684             if(initialRender){
47685                 this.lockedWrap.show();
47686                 this.mainWrap.show();
47687             }
47688             return;
47689         }
47690
47691         var hasLock = this.cm.isLocked(0);
47692
47693         var tbh = this.headerPanel.getHeight();
47694         var bbh = this.footerPanel.getHeight();
47695
47696         if(auto){
47697             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
47698             var newHeight = ch + c.getBorderWidth("tb");
47699             if(g.maxHeight){
47700                 newHeight = Math.min(g.maxHeight, newHeight);
47701             }
47702             c.setHeight(newHeight);
47703         }
47704
47705         if(g.autoWidth){
47706             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
47707         }
47708
47709         var s = this.scroller;
47710
47711         var csize = c.getSize(true);
47712
47713         this.el.setSize(csize.width, csize.height);
47714
47715         this.headerPanel.setWidth(csize.width);
47716         this.footerPanel.setWidth(csize.width);
47717
47718         var hdHeight = this.mainHd.getHeight();
47719         var vw = csize.width;
47720         var vh = csize.height - (tbh + bbh);
47721
47722         s.setSize(vw, vh);
47723
47724         var bt = this.getBodyTable();
47725         var ltWidth = hasLock ?
47726                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
47727
47728         var scrollHeight = bt.offsetHeight;
47729         var scrollWidth = ltWidth + bt.offsetWidth;
47730         var vscroll = false, hscroll = false;
47731
47732         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
47733
47734         var lw = this.lockedWrap, mw = this.mainWrap;
47735         var lb = this.lockedBody, mb = this.mainBody;
47736
47737         setTimeout(function(){
47738             var t = s.dom.offsetTop;
47739             var w = s.dom.clientWidth,
47740                 h = s.dom.clientHeight;
47741
47742             lw.setTop(t);
47743             lw.setSize(ltWidth, h);
47744
47745             mw.setLeftTop(ltWidth, t);
47746             mw.setSize(w-ltWidth, h);
47747
47748             lb.setHeight(h-hdHeight);
47749             mb.setHeight(h-hdHeight);
47750
47751             if(is2ndPass !== true && !gv.userResized && expandCol){
47752                 // high speed resize without full column calculation
47753                 
47754                 var ci = cm.getIndexById(expandCol);
47755                 if (ci < 0) {
47756                     ci = cm.findColumnIndex(expandCol);
47757                 }
47758                 ci = Math.max(0, ci); // make sure it's got at least the first col.
47759                 var expandId = cm.getColumnId(ci);
47760                 var  tw = cm.getTotalWidth(false);
47761                 var currentWidth = cm.getColumnWidth(ci);
47762                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
47763                 if(currentWidth != cw){
47764                     cm.setColumnWidth(ci, cw, true);
47765                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47766                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
47767                     gv.updateSplitters();
47768                     gv.layout(false, true);
47769                 }
47770             }
47771
47772             if(initialRender){
47773                 lw.show();
47774                 mw.show();
47775             }
47776             //c.endMeasure();
47777         }, 10);
47778     },
47779
47780     onWindowResize : function(){
47781         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
47782             return;
47783         }
47784         this.layout();
47785     },
47786
47787     appendFooter : function(parentEl){
47788         return null;
47789     },
47790
47791     sortAscText : "Sort Ascending",
47792     sortDescText : "Sort Descending",
47793     lockText : "Lock Column",
47794     unlockText : "Unlock Column",
47795     columnsText : "Columns"
47796 });
47797
47798
47799 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
47800     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
47801     this.proxy.el.addClass('x-grid3-col-dd');
47802 };
47803
47804 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
47805     handleMouseDown : function(e){
47806
47807     },
47808
47809     callHandleMouseDown : function(e){
47810         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
47811     }
47812 });
47813 /*
47814  * Based on:
47815  * Ext JS Library 1.1.1
47816  * Copyright(c) 2006-2007, Ext JS, LLC.
47817  *
47818  * Originally Released Under LGPL - original licence link has changed is not relivant.
47819  *
47820  * Fork - LGPL
47821  * <script type="text/javascript">
47822  */
47823  
47824 // private
47825 // This is a support class used internally by the Grid components
47826 Roo.grid.SplitDragZone = function(grid, hd, hd2){
47827     this.grid = grid;
47828     this.view = grid.getView();
47829     this.proxy = this.view.resizeProxy;
47830     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
47831         "gridSplitters" + this.grid.getGridEl().id, {
47832         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
47833     });
47834     this.setHandleElId(Roo.id(hd));
47835     this.setOuterHandleElId(Roo.id(hd2));
47836     this.scroll = false;
47837 };
47838 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
47839     fly: Roo.Element.fly,
47840
47841     b4StartDrag : function(x, y){
47842         this.view.headersDisabled = true;
47843         this.proxy.setHeight(this.view.mainWrap.getHeight());
47844         var w = this.cm.getColumnWidth(this.cellIndex);
47845         var minw = Math.max(w-this.grid.minColumnWidth, 0);
47846         this.resetConstraints();
47847         this.setXConstraint(minw, 1000);
47848         this.setYConstraint(0, 0);
47849         this.minX = x - minw;
47850         this.maxX = x + 1000;
47851         this.startPos = x;
47852         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
47853     },
47854
47855
47856     handleMouseDown : function(e){
47857         ev = Roo.EventObject.setEvent(e);
47858         var t = this.fly(ev.getTarget());
47859         if(t.hasClass("x-grid-split")){
47860             this.cellIndex = this.view.getCellIndex(t.dom);
47861             this.split = t.dom;
47862             this.cm = this.grid.colModel;
47863             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
47864                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
47865             }
47866         }
47867     },
47868
47869     endDrag : function(e){
47870         this.view.headersDisabled = false;
47871         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
47872         var diff = endX - this.startPos;
47873         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
47874     },
47875
47876     autoOffset : function(){
47877         this.setDelta(0,0);
47878     }
47879 });/*
47880  * Based on:
47881  * Ext JS Library 1.1.1
47882  * Copyright(c) 2006-2007, Ext JS, LLC.
47883  *
47884  * Originally Released Under LGPL - original licence link has changed is not relivant.
47885  *
47886  * Fork - LGPL
47887  * <script type="text/javascript">
47888  */
47889  
47890 // private
47891 // This is a support class used internally by the Grid components
47892 Roo.grid.GridDragZone = function(grid, config){
47893     this.view = grid.getView();
47894     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
47895     if(this.view.lockedBody){
47896         this.setHandleElId(Roo.id(this.view.mainBody.dom));
47897         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
47898     }
47899     this.scroll = false;
47900     this.grid = grid;
47901     this.ddel = document.createElement('div');
47902     this.ddel.className = 'x-grid-dd-wrap';
47903 };
47904
47905 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
47906     ddGroup : "GridDD",
47907
47908     getDragData : function(e){
47909         var t = Roo.lib.Event.getTarget(e);
47910         var rowIndex = this.view.findRowIndex(t);
47911         if(rowIndex !== false){
47912             var sm = this.grid.selModel;
47913             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
47914               //  sm.mouseDown(e, t);
47915             //}
47916             if (e.hasModifier()){
47917                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
47918             }
47919             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
47920         }
47921         return false;
47922     },
47923
47924     onInitDrag : function(e){
47925         var data = this.dragData;
47926         this.ddel.innerHTML = this.grid.getDragDropText();
47927         this.proxy.update(this.ddel);
47928         // fire start drag?
47929     },
47930
47931     afterRepair : function(){
47932         this.dragging = false;
47933     },
47934
47935     getRepairXY : function(e, data){
47936         return false;
47937     },
47938
47939     onEndDrag : function(data, e){
47940         // fire end drag?
47941     },
47942
47943     onValidDrop : function(dd, e, id){
47944         // fire drag drop?
47945         this.hideProxy();
47946     },
47947
47948     beforeInvalidDrop : function(e, id){
47949
47950     }
47951 });/*
47952  * Based on:
47953  * Ext JS Library 1.1.1
47954  * Copyright(c) 2006-2007, Ext JS, LLC.
47955  *
47956  * Originally Released Under LGPL - original licence link has changed is not relivant.
47957  *
47958  * Fork - LGPL
47959  * <script type="text/javascript">
47960  */
47961  
47962
47963 /**
47964  * @class Roo.grid.ColumnModel
47965  * @extends Roo.util.Observable
47966  * This is the default implementation of a ColumnModel used by the Grid. It defines
47967  * the columns in the grid.
47968  * <br>Usage:<br>
47969  <pre><code>
47970  var colModel = new Roo.grid.ColumnModel([
47971         {header: "Ticker", width: 60, sortable: true, locked: true},
47972         {header: "Company Name", width: 150, sortable: true},
47973         {header: "Market Cap.", width: 100, sortable: true},
47974         {header: "$ Sales", width: 100, sortable: true, renderer: money},
47975         {header: "Employees", width: 100, sortable: true, resizable: false}
47976  ]);
47977  </code></pre>
47978  * <p>
47979  
47980  * The config options listed for this class are options which may appear in each
47981  * individual column definition.
47982  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
47983  * @constructor
47984  * @param {Object} config An Array of column config objects. See this class's
47985  * config objects for details.
47986 */
47987 Roo.grid.ColumnModel = function(config){
47988         /**
47989      * The config passed into the constructor
47990      */
47991     this.config = config;
47992     this.lookup = {};
47993
47994     // if no id, create one
47995     // if the column does not have a dataIndex mapping,
47996     // map it to the order it is in the config
47997     for(var i = 0, len = config.length; i < len; i++){
47998         var c = config[i];
47999         if(typeof c.dataIndex == "undefined"){
48000             c.dataIndex = i;
48001         }
48002         if(typeof c.renderer == "string"){
48003             c.renderer = Roo.util.Format[c.renderer];
48004         }
48005         if(typeof c.id == "undefined"){
48006             c.id = Roo.id();
48007         }
48008         if(c.editor && c.editor.xtype){
48009             c.editor  = Roo.factory(c.editor, Roo.grid);
48010         }
48011         if(c.editor && c.editor.isFormField){
48012             c.editor = new Roo.grid.GridEditor(c.editor);
48013         }
48014         this.lookup[c.id] = c;
48015     }
48016
48017     /**
48018      * The width of columns which have no width specified (defaults to 100)
48019      * @type Number
48020      */
48021     this.defaultWidth = 100;
48022
48023     /**
48024      * Default sortable of columns which have no sortable specified (defaults to false)
48025      * @type Boolean
48026      */
48027     this.defaultSortable = false;
48028
48029     this.addEvents({
48030         /**
48031              * @event widthchange
48032              * Fires when the width of a column changes.
48033              * @param {ColumnModel} this
48034              * @param {Number} columnIndex The column index
48035              * @param {Number} newWidth The new width
48036              */
48037             "widthchange": true,
48038         /**
48039              * @event headerchange
48040              * Fires when the text of a header changes.
48041              * @param {ColumnModel} this
48042              * @param {Number} columnIndex The column index
48043              * @param {Number} newText The new header text
48044              */
48045             "headerchange": true,
48046         /**
48047              * @event hiddenchange
48048              * Fires when a column is hidden or "unhidden".
48049              * @param {ColumnModel} this
48050              * @param {Number} columnIndex The column index
48051              * @param {Boolean} hidden true if hidden, false otherwise
48052              */
48053             "hiddenchange": true,
48054             /**
48055          * @event columnmoved
48056          * Fires when a column is moved.
48057          * @param {ColumnModel} this
48058          * @param {Number} oldIndex
48059          * @param {Number} newIndex
48060          */
48061         "columnmoved" : true,
48062         /**
48063          * @event columlockchange
48064          * Fires when a column's locked state is changed
48065          * @param {ColumnModel} this
48066          * @param {Number} colIndex
48067          * @param {Boolean} locked true if locked
48068          */
48069         "columnlockchange" : true
48070     });
48071     Roo.grid.ColumnModel.superclass.constructor.call(this);
48072 };
48073 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48074     /**
48075      * @cfg {String} header The header text to display in the Grid view.
48076      */
48077     /**
48078      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48079      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48080      * specified, the column's index is used as an index into the Record's data Array.
48081      */
48082     /**
48083      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48084      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48085      */
48086     /**
48087      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48088      * Defaults to the value of the {@link #defaultSortable} property.
48089      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48090      */
48091     /**
48092      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48093      */
48094     /**
48095      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48096      */
48097     /**
48098      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48099      */
48100     /**
48101      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48102      */
48103     /**
48104      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48105      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48106      * default renderer uses the raw data value.
48107      */
48108        /**
48109      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48110      */
48111     /**
48112      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48113      */
48114
48115     /**
48116      * Returns the id of the column at the specified index.
48117      * @param {Number} index The column index
48118      * @return {String} the id
48119      */
48120     getColumnId : function(index){
48121         return this.config[index].id;
48122     },
48123
48124     /**
48125      * Returns the column for a specified id.
48126      * @param {String} id The column id
48127      * @return {Object} the column
48128      */
48129     getColumnById : function(id){
48130         return this.lookup[id];
48131     },
48132
48133     
48134     /**
48135      * Returns the column for a specified dataIndex.
48136      * @param {String} dataIndex The column dataIndex
48137      * @return {Object|Boolean} the column or false if not found
48138      */
48139     getColumnByDataIndex: function(dataIndex){
48140         var index = this.findColumnIndex(dataIndex);
48141         return index > -1 ? this.config[index] : false;
48142     },
48143     
48144     /**
48145      * Returns the index for a specified column id.
48146      * @param {String} id The column id
48147      * @return {Number} the index, or -1 if not found
48148      */
48149     getIndexById : function(id){
48150         for(var i = 0, len = this.config.length; i < len; i++){
48151             if(this.config[i].id == id){
48152                 return i;
48153             }
48154         }
48155         return -1;
48156     },
48157     
48158     /**
48159      * Returns the index for a specified column dataIndex.
48160      * @param {String} dataIndex The column dataIndex
48161      * @return {Number} the index, or -1 if not found
48162      */
48163     
48164     findColumnIndex : function(dataIndex){
48165         for(var i = 0, len = this.config.length; i < len; i++){
48166             if(this.config[i].dataIndex == dataIndex){
48167                 return i;
48168             }
48169         }
48170         return -1;
48171     },
48172     
48173     
48174     moveColumn : function(oldIndex, newIndex){
48175         var c = this.config[oldIndex];
48176         this.config.splice(oldIndex, 1);
48177         this.config.splice(newIndex, 0, c);
48178         this.dataMap = null;
48179         this.fireEvent("columnmoved", this, oldIndex, newIndex);
48180     },
48181
48182     isLocked : function(colIndex){
48183         return this.config[colIndex].locked === true;
48184     },
48185
48186     setLocked : function(colIndex, value, suppressEvent){
48187         if(this.isLocked(colIndex) == value){
48188             return;
48189         }
48190         this.config[colIndex].locked = value;
48191         if(!suppressEvent){
48192             this.fireEvent("columnlockchange", this, colIndex, value);
48193         }
48194     },
48195
48196     getTotalLockedWidth : function(){
48197         var totalWidth = 0;
48198         for(var i = 0; i < this.config.length; i++){
48199             if(this.isLocked(i) && !this.isHidden(i)){
48200                 this.totalWidth += this.getColumnWidth(i);
48201             }
48202         }
48203         return totalWidth;
48204     },
48205
48206     getLockedCount : function(){
48207         for(var i = 0, len = this.config.length; i < len; i++){
48208             if(!this.isLocked(i)){
48209                 return i;
48210             }
48211         }
48212     },
48213
48214     /**
48215      * Returns the number of columns.
48216      * @return {Number}
48217      */
48218     getColumnCount : function(visibleOnly){
48219         if(visibleOnly === true){
48220             var c = 0;
48221             for(var i = 0, len = this.config.length; i < len; i++){
48222                 if(!this.isHidden(i)){
48223                     c++;
48224                 }
48225             }
48226             return c;
48227         }
48228         return this.config.length;
48229     },
48230
48231     /**
48232      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
48233      * @param {Function} fn
48234      * @param {Object} scope (optional)
48235      * @return {Array} result
48236      */
48237     getColumnsBy : function(fn, scope){
48238         var r = [];
48239         for(var i = 0, len = this.config.length; i < len; i++){
48240             var c = this.config[i];
48241             if(fn.call(scope||this, c, i) === true){
48242                 r[r.length] = c;
48243             }
48244         }
48245         return r;
48246     },
48247
48248     /**
48249      * Returns true if the specified column is sortable.
48250      * @param {Number} col The column index
48251      * @return {Boolean}
48252      */
48253     isSortable : function(col){
48254         if(typeof this.config[col].sortable == "undefined"){
48255             return this.defaultSortable;
48256         }
48257         return this.config[col].sortable;
48258     },
48259
48260     /**
48261      * Returns the rendering (formatting) function defined for the column.
48262      * @param {Number} col The column index.
48263      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
48264      */
48265     getRenderer : function(col){
48266         if(!this.config[col].renderer){
48267             return Roo.grid.ColumnModel.defaultRenderer;
48268         }
48269         return this.config[col].renderer;
48270     },
48271
48272     /**
48273      * Sets the rendering (formatting) function for a column.
48274      * @param {Number} col The column index
48275      * @param {Function} fn The function to use to process the cell's raw data
48276      * to return HTML markup for the grid view. The render function is called with
48277      * the following parameters:<ul>
48278      * <li>Data value.</li>
48279      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
48280      * <li>css A CSS style string to apply to the table cell.</li>
48281      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
48282      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
48283      * <li>Row index</li>
48284      * <li>Column index</li>
48285      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
48286      */
48287     setRenderer : function(col, fn){
48288         this.config[col].renderer = fn;
48289     },
48290
48291     /**
48292      * Returns the width for the specified column.
48293      * @param {Number} col The column index
48294      * @return {Number}
48295      */
48296     getColumnWidth : function(col){
48297         return this.config[col].width || this.defaultWidth;
48298     },
48299
48300     /**
48301      * Sets the width for a column.
48302      * @param {Number} col The column index
48303      * @param {Number} width The new width
48304      */
48305     setColumnWidth : function(col, width, suppressEvent){
48306         this.config[col].width = width;
48307         this.totalWidth = null;
48308         if(!suppressEvent){
48309              this.fireEvent("widthchange", this, col, width);
48310         }
48311     },
48312
48313     /**
48314      * Returns the total width of all columns.
48315      * @param {Boolean} includeHidden True to include hidden column widths
48316      * @return {Number}
48317      */
48318     getTotalWidth : function(includeHidden){
48319         if(!this.totalWidth){
48320             this.totalWidth = 0;
48321             for(var i = 0, len = this.config.length; i < len; i++){
48322                 if(includeHidden || !this.isHidden(i)){
48323                     this.totalWidth += this.getColumnWidth(i);
48324                 }
48325             }
48326         }
48327         return this.totalWidth;
48328     },
48329
48330     /**
48331      * Returns the header for the specified column.
48332      * @param {Number} col The column index
48333      * @return {String}
48334      */
48335     getColumnHeader : function(col){
48336         return this.config[col].header;
48337     },
48338
48339     /**
48340      * Sets the header for a column.
48341      * @param {Number} col The column index
48342      * @param {String} header The new header
48343      */
48344     setColumnHeader : function(col, header){
48345         this.config[col].header = header;
48346         this.fireEvent("headerchange", this, col, header);
48347     },
48348
48349     /**
48350      * Returns the tooltip for the specified column.
48351      * @param {Number} col The column index
48352      * @return {String}
48353      */
48354     getColumnTooltip : function(col){
48355             return this.config[col].tooltip;
48356     },
48357     /**
48358      * Sets the tooltip for a column.
48359      * @param {Number} col The column index
48360      * @param {String} tooltip The new tooltip
48361      */
48362     setColumnTooltip : function(col, tooltip){
48363             this.config[col].tooltip = tooltip;
48364     },
48365
48366     /**
48367      * Returns the dataIndex for the specified column.
48368      * @param {Number} col The column index
48369      * @return {Number}
48370      */
48371     getDataIndex : function(col){
48372         return this.config[col].dataIndex;
48373     },
48374
48375     /**
48376      * Sets the dataIndex for a column.
48377      * @param {Number} col The column index
48378      * @param {Number} dataIndex The new dataIndex
48379      */
48380     setDataIndex : function(col, dataIndex){
48381         this.config[col].dataIndex = dataIndex;
48382     },
48383
48384     
48385     
48386     /**
48387      * Returns true if the cell is editable.
48388      * @param {Number} colIndex The column index
48389      * @param {Number} rowIndex The row index
48390      * @return {Boolean}
48391      */
48392     isCellEditable : function(colIndex, rowIndex){
48393         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
48394     },
48395
48396     /**
48397      * Returns the editor defined for the cell/column.
48398      * return false or null to disable editing.
48399      * @param {Number} colIndex The column index
48400      * @param {Number} rowIndex The row index
48401      * @return {Object}
48402      */
48403     getCellEditor : function(colIndex, rowIndex){
48404         return this.config[colIndex].editor;
48405     },
48406
48407     /**
48408      * Sets if a column is editable.
48409      * @param {Number} col The column index
48410      * @param {Boolean} editable True if the column is editable
48411      */
48412     setEditable : function(col, editable){
48413         this.config[col].editable = editable;
48414     },
48415
48416
48417     /**
48418      * Returns true if the column is hidden.
48419      * @param {Number} colIndex The column index
48420      * @return {Boolean}
48421      */
48422     isHidden : function(colIndex){
48423         return this.config[colIndex].hidden;
48424     },
48425
48426
48427     /**
48428      * Returns true if the column width cannot be changed
48429      */
48430     isFixed : function(colIndex){
48431         return this.config[colIndex].fixed;
48432     },
48433
48434     /**
48435      * Returns true if the column can be resized
48436      * @return {Boolean}
48437      */
48438     isResizable : function(colIndex){
48439         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
48440     },
48441     /**
48442      * Sets if a column is hidden.
48443      * @param {Number} colIndex The column index
48444      * @param {Boolean} hidden True if the column is hidden
48445      */
48446     setHidden : function(colIndex, hidden){
48447         this.config[colIndex].hidden = hidden;
48448         this.totalWidth = null;
48449         this.fireEvent("hiddenchange", this, colIndex, hidden);
48450     },
48451
48452     /**
48453      * Sets the editor for a column.
48454      * @param {Number} col The column index
48455      * @param {Object} editor The editor object
48456      */
48457     setEditor : function(col, editor){
48458         this.config[col].editor = editor;
48459     }
48460 });
48461
48462 Roo.grid.ColumnModel.defaultRenderer = function(value){
48463         if(typeof value == "string" && value.length < 1){
48464             return "&#160;";
48465         }
48466         return value;
48467 };
48468
48469 // Alias for backwards compatibility
48470 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
48471 /*
48472  * Based on:
48473  * Ext JS Library 1.1.1
48474  * Copyright(c) 2006-2007, Ext JS, LLC.
48475  *
48476  * Originally Released Under LGPL - original licence link has changed is not relivant.
48477  *
48478  * Fork - LGPL
48479  * <script type="text/javascript">
48480  */
48481
48482 /**
48483  * @class Roo.grid.AbstractSelectionModel
48484  * @extends Roo.util.Observable
48485  * Abstract base class for grid SelectionModels.  It provides the interface that should be
48486  * implemented by descendant classes.  This class should not be directly instantiated.
48487  * @constructor
48488  */
48489 Roo.grid.AbstractSelectionModel = function(){
48490     this.locked = false;
48491     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
48492 };
48493
48494 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
48495     /** @ignore Called by the grid automatically. Do not call directly. */
48496     init : function(grid){
48497         this.grid = grid;
48498         this.initEvents();
48499     },
48500
48501     /**
48502      * Locks the selections.
48503      */
48504     lock : function(){
48505         this.locked = true;
48506     },
48507
48508     /**
48509      * Unlocks the selections.
48510      */
48511     unlock : function(){
48512         this.locked = false;
48513     },
48514
48515     /**
48516      * Returns true if the selections are locked.
48517      * @return {Boolean}
48518      */
48519     isLocked : function(){
48520         return this.locked;
48521     }
48522 });/*
48523  * Based on:
48524  * Ext JS Library 1.1.1
48525  * Copyright(c) 2006-2007, Ext JS, LLC.
48526  *
48527  * Originally Released Under LGPL - original licence link has changed is not relivant.
48528  *
48529  * Fork - LGPL
48530  * <script type="text/javascript">
48531  */
48532 /**
48533  * @extends Roo.grid.AbstractSelectionModel
48534  * @class Roo.grid.RowSelectionModel
48535  * The default SelectionModel used by {@link Roo.grid.Grid}.
48536  * It supports multiple selections and keyboard selection/navigation. 
48537  * @constructor
48538  * @param {Object} config
48539  */
48540 Roo.grid.RowSelectionModel = function(config){
48541     Roo.apply(this, config);
48542     this.selections = new Roo.util.MixedCollection(false, function(o){
48543         return o.id;
48544     });
48545
48546     this.last = false;
48547     this.lastActive = false;
48548
48549     this.addEvents({
48550         /**
48551              * @event selectionchange
48552              * Fires when the selection changes
48553              * @param {SelectionModel} this
48554              */
48555             "selectionchange" : true,
48556         /**
48557              * @event afterselectionchange
48558              * Fires after the selection changes (eg. by key press or clicking)
48559              * @param {SelectionModel} this
48560              */
48561             "afterselectionchange" : true,
48562         /**
48563              * @event beforerowselect
48564              * Fires when a row is selected being selected, return false to cancel.
48565              * @param {SelectionModel} this
48566              * @param {Number} rowIndex The selected index
48567              * @param {Boolean} keepExisting False if other selections will be cleared
48568              */
48569             "beforerowselect" : true,
48570         /**
48571              * @event rowselect
48572              * Fires when a row is selected.
48573              * @param {SelectionModel} this
48574              * @param {Number} rowIndex The selected index
48575              * @param {Roo.data.Record} r The record
48576              */
48577             "rowselect" : true,
48578         /**
48579              * @event rowdeselect
48580              * Fires when a row is deselected.
48581              * @param {SelectionModel} this
48582              * @param {Number} rowIndex The selected index
48583              */
48584         "rowdeselect" : true
48585     });
48586     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
48587     this.locked = false;
48588 };
48589
48590 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
48591     /**
48592      * @cfg {Boolean} singleSelect
48593      * True to allow selection of only one row at a time (defaults to false)
48594      */
48595     singleSelect : false,
48596
48597     // private
48598     initEvents : function(){
48599
48600         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
48601             this.grid.on("mousedown", this.handleMouseDown, this);
48602         }else{ // allow click to work like normal
48603             this.grid.on("rowclick", this.handleDragableRowClick, this);
48604         }
48605
48606         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
48607             "up" : function(e){
48608                 if(!e.shiftKey){
48609                     this.selectPrevious(e.shiftKey);
48610                 }else if(this.last !== false && this.lastActive !== false){
48611                     var last = this.last;
48612                     this.selectRange(this.last,  this.lastActive-1);
48613                     this.grid.getView().focusRow(this.lastActive);
48614                     if(last !== false){
48615                         this.last = last;
48616                     }
48617                 }else{
48618                     this.selectFirstRow();
48619                 }
48620                 this.fireEvent("afterselectionchange", this);
48621             },
48622             "down" : function(e){
48623                 if(!e.shiftKey){
48624                     this.selectNext(e.shiftKey);
48625                 }else if(this.last !== false && this.lastActive !== false){
48626                     var last = this.last;
48627                     this.selectRange(this.last,  this.lastActive+1);
48628                     this.grid.getView().focusRow(this.lastActive);
48629                     if(last !== false){
48630                         this.last = last;
48631                     }
48632                 }else{
48633                     this.selectFirstRow();
48634                 }
48635                 this.fireEvent("afterselectionchange", this);
48636             },
48637             scope: this
48638         });
48639
48640         var view = this.grid.view;
48641         view.on("refresh", this.onRefresh, this);
48642         view.on("rowupdated", this.onRowUpdated, this);
48643         view.on("rowremoved", this.onRemove, this);
48644     },
48645
48646     // private
48647     onRefresh : function(){
48648         var ds = this.grid.dataSource, i, v = this.grid.view;
48649         var s = this.selections;
48650         s.each(function(r){
48651             if((i = ds.indexOfId(r.id)) != -1){
48652                 v.onRowSelect(i);
48653             }else{
48654                 s.remove(r);
48655             }
48656         });
48657     },
48658
48659     // private
48660     onRemove : function(v, index, r){
48661         this.selections.remove(r);
48662     },
48663
48664     // private
48665     onRowUpdated : function(v, index, r){
48666         if(this.isSelected(r)){
48667             v.onRowSelect(index);
48668         }
48669     },
48670
48671     /**
48672      * Select records.
48673      * @param {Array} records The records to select
48674      * @param {Boolean} keepExisting (optional) True to keep existing selections
48675      */
48676     selectRecords : function(records, keepExisting){
48677         if(!keepExisting){
48678             this.clearSelections();
48679         }
48680         var ds = this.grid.dataSource;
48681         for(var i = 0, len = records.length; i < len; i++){
48682             this.selectRow(ds.indexOf(records[i]), true);
48683         }
48684     },
48685
48686     /**
48687      * Gets the number of selected rows.
48688      * @return {Number}
48689      */
48690     getCount : function(){
48691         return this.selections.length;
48692     },
48693
48694     /**
48695      * Selects the first row in the grid.
48696      */
48697     selectFirstRow : function(){
48698         this.selectRow(0);
48699     },
48700
48701     /**
48702      * Select the last row.
48703      * @param {Boolean} keepExisting (optional) True to keep existing selections
48704      */
48705     selectLastRow : function(keepExisting){
48706         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
48707     },
48708
48709     /**
48710      * Selects the row immediately following the last selected row.
48711      * @param {Boolean} keepExisting (optional) True to keep existing selections
48712      */
48713     selectNext : function(keepExisting){
48714         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
48715             this.selectRow(this.last+1, keepExisting);
48716             this.grid.getView().focusRow(this.last);
48717         }
48718     },
48719
48720     /**
48721      * Selects the row that precedes the last selected row.
48722      * @param {Boolean} keepExisting (optional) True to keep existing selections
48723      */
48724     selectPrevious : function(keepExisting){
48725         if(this.last){
48726             this.selectRow(this.last-1, keepExisting);
48727             this.grid.getView().focusRow(this.last);
48728         }
48729     },
48730
48731     /**
48732      * Returns the selected records
48733      * @return {Array} Array of selected records
48734      */
48735     getSelections : function(){
48736         return [].concat(this.selections.items);
48737     },
48738
48739     /**
48740      * Returns the first selected record.
48741      * @return {Record}
48742      */
48743     getSelected : function(){
48744         return this.selections.itemAt(0);
48745     },
48746
48747
48748     /**
48749      * Clears all selections.
48750      */
48751     clearSelections : function(fast){
48752         if(this.locked) return;
48753         if(fast !== true){
48754             var ds = this.grid.dataSource;
48755             var s = this.selections;
48756             s.each(function(r){
48757                 this.deselectRow(ds.indexOfId(r.id));
48758             }, this);
48759             s.clear();
48760         }else{
48761             this.selections.clear();
48762         }
48763         this.last = false;
48764     },
48765
48766
48767     /**
48768      * Selects all rows.
48769      */
48770     selectAll : function(){
48771         if(this.locked) return;
48772         this.selections.clear();
48773         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
48774             this.selectRow(i, true);
48775         }
48776     },
48777
48778     /**
48779      * Returns True if there is a selection.
48780      * @return {Boolean}
48781      */
48782     hasSelection : function(){
48783         return this.selections.length > 0;
48784     },
48785
48786     /**
48787      * Returns True if the specified row is selected.
48788      * @param {Number/Record} record The record or index of the record to check
48789      * @return {Boolean}
48790      */
48791     isSelected : function(index){
48792         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
48793         return (r && this.selections.key(r.id) ? true : false);
48794     },
48795
48796     /**
48797      * Returns True if the specified record id is selected.
48798      * @param {String} id The id of record to check
48799      * @return {Boolean}
48800      */
48801     isIdSelected : function(id){
48802         return (this.selections.key(id) ? true : false);
48803     },
48804
48805     // private
48806     handleMouseDown : function(e, t){
48807         var view = this.grid.getView(), rowIndex;
48808         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
48809             return;
48810         };
48811         if(e.shiftKey && this.last !== false){
48812             var last = this.last;
48813             this.selectRange(last, rowIndex, e.ctrlKey);
48814             this.last = last; // reset the last
48815             view.focusRow(rowIndex);
48816         }else{
48817             var isSelected = this.isSelected(rowIndex);
48818             if(e.button !== 0 && isSelected){
48819                 view.focusRow(rowIndex);
48820             }else if(e.ctrlKey && isSelected){
48821                 this.deselectRow(rowIndex);
48822             }else if(!isSelected){
48823                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
48824                 view.focusRow(rowIndex);
48825             }
48826         }
48827         this.fireEvent("afterselectionchange", this);
48828     },
48829     // private
48830     handleDragableRowClick :  function(grid, rowIndex, e) 
48831     {
48832         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
48833             this.selectRow(rowIndex, false);
48834             grid.view.focusRow(rowIndex);
48835              this.fireEvent("afterselectionchange", this);
48836         }
48837     },
48838     
48839     /**
48840      * Selects multiple rows.
48841      * @param {Array} rows Array of the indexes of the row to select
48842      * @param {Boolean} keepExisting (optional) True to keep existing selections
48843      */
48844     selectRows : function(rows, keepExisting){
48845         if(!keepExisting){
48846             this.clearSelections();
48847         }
48848         for(var i = 0, len = rows.length; i < len; i++){
48849             this.selectRow(rows[i], true);
48850         }
48851     },
48852
48853     /**
48854      * Selects a range of rows. All rows in between startRow and endRow are also selected.
48855      * @param {Number} startRow The index of the first row in the range
48856      * @param {Number} endRow The index of the last row in the range
48857      * @param {Boolean} keepExisting (optional) True to retain existing selections
48858      */
48859     selectRange : function(startRow, endRow, keepExisting){
48860         if(this.locked) return;
48861         if(!keepExisting){
48862             this.clearSelections();
48863         }
48864         if(startRow <= endRow){
48865             for(var i = startRow; i <= endRow; i++){
48866                 this.selectRow(i, true);
48867             }
48868         }else{
48869             for(var i = startRow; i >= endRow; i--){
48870                 this.selectRow(i, true);
48871             }
48872         }
48873     },
48874
48875     /**
48876      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
48877      * @param {Number} startRow The index of the first row in the range
48878      * @param {Number} endRow The index of the last row in the range
48879      */
48880     deselectRange : function(startRow, endRow, preventViewNotify){
48881         if(this.locked) return;
48882         for(var i = startRow; i <= endRow; i++){
48883             this.deselectRow(i, preventViewNotify);
48884         }
48885     },
48886
48887     /**
48888      * Selects a row.
48889      * @param {Number} row The index of the row to select
48890      * @param {Boolean} keepExisting (optional) True to keep existing selections
48891      */
48892     selectRow : function(index, keepExisting, preventViewNotify){
48893         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
48894         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
48895             if(!keepExisting || this.singleSelect){
48896                 this.clearSelections();
48897             }
48898             var r = this.grid.dataSource.getAt(index);
48899             this.selections.add(r);
48900             this.last = this.lastActive = index;
48901             if(!preventViewNotify){
48902                 this.grid.getView().onRowSelect(index);
48903             }
48904             this.fireEvent("rowselect", this, index, r);
48905             this.fireEvent("selectionchange", this);
48906         }
48907     },
48908
48909     /**
48910      * Deselects a row.
48911      * @param {Number} row The index of the row to deselect
48912      */
48913     deselectRow : function(index, preventViewNotify){
48914         if(this.locked) return;
48915         if(this.last == index){
48916             this.last = false;
48917         }
48918         if(this.lastActive == index){
48919             this.lastActive = false;
48920         }
48921         var r = this.grid.dataSource.getAt(index);
48922         this.selections.remove(r);
48923         if(!preventViewNotify){
48924             this.grid.getView().onRowDeselect(index);
48925         }
48926         this.fireEvent("rowdeselect", this, index);
48927         this.fireEvent("selectionchange", this);
48928     },
48929
48930     // private
48931     restoreLast : function(){
48932         if(this._last){
48933             this.last = this._last;
48934         }
48935     },
48936
48937     // private
48938     acceptsNav : function(row, col, cm){
48939         return !cm.isHidden(col) && cm.isCellEditable(col, row);
48940     },
48941
48942     // private
48943     onEditorKey : function(field, e){
48944         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
48945         if(k == e.TAB){
48946             e.stopEvent();
48947             ed.completeEdit();
48948             if(e.shiftKey){
48949                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
48950             }else{
48951                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
48952             }
48953         }else if(k == e.ENTER && !e.ctrlKey){
48954             e.stopEvent();
48955             ed.completeEdit();
48956             if(e.shiftKey){
48957                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
48958             }else{
48959                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
48960             }
48961         }else if(k == e.ESC){
48962             ed.cancelEdit();
48963         }
48964         if(newCell){
48965             g.startEditing(newCell[0], newCell[1]);
48966         }
48967     }
48968 });/*
48969  * Based on:
48970  * Ext JS Library 1.1.1
48971  * Copyright(c) 2006-2007, Ext JS, LLC.
48972  *
48973  * Originally Released Under LGPL - original licence link has changed is not relivant.
48974  *
48975  * Fork - LGPL
48976  * <script type="text/javascript">
48977  */
48978 /**
48979  * @class Roo.grid.CellSelectionModel
48980  * @extends Roo.grid.AbstractSelectionModel
48981  * This class provides the basic implementation for cell selection in a grid.
48982  * @constructor
48983  * @param {Object} config The object containing the configuration of this model.
48984  */
48985 Roo.grid.CellSelectionModel = function(config){
48986     Roo.apply(this, config);
48987
48988     this.selection = null;
48989
48990     this.addEvents({
48991         /**
48992              * @event beforerowselect
48993              * Fires before a cell is selected.
48994              * @param {SelectionModel} this
48995              * @param {Number} rowIndex The selected row index
48996              * @param {Number} colIndex The selected cell index
48997              */
48998             "beforecellselect" : true,
48999         /**
49000              * @event cellselect
49001              * Fires when a cell is selected.
49002              * @param {SelectionModel} this
49003              * @param {Number} rowIndex The selected row index
49004              * @param {Number} colIndex The selected cell index
49005              */
49006             "cellselect" : true,
49007         /**
49008              * @event selectionchange
49009              * Fires when the active selection changes.
49010              * @param {SelectionModel} this
49011              * @param {Object} selection null for no selection or an object (o) with two properties
49012                 <ul>
49013                 <li>o.record: the record object for the row the selection is in</li>
49014                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49015                 </ul>
49016              */
49017             "selectionchange" : true
49018     });
49019     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49020 };
49021
49022 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49023
49024     /** @ignore */
49025     initEvents : function(){
49026         this.grid.on("mousedown", this.handleMouseDown, this);
49027         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49028         var view = this.grid.view;
49029         view.on("refresh", this.onViewChange, this);
49030         view.on("rowupdated", this.onRowUpdated, this);
49031         view.on("beforerowremoved", this.clearSelections, this);
49032         view.on("beforerowsinserted", this.clearSelections, this);
49033         if(this.grid.isEditor){
49034             this.grid.on("beforeedit", this.beforeEdit,  this);
49035         }
49036     },
49037
49038         //private
49039     beforeEdit : function(e){
49040         this.select(e.row, e.column, false, true, e.record);
49041     },
49042
49043         //private
49044     onRowUpdated : function(v, index, r){
49045         if(this.selection && this.selection.record == r){
49046             v.onCellSelect(index, this.selection.cell[1]);
49047         }
49048     },
49049
49050         //private
49051     onViewChange : function(){
49052         this.clearSelections(true);
49053     },
49054
49055         /**
49056          * Returns the currently selected cell,.
49057          * @return {Array} The selected cell (row, column) or null if none selected.
49058          */
49059     getSelectedCell : function(){
49060         return this.selection ? this.selection.cell : null;
49061     },
49062
49063     /**
49064      * Clears all selections.
49065      * @param {Boolean} true to prevent the gridview from being notified about the change.
49066      */
49067     clearSelections : function(preventNotify){
49068         var s = this.selection;
49069         if(s){
49070             if(preventNotify !== true){
49071                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49072             }
49073             this.selection = null;
49074             this.fireEvent("selectionchange", this, null);
49075         }
49076     },
49077
49078     /**
49079      * Returns true if there is a selection.
49080      * @return {Boolean}
49081      */
49082     hasSelection : function(){
49083         return this.selection ? true : false;
49084     },
49085
49086     /** @ignore */
49087     handleMouseDown : function(e, t){
49088         var v = this.grid.getView();
49089         if(this.isLocked()){
49090             return;
49091         };
49092         var row = v.findRowIndex(t);
49093         var cell = v.findCellIndex(t);
49094         if(row !== false && cell !== false){
49095             this.select(row, cell);
49096         }
49097     },
49098
49099     /**
49100      * Selects a cell.
49101      * @param {Number} rowIndex
49102      * @param {Number} collIndex
49103      */
49104     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49105         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49106             this.clearSelections();
49107             r = r || this.grid.dataSource.getAt(rowIndex);
49108             this.selection = {
49109                 record : r,
49110                 cell : [rowIndex, colIndex]
49111             };
49112             if(!preventViewNotify){
49113                 var v = this.grid.getView();
49114                 v.onCellSelect(rowIndex, colIndex);
49115                 if(preventFocus !== true){
49116                     v.focusCell(rowIndex, colIndex);
49117                 }
49118             }
49119             this.fireEvent("cellselect", this, rowIndex, colIndex);
49120             this.fireEvent("selectionchange", this, this.selection);
49121         }
49122     },
49123
49124         //private
49125     isSelectable : function(rowIndex, colIndex, cm){
49126         return !cm.isHidden(colIndex);
49127     },
49128
49129     /** @ignore */
49130     handleKeyDown : function(e){
49131         if(!e.isNavKeyPress()){
49132             return;
49133         }
49134         var g = this.grid, s = this.selection;
49135         if(!s){
49136             e.stopEvent();
49137             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49138             if(cell){
49139                 this.select(cell[0], cell[1]);
49140             }
49141             return;
49142         }
49143         var sm = this;
49144         var walk = function(row, col, step){
49145             return g.walkCells(row, col, step, sm.isSelectable,  sm);
49146         };
49147         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
49148         var newCell;
49149
49150         switch(k){
49151              case e.TAB:
49152                  if(e.shiftKey){
49153                      newCell = walk(r, c-1, -1);
49154                  }else{
49155                      newCell = walk(r, c+1, 1);
49156                  }
49157              break;
49158              case e.DOWN:
49159                  newCell = walk(r+1, c, 1);
49160              break;
49161              case e.UP:
49162                  newCell = walk(r-1, c, -1);
49163              break;
49164              case e.RIGHT:
49165                  newCell = walk(r, c+1, 1);
49166              break;
49167              case e.LEFT:
49168                  newCell = walk(r, c-1, -1);
49169              break;
49170              case e.ENTER:
49171                  if(g.isEditor && !g.editing){
49172                     g.startEditing(r, c);
49173                     e.stopEvent();
49174                     return;
49175                 }
49176              break;
49177         };
49178         if(newCell){
49179             this.select(newCell[0], newCell[1]);
49180             e.stopEvent();
49181         }
49182     },
49183
49184     acceptsNav : function(row, col, cm){
49185         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49186     },
49187
49188     onEditorKey : function(field, e){
49189         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49190         if(k == e.TAB){
49191             if(e.shiftKey){
49192                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49193             }else{
49194                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49195             }
49196             e.stopEvent();
49197         }else if(k == e.ENTER && !e.ctrlKey){
49198             ed.completeEdit();
49199             e.stopEvent();
49200         }else if(k == e.ESC){
49201             ed.cancelEdit();
49202         }
49203         if(newCell){
49204             g.startEditing(newCell[0], newCell[1]);
49205         }
49206     }
49207 });/*
49208  * Based on:
49209  * Ext JS Library 1.1.1
49210  * Copyright(c) 2006-2007, Ext JS, LLC.
49211  *
49212  * Originally Released Under LGPL - original licence link has changed is not relivant.
49213  *
49214  * Fork - LGPL
49215  * <script type="text/javascript">
49216  */
49217  
49218 /**
49219  * @class Roo.grid.EditorGrid
49220  * @extends Roo.grid.Grid
49221  * Class for creating and editable grid.
49222  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
49223  * The container MUST have some type of size defined for the grid to fill. The container will be 
49224  * automatically set to position relative if it isn't already.
49225  * @param {Object} dataSource The data model to bind to
49226  * @param {Object} colModel The column model with info about this grid's columns
49227  */
49228 Roo.grid.EditorGrid = function(container, config){
49229     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
49230     this.getGridEl().addClass("xedit-grid");
49231
49232     if(!this.selModel){
49233         this.selModel = new Roo.grid.CellSelectionModel();
49234     }
49235
49236     this.activeEditor = null;
49237
49238         this.addEvents({
49239             /**
49240              * @event beforeedit
49241              * Fires before cell editing is triggered. The edit event object has the following properties <br />
49242              * <ul style="padding:5px;padding-left:16px;">
49243              * <li>grid - This grid</li>
49244              * <li>record - The record being edited</li>
49245              * <li>field - The field name being edited</li>
49246              * <li>value - The value for the field being edited.</li>
49247              * <li>row - The grid row index</li>
49248              * <li>column - The grid column index</li>
49249              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49250              * </ul>
49251              * @param {Object} e An edit event (see above for description)
49252              */
49253             "beforeedit" : true,
49254             /**
49255              * @event afteredit
49256              * Fires after a cell is edited. <br />
49257              * <ul style="padding:5px;padding-left:16px;">
49258              * <li>grid - This grid</li>
49259              * <li>record - The record being edited</li>
49260              * <li>field - The field name being edited</li>
49261              * <li>value - The value being set</li>
49262              * <li>originalValue - The original value for the field, before the edit.</li>
49263              * <li>row - The grid row index</li>
49264              * <li>column - The grid column index</li>
49265              * </ul>
49266              * @param {Object} e An edit event (see above for description)
49267              */
49268             "afteredit" : true,
49269             /**
49270              * @event validateedit
49271              * Fires after a cell is edited, but before the value is set in the record. 
49272          * You can use this to modify the value being set in the field, Return false
49273              * to cancel the change. The edit event object has the following properties <br />
49274              * <ul style="padding:5px;padding-left:16px;">
49275          * <li>editor - This editor</li>
49276              * <li>grid - This grid</li>
49277              * <li>record - The record being edited</li>
49278              * <li>field - The field name being edited</li>
49279              * <li>value - The value being set</li>
49280              * <li>originalValue - The original value for the field, before the edit.</li>
49281              * <li>row - The grid row index</li>
49282              * <li>column - The grid column index</li>
49283              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
49284              * </ul>
49285              * @param {Object} e An edit event (see above for description)
49286              */
49287             "validateedit" : true
49288         });
49289     this.on("bodyscroll", this.stopEditing,  this);
49290     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
49291 };
49292
49293 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
49294     /**
49295      * @cfg {Number} clicksToEdit
49296      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
49297      */
49298     clicksToEdit: 2,
49299
49300     // private
49301     isEditor : true,
49302     // private
49303     trackMouseOver: false, // causes very odd FF errors
49304
49305     onCellDblClick : function(g, row, col){
49306         this.startEditing(row, col);
49307     },
49308
49309     onEditComplete : function(ed, value, startValue){
49310         this.editing = false;
49311         this.activeEditor = null;
49312         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
49313         var r = ed.record;
49314         var field = this.colModel.getDataIndex(ed.col);
49315         var e = {
49316             grid: this,
49317             record: r,
49318             field: field,
49319             originalValue: startValue,
49320             value: value,
49321             row: ed.row,
49322             column: ed.col,
49323             cancel:false,
49324             editor: ed
49325         };
49326         if(String(value) !== String(startValue)){
49327             
49328             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
49329                 r.set(field, e.value);
49330                 delete e.cancel; //?? why!!!
49331                 this.fireEvent("afteredit", e);
49332             }
49333         } else {
49334             this.fireEvent("afteredit", e); // always fir it!
49335         }
49336         this.view.focusCell(ed.row, ed.col);
49337     },
49338
49339     /**
49340      * Starts editing the specified for the specified row/column
49341      * @param {Number} rowIndex
49342      * @param {Number} colIndex
49343      */
49344     startEditing : function(row, col){
49345         this.stopEditing();
49346         if(this.colModel.isCellEditable(col, row)){
49347             this.view.ensureVisible(row, col, true);
49348             var r = this.dataSource.getAt(row);
49349             var field = this.colModel.getDataIndex(col);
49350             var e = {
49351                 grid: this,
49352                 record: r,
49353                 field: field,
49354                 value: r.data[field],
49355                 row: row,
49356                 column: col,
49357                 cancel:false
49358             };
49359             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
49360                 this.editing = true;
49361                 var ed = this.colModel.getCellEditor(col, row);
49362                 
49363                 if (!ed) {
49364                     return;
49365                 }
49366                 if(!ed.rendered){
49367                     ed.render(ed.parentEl || document.body);
49368                 }
49369                 ed.field.reset();
49370                 (function(){ // complex but required for focus issues in safari, ie and opera
49371                     ed.row = row;
49372                     ed.col = col;
49373                     ed.record = r;
49374                     ed.on("complete", this.onEditComplete, this, {single: true});
49375                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
49376                     this.activeEditor = ed;
49377                     var v = r.data[field];
49378                     ed.startEdit(this.view.getCell(row, col), v);
49379                 }).defer(50, this);
49380             }
49381         }
49382     },
49383         
49384     /**
49385      * Stops any active editing
49386      */
49387     stopEditing : function(){
49388         if(this.activeEditor){
49389             this.activeEditor.completeEdit();
49390         }
49391         this.activeEditor = null;
49392     }
49393 });/*
49394  * Based on:
49395  * Ext JS Library 1.1.1
49396  * Copyright(c) 2006-2007, Ext JS, LLC.
49397  *
49398  * Originally Released Under LGPL - original licence link has changed is not relivant.
49399  *
49400  * Fork - LGPL
49401  * <script type="text/javascript">
49402  */
49403
49404 // private - not really -- you end up using it !
49405 // This is a support class used internally by the Grid components
49406
49407 /**
49408  * @class Roo.grid.GridEditor
49409  * @extends Roo.Editor
49410  * Class for creating and editable grid elements.
49411  * @param {Object} config any settings (must include field)
49412  */
49413 Roo.grid.GridEditor = function(field, config){
49414     if (!config && field.field) {
49415         config = field;
49416         field = Roo.factory(config.field, Roo.form);
49417     }
49418     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
49419     field.monitorTab = false;
49420 };
49421
49422 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
49423     
49424     /**
49425      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
49426      */
49427     
49428     alignment: "tl-tl",
49429     autoSize: "width",
49430     hideEl : false,
49431     cls: "x-small-editor x-grid-editor",
49432     shim:false,
49433     shadow:"frame"
49434 });/*
49435  * Based on:
49436  * Ext JS Library 1.1.1
49437  * Copyright(c) 2006-2007, Ext JS, LLC.
49438  *
49439  * Originally Released Under LGPL - original licence link has changed is not relivant.
49440  *
49441  * Fork - LGPL
49442  * <script type="text/javascript">
49443  */
49444   
49445
49446   
49447 Roo.grid.PropertyRecord = Roo.data.Record.create([
49448     {name:'name',type:'string'},  'value'
49449 ]);
49450
49451
49452 Roo.grid.PropertyStore = function(grid, source){
49453     this.grid = grid;
49454     this.store = new Roo.data.Store({
49455         recordType : Roo.grid.PropertyRecord
49456     });
49457     this.store.on('update', this.onUpdate,  this);
49458     if(source){
49459         this.setSource(source);
49460     }
49461     Roo.grid.PropertyStore.superclass.constructor.call(this);
49462 };
49463
49464
49465
49466 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
49467     setSource : function(o){
49468         this.source = o;
49469         this.store.removeAll();
49470         var data = [];
49471         for(var k in o){
49472             if(this.isEditableValue(o[k])){
49473                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
49474             }
49475         }
49476         this.store.loadRecords({records: data}, {}, true);
49477     },
49478
49479     onUpdate : function(ds, record, type){
49480         if(type == Roo.data.Record.EDIT){
49481             var v = record.data['value'];
49482             var oldValue = record.modified['value'];
49483             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
49484                 this.source[record.id] = v;
49485                 record.commit();
49486                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
49487             }else{
49488                 record.reject();
49489             }
49490         }
49491     },
49492
49493     getProperty : function(row){
49494        return this.store.getAt(row);
49495     },
49496
49497     isEditableValue: function(val){
49498         if(val && val instanceof Date){
49499             return true;
49500         }else if(typeof val == 'object' || typeof val == 'function'){
49501             return false;
49502         }
49503         return true;
49504     },
49505
49506     setValue : function(prop, value){
49507         this.source[prop] = value;
49508         this.store.getById(prop).set('value', value);
49509     },
49510
49511     getSource : function(){
49512         return this.source;
49513     }
49514 });
49515
49516 Roo.grid.PropertyColumnModel = function(grid, store){
49517     this.grid = grid;
49518     var g = Roo.grid;
49519     g.PropertyColumnModel.superclass.constructor.call(this, [
49520         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
49521         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
49522     ]);
49523     this.store = store;
49524     this.bselect = Roo.DomHelper.append(document.body, {
49525         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
49526             {tag: 'option', value: 'true', html: 'true'},
49527             {tag: 'option', value: 'false', html: 'false'}
49528         ]
49529     });
49530     Roo.id(this.bselect);
49531     var f = Roo.form;
49532     this.editors = {
49533         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
49534         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
49535         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
49536         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
49537         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
49538     };
49539     this.renderCellDelegate = this.renderCell.createDelegate(this);
49540     this.renderPropDelegate = this.renderProp.createDelegate(this);
49541 };
49542
49543 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
49544     
49545     
49546     nameText : 'Name',
49547     valueText : 'Value',
49548     
49549     dateFormat : 'm/j/Y',
49550     
49551     
49552     renderDate : function(dateVal){
49553         return dateVal.dateFormat(this.dateFormat);
49554     },
49555
49556     renderBool : function(bVal){
49557         return bVal ? 'true' : 'false';
49558     },
49559
49560     isCellEditable : function(colIndex, rowIndex){
49561         return colIndex == 1;
49562     },
49563
49564     getRenderer : function(col){
49565         return col == 1 ?
49566             this.renderCellDelegate : this.renderPropDelegate;
49567     },
49568
49569     renderProp : function(v){
49570         return this.getPropertyName(v);
49571     },
49572
49573     renderCell : function(val){
49574         var rv = val;
49575         if(val instanceof Date){
49576             rv = this.renderDate(val);
49577         }else if(typeof val == 'boolean'){
49578             rv = this.renderBool(val);
49579         }
49580         return Roo.util.Format.htmlEncode(rv);
49581     },
49582
49583     getPropertyName : function(name){
49584         var pn = this.grid.propertyNames;
49585         return pn && pn[name] ? pn[name] : name;
49586     },
49587
49588     getCellEditor : function(colIndex, rowIndex){
49589         var p = this.store.getProperty(rowIndex);
49590         var n = p.data['name'], val = p.data['value'];
49591         
49592         if(typeof(this.grid.customEditors[n]) == 'string'){
49593             return this.editors[this.grid.customEditors[n]];
49594         }
49595         if(typeof(this.grid.customEditors[n]) != 'undefined'){
49596             return this.grid.customEditors[n];
49597         }
49598         if(val instanceof Date){
49599             return this.editors['date'];
49600         }else if(typeof val == 'number'){
49601             return this.editors['number'];
49602         }else if(typeof val == 'boolean'){
49603             return this.editors['boolean'];
49604         }else{
49605             return this.editors['string'];
49606         }
49607     }
49608 });
49609
49610 /**
49611  * @class Roo.grid.PropertyGrid
49612  * @extends Roo.grid.EditorGrid
49613  * This class represents the  interface of a component based property grid control.
49614  * <br><br>Usage:<pre><code>
49615  var grid = new Roo.grid.PropertyGrid("my-container-id", {
49616       
49617  });
49618  // set any options
49619  grid.render();
49620  * </code></pre>
49621   
49622  * @constructor
49623  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
49624  * The container MUST have some type of size defined for the grid to fill. The container will be
49625  * automatically set to position relative if it isn't already.
49626  * @param {Object} config A config object that sets properties on this grid.
49627  */
49628 Roo.grid.PropertyGrid = function(container, config){
49629     config = config || {};
49630     var store = new Roo.grid.PropertyStore(this);
49631     this.store = store;
49632     var cm = new Roo.grid.PropertyColumnModel(this, store);
49633     store.store.sort('name', 'ASC');
49634     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
49635         ds: store.store,
49636         cm: cm,
49637         enableColLock:false,
49638         enableColumnMove:false,
49639         stripeRows:false,
49640         trackMouseOver: false,
49641         clicksToEdit:1
49642     }, config));
49643     this.getGridEl().addClass('x-props-grid');
49644     this.lastEditRow = null;
49645     this.on('columnresize', this.onColumnResize, this);
49646     this.addEvents({
49647          /**
49648              * @event beforepropertychange
49649              * Fires before a property changes (return false to stop?)
49650              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49651              * @param {String} id Record Id
49652              * @param {String} newval New Value
49653          * @param {String} oldval Old Value
49654              */
49655         "beforepropertychange": true,
49656         /**
49657              * @event propertychange
49658              * Fires after a property changes
49659              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
49660              * @param {String} id Record Id
49661              * @param {String} newval New Value
49662          * @param {String} oldval Old Value
49663              */
49664         "propertychange": true
49665     });
49666     this.customEditors = this.customEditors || {};
49667 };
49668 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
49669     
49670      /**
49671      * @cfg {Object} customEditors map of colnames=> custom editors.
49672      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
49673      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
49674      * false disables editing of the field.
49675          */
49676     
49677       /**
49678      * @cfg {Object} propertyNames map of property Names to their displayed value
49679          */
49680     
49681     render : function(){
49682         Roo.grid.PropertyGrid.superclass.render.call(this);
49683         this.autoSize.defer(100, this);
49684     },
49685
49686     autoSize : function(){
49687         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
49688         if(this.view){
49689             this.view.fitColumns();
49690         }
49691     },
49692
49693     onColumnResize : function(){
49694         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
49695         this.autoSize();
49696     },
49697     /**
49698      * Sets the data for the Grid
49699      * accepts a Key => Value object of all the elements avaiable.
49700      * @param {Object} data  to appear in grid.
49701      */
49702     setSource : function(source){
49703         this.store.setSource(source);
49704         //this.autoSize();
49705     },
49706     /**
49707      * Gets all the data from the grid.
49708      * @return {Object} data  data stored in grid
49709      */
49710     getSource : function(){
49711         return this.store.getSource();
49712     }
49713 });/*
49714  * Based on:
49715  * Ext JS Library 1.1.1
49716  * Copyright(c) 2006-2007, Ext JS, LLC.
49717  *
49718  * Originally Released Under LGPL - original licence link has changed is not relivant.
49719  *
49720  * Fork - LGPL
49721  * <script type="text/javascript">
49722  */
49723  
49724 /**
49725  * @class Roo.LoadMask
49726  * A simple utility class for generically masking elements while loading data.  If the element being masked has
49727  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
49728  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
49729  * element's UpdateManager load indicator and will be destroyed after the initial load.
49730  * @constructor
49731  * Create a new LoadMask
49732  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
49733  * @param {Object} config The config object
49734  */
49735 Roo.LoadMask = function(el, config){
49736     this.el = Roo.get(el);
49737     Roo.apply(this, config);
49738     if(this.store){
49739         this.store.on('beforeload', this.onBeforeLoad, this);
49740         this.store.on('load', this.onLoad, this);
49741         this.store.on('loadexception', this.onLoad, this);
49742         this.removeMask = false;
49743     }else{
49744         var um = this.el.getUpdateManager();
49745         um.showLoadIndicator = false; // disable the default indicator
49746         um.on('beforeupdate', this.onBeforeLoad, this);
49747         um.on('update', this.onLoad, this);
49748         um.on('failure', this.onLoad, this);
49749         this.removeMask = true;
49750     }
49751 };
49752
49753 Roo.LoadMask.prototype = {
49754     /**
49755      * @cfg {Boolean} removeMask
49756      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
49757      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
49758      */
49759     /**
49760      * @cfg {String} msg
49761      * The text to display in a centered loading message box (defaults to 'Loading...')
49762      */
49763     msg : 'Loading...',
49764     /**
49765      * @cfg {String} msgCls
49766      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
49767      */
49768     msgCls : 'x-mask-loading',
49769
49770     /**
49771      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
49772      * @type Boolean
49773      */
49774     disabled: false,
49775
49776     /**
49777      * Disables the mask to prevent it from being displayed
49778      */
49779     disable : function(){
49780        this.disabled = true;
49781     },
49782
49783     /**
49784      * Enables the mask so that it can be displayed
49785      */
49786     enable : function(){
49787         this.disabled = false;
49788     },
49789
49790     // private
49791     onLoad : function(){
49792         this.el.unmask(this.removeMask);
49793     },
49794
49795     // private
49796     onBeforeLoad : function(){
49797         if(!this.disabled){
49798             this.el.mask(this.msg, this.msgCls);
49799         }
49800     },
49801
49802     // private
49803     destroy : function(){
49804         if(this.store){
49805             this.store.un('beforeload', this.onBeforeLoad, this);
49806             this.store.un('load', this.onLoad, this);
49807             this.store.un('loadexception', this.onLoad, this);
49808         }else{
49809             var um = this.el.getUpdateManager();
49810             um.un('beforeupdate', this.onBeforeLoad, this);
49811             um.un('update', this.onLoad, this);
49812             um.un('failure', this.onLoad, this);
49813         }
49814     }
49815 };/*
49816  * Based on:
49817  * Ext JS Library 1.1.1
49818  * Copyright(c) 2006-2007, Ext JS, LLC.
49819  *
49820  * Originally Released Under LGPL - original licence link has changed is not relivant.
49821  *
49822  * Fork - LGPL
49823  * <script type="text/javascript">
49824  */
49825 Roo.XTemplate = function(){
49826     Roo.XTemplate.superclass.constructor.apply(this, arguments);
49827     var s = this.html;
49828
49829     s = ['<tpl>', s, '</tpl>'].join('');
49830
49831     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
49832
49833     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
49834     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
49835     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
49836     var m, id = 0;
49837     var tpls = [];
49838
49839     while(m = s.match(re)){
49840        var m2 = m[0].match(nameRe);
49841        var m3 = m[0].match(ifRe);
49842        var m4 = m[0].match(execRe);
49843        var exp = null, fn = null, exec = null;
49844        var name = m2 && m2[1] ? m2[1] : '';
49845        if(m3){
49846            exp = m3 && m3[1] ? m3[1] : null;
49847            if(exp){
49848                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
49849            }
49850        }
49851        if(m4){
49852            exp = m4 && m4[1] ? m4[1] : null;
49853            if(exp){
49854                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
49855            }
49856        }
49857        if(name){
49858            switch(name){
49859                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
49860                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
49861                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
49862            }
49863        }
49864        tpls.push({
49865             id: id,
49866             target: name,
49867             exec: exec,
49868             test: fn,
49869             body: m[1]||''
49870         });
49871        s = s.replace(m[0], '{xtpl'+ id + '}');
49872        ++id;
49873     }
49874     for(var i = tpls.length-1; i >= 0; --i){
49875         this.compileTpl(tpls[i]);
49876     }
49877     this.master = tpls[tpls.length-1];
49878     this.tpls = tpls;
49879 };
49880 Roo.extend(Roo.XTemplate, Roo.Template, {
49881
49882     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
49883
49884     applySubTemplate : function(id, values, parent){
49885         var t = this.tpls[id];
49886         if(t.test && !t.test.call(this, values, parent)){
49887             return '';
49888         }
49889         if(t.exec && t.exec.call(this, values, parent)){
49890             return '';
49891         }
49892         var vs = t.target ? t.target.call(this, values, parent) : values;
49893         parent = t.target ? values : parent;
49894         if(t.target && vs instanceof Array){
49895             var buf = [];
49896             for(var i = 0, len = vs.length; i < len; i++){
49897                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
49898             }
49899             return buf.join('');
49900         }
49901         return t.compiled.call(this, vs, parent);
49902     },
49903
49904     compileTpl : function(tpl){
49905         var fm = Roo.util.Format;
49906         var useF = this.disableFormats !== true;
49907         var sep = Roo.isGecko ? "+" : ",";
49908         var fn = function(m, name, format, args){
49909             if(name.substr(0, 4) == 'xtpl'){
49910                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
49911             }
49912             var v;
49913             if(name.indexOf('.') != -1){
49914                 v = name;
49915             }else{
49916                 v = "values['" + name + "']";
49917             }
49918             if(format && useF){
49919                 args = args ? ',' + args : "";
49920                 if(format.substr(0, 5) != "this."){
49921                     format = "fm." + format + '(';
49922                 }else{
49923                     format = 'this.call("'+ format.substr(5) + '", ';
49924                     args = ", values";
49925                 }
49926             }else{
49927                 args= ''; format = "("+v+" === undefined ? '' : ";
49928             }
49929             return "'"+ sep + format + v + args + ")"+sep+"'";
49930         };
49931         var body;
49932         // branched to use + in gecko and [].join() in others
49933         if(Roo.isGecko){
49934             body = "tpl.compiled = function(values, parent){ return '" +
49935                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
49936                     "';};";
49937         }else{
49938             body = ["tpl.compiled = function(values, parent){ return ['"];
49939             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
49940             body.push("'].join('');};");
49941             body = body.join('');
49942         }
49943         /** eval:var:zzzzzzz */
49944         eval(body);
49945         return this;
49946     },
49947
49948     applyTemplate : function(values){
49949         return this.master.compiled.call(this, values, {});
49950         var s = this.subs;
49951     },
49952
49953     apply : function(){
49954         return this.applyTemplate.apply(this, arguments);
49955     },
49956
49957     compile : function(){return this;}
49958 });
49959
49960 Roo.XTemplate.from = function(el){
49961     el = Roo.getDom(el);
49962     return new Roo.XTemplate(el.value || el.innerHTML);
49963 };/*
49964  * Original code for Roojs - LGPL
49965  * <script type="text/javascript">
49966  */
49967  
49968 /**
49969  * @class Roo.XComponent
49970  * A delayed Element creator...
49971  * 
49972  * Mypart.xyx = new Roo.XComponent({
49973
49974     parent : 'Mypart.xyz', // empty == document.element.!!
49975     order : '001',
49976     name : 'xxxx'
49977     region : 'xxxx'
49978     disabled : function() {} 
49979      
49980     tree : function() { // return an tree of xtype declared components
49981         var MODULE = this;
49982         return 
49983         {
49984             xtype : 'NestedLayoutPanel',
49985             // technicall
49986         }
49987      ]
49988  *})
49989  * @extends Roo.util.Observable
49990  * @constructor
49991  * @param cfg {Object} configuration of component
49992  * 
49993  */
49994 Roo.XComponent = function(cfg) {
49995     Roo.apply(this, cfg);
49996     this.addEvents({ 
49997         /**
49998              * @event built
49999              * Fires when this the componnt is built
50000              * @param {Roo.XComponent} c the component
50001              */
50002         'built' : true,
50003         /**
50004              * @event buildcomplete
50005              * Fires on the top level element when all elements have been built
50006              * @param {Roo.XComponent} c the top level component.
50007          */
50008         'buildcomplete' : true
50009         
50010     });
50011     
50012     Roo.XComponent.register(this);
50013     this.modules = false;
50014     this.el = false; // where the layout goes..
50015     
50016     
50017 }
50018 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50019     /**
50020      * @property el
50021      * The created element (with Roo.factory())
50022      * @type {Roo.Layout}
50023      */
50024     el  : false,
50025     
50026     /**
50027      * @property el
50028      * for BC  - use el in new code
50029      * @type {Roo.Layout}
50030      */
50031     panel : false,
50032     
50033     /**
50034      * @property layout
50035      * for BC  - use el in new code
50036      * @type {Roo.Layout}
50037      */
50038     layout : false,
50039     
50040      /**
50041      * @cfg {Function|boolean} disabled
50042      * If this module is disabled by some rule, return true from the funtion
50043      */
50044     disabled : false,
50045     
50046     /**
50047      * @cfg {String} parent 
50048      * Name of parent element which it get xtype added to..
50049      */
50050     parent: false,
50051     
50052     /**
50053      * @cfg {String} order
50054      * Used to set the order in which elements are created (usefull for multiple tabs)
50055      */
50056     
50057     order : false,
50058     /**
50059      * @cfg {String} name
50060      * String to display while loading.
50061      */
50062     name : false,
50063     /**
50064      * @cfg {Array} items
50065      * A single item array - the first element is the root of the tree..
50066      * It's done this way to stay compatible with the Xtype system...
50067      */
50068     items : false
50069      
50070      
50071     
50072 });
50073
50074 Roo.apply(Roo.XComponent, {
50075     
50076     /**
50077      * @property  buildCompleted
50078      * True when the builder has completed building the interface.
50079      * @type Boolean
50080      */
50081     buildCompleted : false,
50082      
50083     /**
50084      * @property  topModule
50085      * the upper most module - uses document.element as it's constructor.
50086      * @type Object
50087      */
50088      
50089     topModule  : false,
50090       
50091     /**
50092      * @property  modules
50093      * array of modules to be created by registration system.
50094      * @type Roo.XComponent
50095      */
50096     
50097     modules : [],
50098       
50099     
50100     /**
50101      * Register components to be built later.
50102      *
50103      * This solves the following issues
50104      * - Building is not done on page load, but after an authentication process has occured.
50105      * - Interface elements are registered on page load
50106      * - Parent Interface elements may not be loaded before child, so this handles that..
50107      * 
50108      *
50109      * example:
50110      * 
50111      * MyApp.register({
50112           order : '000001',
50113           module : 'Pman.Tab.projectMgr',
50114           region : 'center',
50115           parent : 'Pman.layout',
50116           disabled : false,  // or use a function..
50117         })
50118      
50119      * * @param {Object} details about module
50120      */
50121     register : function(obj) {
50122         this.modules.push(obj);
50123          
50124     },
50125     /**
50126      * convert a string to an object..
50127      * 
50128      */
50129     
50130     toObject : function(str)
50131     {
50132         if (!str || typeof(str) == 'object') {
50133             return str;
50134         }
50135         var ar = str.split('.');
50136         var rt, o;
50137         rt = ar.shift();
50138             /** eval:var:o */
50139         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
50140         if (o === false) {
50141             throw "Module not found : " + str;
50142         }
50143         Roo.each(ar, function(e) {
50144             if (typeof(o[e]) == 'undefined') {
50145                 throw "Module not found : " + str;
50146             }
50147             o = o[e];
50148         });
50149         return o;
50150         
50151     },
50152     
50153     
50154     /**
50155      * move modules into their correct place in the tree..
50156      * 
50157      */
50158     preBuild : function ()
50159     {
50160         
50161         Roo.each(this.modules , function (obj)
50162         {
50163             obj.parent = this.toObject(obj.parent);
50164             
50165             if (!obj.parent) {
50166                 this.topModule = obj;
50167                 return;
50168             }
50169             
50170             if (!obj.parent.modules) {
50171                 obj.parent.modules = new Roo.util.MixedCollection(false, 
50172                     function(o) { return o.order + '' }
50173                 );
50174             }
50175             
50176             obj.parent.modules.add(obj);
50177         }, this);
50178     },
50179     
50180      /**
50181      * make a list of modules to build.
50182      * @return {Array} list of modules. 
50183      */ 
50184     
50185     buildOrder : function()
50186     {
50187         var _this = this;
50188         var cmp = function(a,b) {   
50189             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
50190         };
50191         
50192         if (!this.topModule || !this.topModule.modules) {
50193             throw "No top level modules to build";
50194         }
50195        
50196         // make a flat list in order of modules to build.
50197         var mods = [ this.topModule ];
50198         
50199         
50200         // add modules to their parents..
50201         var addMod = function(m) {
50202            // Roo.debug && Roo.log(m.modKey);
50203             
50204             mods.push(m);
50205             if (m.modules) {
50206                 m.modules.keySort('ASC',  cmp );
50207                 m.modules.each(addMod);
50208             }
50209             // not sure if this is used any more..
50210             if (m.finalize) {
50211                 m.finalize.name = m.name + " (clean up) ";
50212                 mods.push(m.finalize);
50213             }
50214             
50215         }
50216         this.topModule.modules.keySort('ASC',  cmp );
50217         this.topModule.modules.each(addMod);
50218         return mods;
50219     },
50220     
50221      /**
50222      * Build the registered modules.
50223      * @param {Object} parent element.
50224      * @param {Function} optional method to call after module has been added.
50225      * 
50226      */ 
50227    
50228     build : function() 
50229     {
50230         
50231         this.preBuild();
50232         var mods = this.buildOrder();
50233       
50234         //this.allmods = mods;
50235         //Roo.debug && Roo.log(mods);
50236         //return;
50237         if (!mods.length) { // should not happen
50238             throw "NO modules!!!";
50239         }
50240         
50241         
50242         
50243         // flash it up as modal - so we store the mask!?
50244         Roo.MessageBox.show({ title: 'loading' });
50245         Roo.MessageBox.show({
50246            title: "Please wait...",
50247            msg: "Building Interface...",
50248            width:450,
50249            progress:true,
50250            closable:false,
50251            modal: false
50252           
50253         });
50254         var total = mods.length;
50255         
50256         var _this = this;
50257         var progressRun = function() {
50258             if (!mods.length) {
50259                 Roo.debug && Roo.log('hide?');
50260                 Roo.MessageBox.hide();
50261                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
50262                 return;    
50263             }
50264             
50265             var m = mods.shift();
50266             Roo.debug && Roo.log(m);
50267             if (typeof(m) == 'function') { // not sure if this is supported any more..
50268                 m.call(this);
50269                 return progressRun.defer(10, _this);
50270             } 
50271             
50272             Roo.MessageBox.updateProgress(
50273                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
50274                     " of " + total + 
50275                     (m.name ? (' - ' + m.name) : '')
50276                     );
50277             
50278          
50279             
50280             var disabled = (typeof(m.disabled) == 'function') ?
50281                 m.disabled.call(m.module.disabled) : m.disabled;    
50282             
50283             
50284             if (disabled) {
50285                 return progressRun(); // we do not update the display!
50286             }
50287             
50288             if (!m.parent) {
50289                 // it's a top level one..
50290                 var layoutbase = new Ext.BorderLayout(document.body, {
50291                
50292                     center: {
50293                          titlebar: false,
50294                          autoScroll:false,
50295                          closeOnTab: true,
50296                          tabPosition: 'top',
50297                          //resizeTabs: true,
50298                          alwaysShowTabs: true,
50299                          minTabWidth: 140
50300                     }
50301                 });
50302                 var tree = m.tree();
50303                 tree.region = 'center';
50304                 m.el = layoutbase.addxtype(tree);
50305                 m.panel = m.el;
50306                 m.layout = m.panel.layout;    
50307                 return progressRun.defer(10, _this);
50308             }
50309             
50310             var tree = m.tree();
50311             tree.region = tree.region || m.region;
50312             m.el = m.parent.el.addxtype(tree);
50313             m.fireEvent('built', m);
50314             m.panel = m.el;
50315             m.layout = m.panel.layout;    
50316             progressRun.defer(10, _this); 
50317             
50318         }
50319         progressRun.defer(1, _this);
50320      
50321         
50322         
50323     }
50324      
50325    
50326     
50327     
50328 });
50329  //<script type="text/javascript">
50330
50331
50332 /**
50333  * @class Roo.Login
50334  * @extends Roo.LayoutDialog
50335  * A generic Login Dialog..... - only one needed in theory!?!?
50336  *
50337  * Fires XComponent builder on success...
50338  * 
50339  * Sends 
50340  *    username,password, lang = for login actions.
50341  *    check = 1 for periodic checking that sesion is valid.
50342  *    passwordRequest = email request password
50343  *    logout = 1 = to logout
50344  * 
50345  * Affects: (this id="????" elements)
50346  *   loading  (removed) (used to indicate application is loading)
50347  *   loading-mask (hides) (used to hide application when it's building loading)
50348  *   
50349  * 
50350  * Usage: 
50351  *    
50352  * 
50353  * Myapp.login = Roo.Login({
50354      url: xxxx,
50355    
50356      realm : 'Myapp', 
50357      
50358      
50359      method : 'POST',
50360      
50361      
50362      * 
50363  })
50364  * 
50365  * 
50366  * 
50367  **/
50368  
50369 Roo.Login = function(cfg)
50370 {
50371     this.addEvents({
50372         'refreshed' : true,
50373     });
50374     
50375     Roo.apply(this,cfg);
50376     
50377     Roo.onReady(function() {
50378         this.onLoad();
50379     }, this);
50380     // call parent..
50381     
50382    
50383     Roo.Login.superclass.constructor.call(this, this);
50384     //this.addxtype(this.items[0]);
50385     
50386     
50387 }
50388
50389
50390 Roo.extend(Roo.Login, Roo.LayoutDialog, {
50391     
50392     /**
50393      * @cfg {String} method
50394      * Method used to query for login details.
50395      */
50396     
50397     method : 'POST',
50398     /**
50399      * @cfg {String} url
50400      * URL to query login data. - eg. baseURL + '/Login.php'
50401      */
50402     url : '',
50403     
50404     /**
50405      * @property user
50406      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
50407      * @type {Object} 
50408      */
50409     user : false,
50410     /**
50411      * @property checkFails
50412      * Number of times we have attempted to get authentication check, and failed.
50413      * @type {Number} 
50414      */
50415     checkFails : 0,
50416       /**
50417      * @property intervalID
50418      * The window interval that does the constant login checking.
50419      * @type {Number} 
50420      */
50421     intervalID : 0,
50422     
50423     
50424     onLoad : function() // called on page load...
50425     {
50426         // load 
50427          
50428         if (Roo.get('loading')) { // clear any loading indicator..
50429             Roo.get('loading').remove();
50430         }
50431         
50432         //this.switchLang('en'); // set the language to english..
50433        
50434         this.check({
50435             success:  function(response, opts)  {  // check successfull...
50436             
50437                 var res = this.processResponse(response);
50438                 this.checkFails =0;
50439                 if (!res.success) { // error!
50440                     this.checkFails = 5;
50441                     //console.log('call failure');
50442                     return this.failure(response,opts);
50443                 }
50444                 
50445                 if (!res.data.id) { // id=0 == login failure.
50446                     return this.show();
50447                 }
50448                 
50449                               
50450                         //console.log(success);
50451                 this.fillAuth(res.data);   
50452                 this.checkFails =0;
50453                 Roo.XComponent.build();
50454             },
50455             failure : this.show
50456         });
50457         
50458     }, 
50459     
50460     
50461     check: function(cfg) // called every so often to refresh cookie etc..
50462     {
50463         if (cfg.again) { // could be undefined..
50464             this.checkFails++;
50465         } else {
50466             this.checkFails = 0;
50467         }
50468         var _this = this;
50469         if (this.sending) {
50470             if ( this.checkFails > 4) {
50471                 Roo.MessageBox.alert("Error",  
50472                     "Error getting authentication status. - try reloading, or wait a while", function() {
50473                         _this.sending = false;
50474                     }); 
50475                 return;
50476             }
50477             cfg.again = true;
50478             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
50479             return;
50480         }
50481         this.sending = true;
50482         
50483         Roo.Ajax.request({  
50484             url: this.url,
50485             params: {
50486                 getAuthUser: true
50487             },  
50488             method: this.method,
50489             success:  cfg.success || this.success,
50490             failure : cfg.failure || this.failure,
50491             scope : this,
50492             callCfg : cfg
50493               
50494         });  
50495     }, 
50496     
50497     
50498     logout: function()
50499     {
50500         window.onbeforeunload = function() { }; // false does not work for IE..
50501         this.user = false;
50502         var _this = this;
50503         
50504         Roo.Ajax.request({  
50505             url: this.url,
50506             params: {
50507                 logout: 1
50508             },  
50509             method: 'GET',
50510             failure : function() {
50511                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
50512                     document.location = document.location.toString() + '?ts=' + Math.random();
50513                 });
50514                 
50515             },
50516             success : function() {
50517                 _this.user = false;
50518                 this.checkFails =0;
50519                 // fixme..
50520                 document.location = document.location.toString() + '?ts=' + Math.random();
50521             }
50522               
50523               
50524         }); 
50525     },
50526     
50527     processResponse : function (response)
50528     {
50529         var res = '';
50530         try {
50531             res = Roo.decode(response.responseText);
50532             // oops...
50533             if (typeof(res) != 'object') {
50534                 res = { success : false, errorMsg : res, errors : true };
50535             }
50536             if (typeof(res.success) == 'undefined') {
50537                 res.success = false;
50538             }
50539             
50540         } catch(e) {
50541             res = { success : false,  errorMsg : response.responseText, errors : true };
50542         }
50543         return res;
50544     },
50545     
50546     success : function(response, opts)  // check successfull...
50547     {  
50548         this.sending = false;
50549         var res = this.processResponse(response);
50550         if (!res.success) {
50551             return this.failure(response, opts);
50552         }
50553         if (!res.data || !res.data.id) {
50554             return this.failure(response,opts);
50555         }
50556         //console.log(res);
50557         this.fillAuth(res.data);
50558         
50559         this.checkFails =0;
50560         
50561     },
50562     
50563     
50564     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
50565     {
50566         this.authUser = -1;
50567         this.sending = false;
50568         var res = this.processResponse(response);
50569         //console.log(res);
50570         if ( this.checkFails > 2) {
50571         
50572             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
50573                 "Error getting authentication status. - try reloading"); 
50574             return;
50575         }
50576         opts.callCfg.again = true;
50577         this.check.defer(1000, this, [ opts.callCfg ]);
50578         return;  
50579     },
50580     
50581     
50582     
50583     fillAuth: function(au) {
50584         this.startAuthCheck();
50585         this.authUserId = au.id;
50586         this.authUser = au;
50587         this.lastChecked = new Date();
50588         this.fireEvent('refreshed', au);
50589         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
50590         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
50591         au.lang = au.lang || 'en';
50592         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
50593         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
50594         this.switchLang(au.lang );
50595         
50596      
50597         // open system... - -on setyp..
50598         if (this.authUserId  < 0) {
50599             Roo.MessageBox.alert("Warning", 
50600                 "This is an open system - please set up a admin user with a password.");  
50601         }
50602          
50603         //Pman.onload(); // which should do nothing if it's a re-auth result...
50604         
50605              
50606     },
50607     
50608     startAuthCheck : function() // starter for timeout checking..
50609     {
50610         if (this.intervalID) { // timer already in place...
50611             return false;
50612         }
50613         var _this = this;
50614         this.intervalID =  window.setInterval(function() {
50615               _this.check(false);
50616             }, 120000); // every 120 secs = 2mins..
50617         
50618         
50619     },
50620          
50621     
50622     switchLang : function (lang) 
50623     {
50624         _T = typeof(_T) == 'undefined' ? false : _T;
50625           if (!_T || !lang.length) {
50626             return;
50627         }
50628         
50629         if (!_T && lang != 'en') {
50630             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50631             return;
50632         }
50633         
50634         if (typeof(_T.en) == 'undefined') {
50635             _T.en = {};
50636             Roo.apply(_T.en, _T);
50637         }
50638         
50639         if (typeof(_T[lang]) == 'undefined') {
50640             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
50641             return;
50642         }
50643         
50644         
50645         Roo.apply(_T, _T[lang]);
50646         // just need to set the text values for everything...
50647         var _this = this;
50648         /* this will not work ...
50649         if (this.form) { 
50650             
50651                
50652             function formLabel(name, val) {
50653                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
50654             }
50655             
50656             formLabel('password', "Password"+':');
50657             formLabel('username', "Email Address"+':');
50658             formLabel('lang', "Language"+':');
50659             this.dialog.setTitle("Login");
50660             this.dialog.buttons[0].setText("Forgot Password");
50661             this.dialog.buttons[1].setText("Login");
50662         }
50663         */
50664         
50665         
50666     },
50667     
50668     
50669     title: "Login",
50670     modal: true,
50671     width:  350,
50672     //height: 230,
50673     height: 180,
50674     shadow: true,
50675     minWidth:200,
50676     minHeight:180,
50677     //proxyDrag: true,
50678     closable: false,
50679     draggable: false,
50680     collapsible: false,
50681     resizable: false,
50682     center: {  // needed??
50683         autoScroll:false,
50684         titlebar: false,
50685        // tabPosition: 'top',
50686         hideTabs: true,
50687         closeOnTab: true,
50688         alwaysShowTabs: false
50689     } ,
50690     listeners : {
50691         
50692         show  : function(dlg)
50693         {
50694             //console.log(this);
50695             this.form = this.layout.getRegion('center').activePanel.form;
50696             this.form.dialog = dlg;
50697             this.buttons[0].form = this.form;
50698             this.buttons[0].dialog = dlg
50699             this.buttons[1].form = this.form;
50700             this.buttons[1].dialog = dlg;
50701            
50702            //this.resizeToLogo.defer(1000,this);
50703             // this is all related to resizing for logos..
50704             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
50705            //// if (!sz) {
50706              //   this.resizeToLogo.defer(1000,this);
50707              //   return;
50708            // }
50709             //var w = Ext.lib.Dom.getViewWidth() - 100;
50710             //var h = Ext.lib.Dom.getViewHeight() - 100;
50711             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
50712             //this.center();
50713             if (this.disabled) {
50714                 this.hide();
50715                 return;
50716             }
50717             
50718             if (this.user.id < 0) { // used for inital setup situations.
50719                 return;
50720             }
50721             
50722             if (this.intervalID) {
50723                 // remove the timer
50724                 window.clearInterval(this.intervalID);
50725                 this.intervalID = false;
50726             }
50727             
50728             
50729             if (Roo.get('loading')) {
50730                 Roo.get('loading').remove();
50731             }
50732             if (Roo.get('loading-mask')) {
50733                 Roo.get('loading-mask').hide();
50734             }
50735             
50736             //incomming._node = tnode;
50737             this.form.reset();
50738             //this.dialog.modal = !modal;
50739             //this.dialog.show();
50740             this.el.unmask(); 
50741             
50742             
50743             this.form.setValues({
50744                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
50745                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
50746             });
50747             
50748             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
50749             if (this.form.findField('username').getValue().length > 0 ){
50750                 this.form.findField('password').focus();
50751             } else {
50752                this.form.findField('username').focus();
50753             }
50754     
50755         }
50756     },
50757     items : [
50758          {
50759        
50760             xtype : 'ContentPanel',
50761             xns : Roo,
50762             region: 'center',
50763             fitToFrame : true,
50764             
50765             items : [
50766     
50767                 {
50768                
50769                     xtype : 'Form',
50770                     xns : Roo.form,
50771                     labelWidth: 100,
50772                     style : 'margin: 10px;',
50773                     
50774                     listeners : {
50775                         actionfailed : function(f, act) {
50776                             // form can return { errors: .... }
50777                                 
50778                             //act.result.errors // invalid form element list...
50779                             //act.result.errorMsg// invalid form element list...
50780                             
50781                             this.dialog.el.unmask();
50782                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
50783                                         "Login failed - communication error - try again.");
50784                                       
50785                         },
50786                         actioncomplete: function(re, act) {
50787                              
50788                             Roo.state.Manager.set(
50789                                 this.dialog.realm + '.username',  
50790                                     this.findField('username').getValue()
50791                             );
50792                             Roo.state.Manager.set(
50793                                 this.dialog.realm + '.lang',  
50794                                 this.findField('lang').getValue() 
50795                             );
50796                             
50797                             this.dialog.fillAuth(act.result.data);
50798                               
50799                             this.dialog.hide();
50800                             
50801                             if (Roo.get('loading-mask')) {
50802                                 Roo.get('loading-mask').show();
50803                             }
50804                             Roo.XComponent.build();
50805                             
50806                              
50807                             
50808                         }
50809                     },
50810                     items : [
50811                         {
50812                             xtype : 'TextField',
50813                             xns : Roo.form,
50814                             fieldLabel: "Email Address",
50815                             name: 'username',
50816                             width:200,
50817                             autoCreate : {tag: "input", type: "text", size: "20"}
50818                         },
50819                         {
50820                             xtype : 'TextField',
50821                             xns : Roo.form,
50822                             fieldLabel: "Password",
50823                             inputType: 'password',
50824                             name: 'password',
50825                             width:200,
50826                             autoCreate : {tag: "input", type: "text", size: "20"},
50827                             listeners : {
50828                                 specialkey : function(e,ev) {
50829                                     if (ev.keyCode == 13) {
50830                                         this.form.dialog.el.mask("Logging in");
50831                                         this.form.doAction('submit', {
50832                                             url: this.form.dialog.url,
50833                                             method: this.form.dialog.method,
50834                                         });
50835                                     }
50836                                 }
50837                             }  
50838                         },
50839                         {
50840                             xtype : 'ComboBox',
50841                             xns : Roo.form,
50842                             fieldLabel: "Language",
50843                             name : 'langdisp',
50844                             store: {
50845                                 xtype : 'SimpleStore',
50846                                 fields: ['lang', 'ldisp'],
50847                                 data : [
50848                                     [ 'en', 'English' ],
50849                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
50850                                     [ 'zh_CN', '\u7C21\u4E2D' ]
50851                                 ]
50852                             },
50853                             
50854                             valueField : 'lang',
50855                             hiddenName:  'lang',
50856                             width: 200,
50857                             displayField:'ldisp',
50858                             typeAhead: false,
50859                             editable: false,
50860                             mode: 'local',
50861                             triggerAction: 'all',
50862                             emptyText:'Select a Language...',
50863                             selectOnFocus:true,
50864                             listeners : {
50865                                 select :  function(cb, rec, ix) {
50866                                     this.form.switchLang(rec.data.lang);
50867                                 }
50868                             }
50869                         
50870                         }
50871                     ]
50872                 }
50873                   
50874                 
50875             ]
50876         }
50877     ],
50878     buttons : [
50879         {
50880             xtype : 'Button',
50881             xns : 'Roo',
50882             text : "Forgot Password",
50883             listeners : {
50884                 click : function() {
50885                     //console.log(this);
50886                     var n = this.form.findField('username').getValue();
50887                     if (!n.length) {
50888                         Roo.MessageBox.alert("Error", "Fill in your email address");
50889                         return;
50890                     }
50891                     Roo.Ajax.request({
50892                         url: this.dialog.url,
50893                         params: {
50894                             passwordRequest: n
50895                         },
50896                         method: this.dialog.method,
50897                         success:  function(response, opts)  {  // check successfull...
50898                         
50899                             var res = this.dialog.processResponse(response);
50900                             if (!res.success) { // error!
50901                                Roo.MessageBox.alert("Error" ,
50902                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
50903                                return;
50904                             }
50905                             Roo.MessageBox.alert("Notice" ,
50906                                 "Please check you email for the Password Reset message");
50907                         },
50908                         failure : function() {
50909                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
50910                         }
50911                         
50912                     });
50913                 }
50914             }
50915         },
50916         {
50917             xtype : 'Button',
50918             xns : 'Roo',
50919             text : "Login",
50920             listeners : {
50921                 
50922                 click : function () {
50923                         
50924                     this.dialog.el.mask("Logging in");
50925                     this.form.doAction('submit', {
50926                             url: this.dialog.url,
50927                             method: this.dialog.method
50928                     });
50929                 }
50930             }
50931         }
50932     ]
50933   
50934   
50935 })
50936  
50937
50938
50939