Roo/form/BasicForm.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0;
64
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358
359         /**
360          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
361          * @param {String} string
362          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
363          * @return {Object} A literal with members
364          */
365         urlDecode : function(string, overwrite){
366             if(!string || !string.length){
367                 return {};
368             }
369             var obj = {};
370             var pairs = string.split('&');
371             var pair, name, value;
372             for(var i = 0, len = pairs.length; i < len; i++){
373                 pair = pairs[i].split('=');
374                 name = decodeURIComponent(pair[0]);
375                 value = decodeURIComponent(pair[1]);
376                 if(overwrite !== true){
377                     if(typeof obj[name] == "undefined"){
378                         obj[name] = value;
379                     }else if(typeof obj[name] == "string"){
380                         obj[name] = [obj[name]];
381                         obj[name].push(value);
382                     }else{
383                         obj[name].push(value);
384                     }
385                 }else{
386                     obj[name] = value;
387                 }
388             }
389             return obj;
390         },
391
392         /**
393          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
394          * passed array is not really an array, your function is called once with it.
395          * The supplied function is called with (Object item, Number index, Array allItems).
396          * @param {Array/NodeList/Mixed} array
397          * @param {Function} fn
398          * @param {Object} scope
399          */
400         each : function(array, fn, scope){
401             if(typeof array.length == "undefined" || typeof array == "string"){
402                 array = [array];
403             }
404             for(var i = 0, len = array.length; i < len; i++){
405                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
406             }
407         },
408
409         // deprecated
410         combine : function(){
411             var as = arguments, l = as.length, r = [];
412             for(var i = 0; i < l; i++){
413                 var a = as[i];
414                 if(a instanceof Array){
415                     r = r.concat(a);
416                 }else if(a.length !== undefined && !a.substr){
417                     r = r.concat(Array.prototype.slice.call(a, 0));
418                 }else{
419                     r.push(a);
420                 }
421             }
422             return r;
423         },
424
425         /**
426          * Escapes the passed string for use in a regular expression
427          * @param {String} str
428          * @return {String}
429          */
430         escapeRe : function(s) {
431             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
432         },
433
434         // internal
435         callback : function(cb, scope, args, delay){
436             if(typeof cb == "function"){
437                 if(delay){
438                     cb.defer(delay, scope, args || []);
439                 }else{
440                     cb.apply(scope, args || []);
441                 }
442             }
443         },
444
445         /**
446          * Return the dom node for the passed string (id), dom node, or Roo.Element
447          * @param {String/HTMLElement/Roo.Element} el
448          * @return HTMLElement
449          */
450         getDom : function(el){
451             if(!el){
452                 return null;
453             }
454             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
455         },
456
457         /**
458         * Shorthand for {@link Roo.ComponentMgr#get}
459         * @param {String} id
460         * @return Roo.Component
461         */
462         getCmp : function(id){
463             return Roo.ComponentMgr.get(id);
464         },
465          
466         num : function(v, defaultValue){
467             if(typeof v != 'number'){
468                 return defaultValue;
469             }
470             return v;
471         },
472
473         destroy : function(){
474             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
475                 var as = a[i];
476                 if(as){
477                     if(as.dom){
478                         as.removeAllListeners();
479                         as.remove();
480                         continue;
481                     }
482                     if(typeof as.purgeListeners == 'function'){
483                         as.purgeListeners();
484                     }
485                     if(typeof as.destroy == 'function'){
486                         as.destroy();
487                     }
488                 }
489             }
490         },
491
492         // inpired by a similar function in mootools library
493         /**
494          * Returns the type of object that is passed in. If the object passed in is null or undefined it
495          * return false otherwise it returns one of the following values:<ul>
496          * <li><b>string</b>: If the object passed is a string</li>
497          * <li><b>number</b>: If the object passed is a number</li>
498          * <li><b>boolean</b>: If the object passed is a boolean value</li>
499          * <li><b>function</b>: If the object passed is a function reference</li>
500          * <li><b>object</b>: If the object passed is an object</li>
501          * <li><b>array</b>: If the object passed is an array</li>
502          * <li><b>regexp</b>: If the object passed is a regular expression</li>
503          * <li><b>element</b>: If the object passed is a DOM Element</li>
504          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
505          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
506          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
507          * @param {Mixed} object
508          * @return {String}
509          */
510         type : function(o){
511             if(o === undefined || o === null){
512                 return false;
513             }
514             if(o.htmlElement){
515                 return 'element';
516             }
517             var t = typeof o;
518             if(t == 'object' && o.nodeName) {
519                 switch(o.nodeType) {
520                     case 1: return 'element';
521                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
522                 }
523             }
524             if(t == 'object' || t == 'function') {
525                 switch(o.constructor) {
526                     case Array: return 'array';
527                     case RegExp: return 'regexp';
528                 }
529                 if(typeof o.length == 'number' && typeof o.item == 'function') {
530                     return 'nodelist';
531                 }
532             }
533             return t;
534         },
535
536         /**
537          * Returns true if the passed value is null, undefined or an empty string (optional).
538          * @param {Mixed} value The value to test
539          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
540          * @return {Boolean}
541          */
542         isEmpty : function(v, allowBlank){
543             return v === null || v === undefined || (!allowBlank ? v === '' : false);
544         },
545         
546         /** @type Boolean */
547         isOpera : isOpera,
548         /** @type Boolean */
549         isSafari : isSafari,
550         /** @type Boolean */
551         isIE : isIE,
552         /** @type Boolean */
553         isIE7 : isIE7,
554         /** @type Boolean */
555         isGecko : isGecko,
556         /** @type Boolean */
557         isBorderBox : isBorderBox,
558         /** @type Boolean */
559         isWindows : isWindows,
560         /** @type Boolean */
561         isLinux : isLinux,
562         /** @type Boolean */
563         isMac : isMac,
564
565         /**
566          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
567          * you may want to set this to true.
568          * @type Boolean
569          */
570         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
571         
572         
573                 
574         /**
575          * Selects a single element as a Roo Element
576          * This is about as close as you can get to jQuery's $('do crazy stuff')
577          * @param {String} selector The selector/xpath query
578          * @param {Node} root (optional) The start of the query (defaults to document).
579          * @return {Roo.Element}
580          */
581         selectNode : function(selector, root) 
582         {
583             var node = Roo.DomQuery.selectNode(selector,root);
584             return node ? Roo.get(node) : new Roo.Element(false);
585         }
586         
587     });
588
589
590 })();
591
592 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
593                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
594 /*
595  * Based on:
596  * Ext JS Library 1.1.1
597  * Copyright(c) 2006-2007, Ext JS, LLC.
598  *
599  * Originally Released Under LGPL - original licence link has changed is not relivant.
600  *
601  * Fork - LGPL
602  * <script type="text/javascript">
603  */
604
605 (function() {    
606     // wrappedn so fnCleanup is not in global scope...
607     if(Roo.isIE) {
608         function fnCleanUp() {
609             var p = Function.prototype;
610             delete p.createSequence;
611             delete p.defer;
612             delete p.createDelegate;
613             delete p.createCallback;
614             delete p.createInterceptor;
615
616             window.detachEvent("onunload", fnCleanUp);
617         }
618         window.attachEvent("onunload", fnCleanUp);
619     }
620 })();
621
622
623 /**
624  * @class Function
625  * These functions are available on every Function object (any JavaScript function).
626  */
627 Roo.apply(Function.prototype, {
628      /**
629      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
630      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
631      * Will create a function that is bound to those 2 args.
632      * @return {Function} The new function
633     */
634     createCallback : function(/*args...*/){
635         // make args available, in function below
636         var args = arguments;
637         var method = this;
638         return function() {
639             return method.apply(window, args);
640         };
641     },
642
643     /**
644      * Creates a delegate (callback) that sets the scope to obj.
645      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
646      * Will create a function that is automatically scoped to this.
647      * @param {Object} obj (optional) The object for which the scope is set
648      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
649      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
650      *                                             if a number the args are inserted at the specified position
651      * @return {Function} The new function
652      */
653     createDelegate : function(obj, args, appendArgs){
654         var method = this;
655         return function() {
656             var callArgs = args || arguments;
657             if(appendArgs === true){
658                 callArgs = Array.prototype.slice.call(arguments, 0);
659                 callArgs = callArgs.concat(args);
660             }else if(typeof appendArgs == "number"){
661                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
662                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
663                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
664             }
665             return method.apply(obj || window, callArgs);
666         };
667     },
668
669     /**
670      * Calls this function after the number of millseconds specified.
671      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
672      * @param {Object} obj (optional) The object for which the scope is set
673      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
674      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
675      *                                             if a number the args are inserted at the specified position
676      * @return {Number} The timeout id that can be used with clearTimeout
677      */
678     defer : function(millis, obj, args, appendArgs){
679         var fn = this.createDelegate(obj, args, appendArgs);
680         if(millis){
681             return setTimeout(fn, millis);
682         }
683         fn();
684         return 0;
685     },
686     /**
687      * Create a combined function call sequence of the original function + the passed function.
688      * The resulting function returns the results of the original function.
689      * The passed fcn is called with the parameters of the original function
690      * @param {Function} fcn The function to sequence
691      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
692      * @return {Function} The new function
693      */
694     createSequence : function(fcn, scope){
695         if(typeof fcn != "function"){
696             return this;
697         }
698         var method = this;
699         return function() {
700             var retval = method.apply(this || window, arguments);
701             fcn.apply(scope || this || window, arguments);
702             return retval;
703         };
704     },
705
706     /**
707      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
708      * The resulting function returns the results of the original function.
709      * The passed fcn is called with the parameters of the original function.
710      * @addon
711      * @param {Function} fcn The function to call before the original
712      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
713      * @return {Function} The new function
714      */
715     createInterceptor : function(fcn, scope){
716         if(typeof fcn != "function"){
717             return this;
718         }
719         var method = this;
720         return function() {
721             fcn.target = this;
722             fcn.method = method;
723             if(fcn.apply(scope || this || window, arguments) === false){
724                 return;
725             }
726             return method.apply(this || window, arguments);
727         };
728     }
729 });
730 /*
731  * Based on:
732  * Ext JS Library 1.1.1
733  * Copyright(c) 2006-2007, Ext JS, LLC.
734  *
735  * Originally Released Under LGPL - original licence link has changed is not relivant.
736  *
737  * Fork - LGPL
738  * <script type="text/javascript">
739  */
740
741 Roo.applyIf(String, {
742     
743     /** @scope String */
744     
745     /**
746      * Escapes the passed string for ' and \
747      * @param {String} string The string to escape
748      * @return {String} The escaped string
749      * @static
750      */
751     escape : function(string) {
752         return string.replace(/('|\\)/g, "\\$1");
753     },
754
755     /**
756      * Pads the left side of a string with a specified character.  This is especially useful
757      * for normalizing number and date strings.  Example usage:
758      * <pre><code>
759 var s = String.leftPad('123', 5, '0');
760 // s now contains the string: '00123'
761 </code></pre>
762      * @param {String} string The original string
763      * @param {Number} size The total length of the output string
764      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
765      * @return {String} The padded string
766      * @static
767      */
768     leftPad : function (val, size, ch) {
769         var result = new String(val);
770         if(ch === null || ch === undefined || ch === '') {
771             ch = " ";
772         }
773         while (result.length < size) {
774             result = ch + result;
775         }
776         return result;
777     },
778
779     /**
780      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
781      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
782      * <pre><code>
783 var cls = 'my-class', text = 'Some text';
784 var s = String.format('<div class="{0}">{1}</div>', cls, text);
785 // s now contains the string: '<div class="my-class">Some text</div>'
786 </code></pre>
787      * @param {String} string The tokenized string to be formatted
788      * @param {String} value1 The value to replace token {0}
789      * @param {String} value2 Etc...
790      * @return {String} The formatted string
791      * @static
792      */
793     format : function(format){
794         var args = Array.prototype.slice.call(arguments, 1);
795         return format.replace(/\{(\d+)\}/g, function(m, i){
796             return Roo.util.Format.htmlEncode(args[i]);
797         });
798     }
799 });
800
801 /**
802  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
803  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
804  * they are already different, the first value passed in is returned.  Note that this method returns the new value
805  * but does not change the current string.
806  * <pre><code>
807 // alternate sort directions
808 sort = sort.toggle('ASC', 'DESC');
809
810 // instead of conditional logic:
811 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
812 </code></pre>
813  * @param {String} value The value to compare to the current string
814  * @param {String} other The new value to use if the string already equals the first value passed in
815  * @return {String} The new value
816  */
817  
818 String.prototype.toggle = function(value, other){
819     return this == value ? other : value;
820 };/*
821  * Based on:
822  * Ext JS Library 1.1.1
823  * Copyright(c) 2006-2007, Ext JS, LLC.
824  *
825  * Originally Released Under LGPL - original licence link has changed is not relivant.
826  *
827  * Fork - LGPL
828  * <script type="text/javascript">
829  */
830
831  /**
832  * @class Number
833  */
834 Roo.applyIf(Number.prototype, {
835     /**
836      * Checks whether or not the current number is within a desired range.  If the number is already within the
837      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
838      * exceeded.  Note that this method returns the constrained value but does not change the current number.
839      * @param {Number} min The minimum number in the range
840      * @param {Number} max The maximum number in the range
841      * @return {Number} The constrained value if outside the range, otherwise the current value
842      */
843     constrain : function(min, max){
844         return Math.min(Math.max(this, min), max);
845     }
846 });/*
847  * Based on:
848  * Ext JS Library 1.1.1
849  * Copyright(c) 2006-2007, Ext JS, LLC.
850  *
851  * Originally Released Under LGPL - original licence link has changed is not relivant.
852  *
853  * Fork - LGPL
854  * <script type="text/javascript">
855  */
856  /**
857  * @class Array
858  */
859 Roo.applyIf(Array.prototype, {
860     /**
861      * Checks whether or not the specified object exists in the array.
862      * @param {Object} o The object to check for
863      * @return {Number} The index of o in the array (or -1 if it is not found)
864      */
865     indexOf : function(o){
866        for (var i = 0, len = this.length; i < len; i++){
867               if(this[i] == o) return i;
868        }
869            return -1;
870     },
871
872     /**
873      * Removes the specified object from the array.  If the object is not found nothing happens.
874      * @param {Object} o The object to remove
875      */
876     remove : function(o){
877        var index = this.indexOf(o);
878        if(index != -1){
879            this.splice(index, 1);
880        }
881     },
882     /**
883      * Map (JS 1.6 compatibility)
884      * @param {Function} function  to call
885      */
886     map : function(fun )
887     {
888         var len = this.length >>> 0;
889         if (typeof fun != "function")
890             throw new TypeError();
891
892         var res = new Array(len);
893         var thisp = arguments[1];
894         for (var i = 0; i < len; i++)
895         {
896             if (i in this)
897                 res[i] = fun.call(thisp, this[i], i, this);
898         }
899
900         return res;
901     }
902     
903 });
904
905
906  /*
907  * Based on:
908  * Ext JS Library 1.1.1
909  * Copyright(c) 2006-2007, Ext JS, LLC.
910  *
911  * Originally Released Under LGPL - original licence link has changed is not relivant.
912  *
913  * Fork - LGPL
914  * <script type="text/javascript">
915  */
916
917 /**
918  * @class Date
919  *
920  * The date parsing and format syntax is a subset of
921  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
922  * supported will provide results equivalent to their PHP versions.
923  *
924  * Following is the list of all currently supported formats:
925  *<pre>
926 Sample date:
927 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
928
929 Format  Output      Description
930 ------  ----------  --------------------------------------------------------------
931   d      10         Day of the month, 2 digits with leading zeros
932   D      Wed        A textual representation of a day, three letters
933   j      10         Day of the month without leading zeros
934   l      Wednesday  A full textual representation of the day of the week
935   S      th         English ordinal day of month suffix, 2 chars (use with j)
936   w      3          Numeric representation of the day of the week
937   z      9          The julian date, or day of the year (0-365)
938   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
939   F      January    A full textual representation of the month
940   m      01         Numeric representation of a month, with leading zeros
941   M      Jan        Month name abbreviation, three letters
942   n      1          Numeric representation of a month, without leading zeros
943   t      31         Number of days in the given month
944   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
945   Y      2007       A full numeric representation of a year, 4 digits
946   y      07         A two digit representation of a year
947   a      pm         Lowercase Ante meridiem and Post meridiem
948   A      PM         Uppercase Ante meridiem and Post meridiem
949   g      3          12-hour format of an hour without leading zeros
950   G      15         24-hour format of an hour without leading zeros
951   h      03         12-hour format of an hour with leading zeros
952   H      15         24-hour format of an hour with leading zeros
953   i      05         Minutes with leading zeros
954   s      01         Seconds, with leading zeros
955   O      -0600      Difference to Greenwich time (GMT) in hours
956   T      CST        Timezone setting of the machine running the code
957   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
958 </pre>
959  *
960  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
961  * <pre><code>
962 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
963 document.write(dt.format('Y-m-d'));                         //2007-01-10
964 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
965 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
966  </code></pre>
967  *
968  * Here are some standard date/time patterns that you might find helpful.  They
969  * are not part of the source of Date.js, but to use them you can simply copy this
970  * block of code into any script that is included after Date.js and they will also become
971  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
972  * <pre><code>
973 Date.patterns = {
974     ISO8601Long:"Y-m-d H:i:s",
975     ISO8601Short:"Y-m-d",
976     ShortDate: "n/j/Y",
977     LongDate: "l, F d, Y",
978     FullDateTime: "l, F d, Y g:i:s A",
979     MonthDay: "F d",
980     ShortTime: "g:i A",
981     LongTime: "g:i:s A",
982     SortableDateTime: "Y-m-d\\TH:i:s",
983     UniversalSortableDateTime: "Y-m-d H:i:sO",
984     YearMonth: "F, Y"
985 };
986 </code></pre>
987  *
988  * Example usage:
989  * <pre><code>
990 var dt = new Date();
991 document.write(dt.format(Date.patterns.ShortDate));
992  </code></pre>
993  */
994
995 /*
996  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
997  * They generate precompiled functions from date formats instead of parsing and
998  * processing the pattern every time you format a date.  These functions are available
999  * on every Date object (any javascript function).
1000  *
1001  * The original article and download are here:
1002  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1003  *
1004  */
1005  
1006  
1007  // was in core
1008 /**
1009  Returns the number of milliseconds between this date and date
1010  @param {Date} date (optional) Defaults to now
1011  @return {Number} The diff in milliseconds
1012  @member Date getElapsed
1013  */
1014 Date.prototype.getElapsed = function(date) {
1015         return Math.abs((date || new Date()).getTime()-this.getTime());
1016 };
1017 // was in date file..
1018
1019
1020 // private
1021 Date.parseFunctions = {count:0};
1022 // private
1023 Date.parseRegexes = [];
1024 // private
1025 Date.formatFunctions = {count:0};
1026
1027 // private
1028 Date.prototype.dateFormat = function(format) {
1029     if (Date.formatFunctions[format] == null) {
1030         Date.createNewFormat(format);
1031     }
1032     var func = Date.formatFunctions[format];
1033     return this[func]();
1034 };
1035
1036
1037 /**
1038  * Formats a date given the supplied format string
1039  * @param {String} format The format string
1040  * @return {String} The formatted date
1041  * @method
1042  */
1043 Date.prototype.format = Date.prototype.dateFormat;
1044
1045 // private
1046 Date.createNewFormat = function(format) {
1047     var funcName = "format" + Date.formatFunctions.count++;
1048     Date.formatFunctions[format] = funcName;
1049     var code = "Date.prototype." + funcName + " = function(){return ";
1050     var special = false;
1051     var ch = '';
1052     for (var i = 0; i < format.length; ++i) {
1053         ch = format.charAt(i);
1054         if (!special && ch == "\\") {
1055             special = true;
1056         }
1057         else if (special) {
1058             special = false;
1059             code += "'" + String.escape(ch) + "' + ";
1060         }
1061         else {
1062             code += Date.getFormatCode(ch);
1063         }
1064     }
1065     /** eval:var:zzzzzzzzzzzzz */
1066     eval(code.substring(0, code.length - 3) + ";}");
1067 };
1068
1069 // private
1070 Date.getFormatCode = function(character) {
1071     switch (character) {
1072     case "d":
1073         return "String.leftPad(this.getDate(), 2, '0') + ";
1074     case "D":
1075         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1076     case "j":
1077         return "this.getDate() + ";
1078     case "l":
1079         return "Date.dayNames[this.getDay()] + ";
1080     case "S":
1081         return "this.getSuffix() + ";
1082     case "w":
1083         return "this.getDay() + ";
1084     case "z":
1085         return "this.getDayOfYear() + ";
1086     case "W":
1087         return "this.getWeekOfYear() + ";
1088     case "F":
1089         return "Date.monthNames[this.getMonth()] + ";
1090     case "m":
1091         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1092     case "M":
1093         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1094     case "n":
1095         return "(this.getMonth() + 1) + ";
1096     case "t":
1097         return "this.getDaysInMonth() + ";
1098     case "L":
1099         return "(this.isLeapYear() ? 1 : 0) + ";
1100     case "Y":
1101         return "this.getFullYear() + ";
1102     case "y":
1103         return "('' + this.getFullYear()).substring(2, 4) + ";
1104     case "a":
1105         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1106     case "A":
1107         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1108     case "g":
1109         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1110     case "G":
1111         return "this.getHours() + ";
1112     case "h":
1113         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1114     case "H":
1115         return "String.leftPad(this.getHours(), 2, '0') + ";
1116     case "i":
1117         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1118     case "s":
1119         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1120     case "O":
1121         return "this.getGMTOffset() + ";
1122     case "T":
1123         return "this.getTimezone() + ";
1124     case "Z":
1125         return "(this.getTimezoneOffset() * -60) + ";
1126     default:
1127         return "'" + String.escape(character) + "' + ";
1128     }
1129 };
1130
1131 /**
1132  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1133  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1134  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1135  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1136  * string or the parse operation will fail.
1137  * Example Usage:
1138 <pre><code>
1139 //dt = Fri May 25 2007 (current date)
1140 var dt = new Date();
1141
1142 //dt = Thu May 25 2006 (today's month/day in 2006)
1143 dt = Date.parseDate("2006", "Y");
1144
1145 //dt = Sun Jan 15 2006 (all date parts specified)
1146 dt = Date.parseDate("2006-1-15", "Y-m-d");
1147
1148 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1149 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1150 </code></pre>
1151  * @param {String} input The unparsed date as a string
1152  * @param {String} format The format the date is in
1153  * @return {Date} The parsed date
1154  * @static
1155  */
1156 Date.parseDate = function(input, format) {
1157     if (Date.parseFunctions[format] == null) {
1158         Date.createParser(format);
1159     }
1160     var func = Date.parseFunctions[format];
1161     return Date[func](input);
1162 };
1163 /**
1164  * @private
1165  */
1166 Date.createParser = function(format) {
1167     var funcName = "parse" + Date.parseFunctions.count++;
1168     var regexNum = Date.parseRegexes.length;
1169     var currentGroup = 1;
1170     Date.parseFunctions[format] = funcName;
1171
1172     var code = "Date." + funcName + " = function(input){\n"
1173         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1174         + "var d = new Date();\n"
1175         + "y = d.getFullYear();\n"
1176         + "m = d.getMonth();\n"
1177         + "d = d.getDate();\n"
1178         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1179         + "if (results && results.length > 0) {";
1180     var regex = "";
1181
1182     var special = false;
1183     var ch = '';
1184     for (var i = 0; i < format.length; ++i) {
1185         ch = format.charAt(i);
1186         if (!special && ch == "\\") {
1187             special = true;
1188         }
1189         else if (special) {
1190             special = false;
1191             regex += String.escape(ch);
1192         }
1193         else {
1194             var obj = Date.formatCodeToRegex(ch, currentGroup);
1195             currentGroup += obj.g;
1196             regex += obj.s;
1197             if (obj.g && obj.c) {
1198                 code += obj.c;
1199             }
1200         }
1201     }
1202
1203     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1204         + "{v = new Date(y, m, d, h, i, s);}\n"
1205         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1206         + "{v = new Date(y, m, d, h, i);}\n"
1207         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1208         + "{v = new Date(y, m, d, h);}\n"
1209         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1210         + "{v = new Date(y, m, d);}\n"
1211         + "else if (y >= 0 && m >= 0)\n"
1212         + "{v = new Date(y, m);}\n"
1213         + "else if (y >= 0)\n"
1214         + "{v = new Date(y);}\n"
1215         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1216         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1217         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1218         + ";}";
1219
1220     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1221     /** eval:var:zzzzzzzzzzzzz */
1222     eval(code);
1223 };
1224
1225 // private
1226 Date.formatCodeToRegex = function(character, currentGroup) {
1227     switch (character) {
1228     case "D":
1229         return {g:0,
1230         c:null,
1231         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1232     case "j":
1233         return {g:1,
1234             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1235             s:"(\\d{1,2})"}; // day of month without leading zeroes
1236     case "d":
1237         return {g:1,
1238             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1239             s:"(\\d{2})"}; // day of month with leading zeroes
1240     case "l":
1241         return {g:0,
1242             c:null,
1243             s:"(?:" + Date.dayNames.join("|") + ")"};
1244     case "S":
1245         return {g:0,
1246             c:null,
1247             s:"(?:st|nd|rd|th)"};
1248     case "w":
1249         return {g:0,
1250             c:null,
1251             s:"\\d"};
1252     case "z":
1253         return {g:0,
1254             c:null,
1255             s:"(?:\\d{1,3})"};
1256     case "W":
1257         return {g:0,
1258             c:null,
1259             s:"(?:\\d{2})"};
1260     case "F":
1261         return {g:1,
1262             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1263             s:"(" + Date.monthNames.join("|") + ")"};
1264     case "M":
1265         return {g:1,
1266             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1267             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1268     case "n":
1269         return {g:1,
1270             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1271             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1272     case "m":
1273         return {g:1,
1274             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1275             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1276     case "t":
1277         return {g:0,
1278             c:null,
1279             s:"\\d{1,2}"};
1280     case "L":
1281         return {g:0,
1282             c:null,
1283             s:"(?:1|0)"};
1284     case "Y":
1285         return {g:1,
1286             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1287             s:"(\\d{4})"};
1288     case "y":
1289         return {g:1,
1290             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1291                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1292             s:"(\\d{1,2})"};
1293     case "a":
1294         return {g:1,
1295             c:"if (results[" + currentGroup + "] == 'am') {\n"
1296                 + "if (h == 12) { h = 0; }\n"
1297                 + "} else { if (h < 12) { h += 12; }}",
1298             s:"(am|pm)"};
1299     case "A":
1300         return {g:1,
1301             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1302                 + "if (h == 12) { h = 0; }\n"
1303                 + "} else { if (h < 12) { h += 12; }}",
1304             s:"(AM|PM)"};
1305     case "g":
1306     case "G":
1307         return {g:1,
1308             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1309             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1310     case "h":
1311     case "H":
1312         return {g:1,
1313             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1314             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1315     case "i":
1316         return {g:1,
1317             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1318             s:"(\\d{2})"};
1319     case "s":
1320         return {g:1,
1321             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1322             s:"(\\d{2})"};
1323     case "O":
1324         return {g:1,
1325             c:[
1326                 "o = results[", currentGroup, "];\n",
1327                 "var sn = o.substring(0,1);\n", // get + / - sign
1328                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1329                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1330                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1331                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1332             ].join(""),
1333             s:"([+\-]\\d{4})"};
1334     case "T":
1335         return {g:0,
1336             c:null,
1337             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1338     case "Z":
1339         return {g:1,
1340             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1341                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1342             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1343     default:
1344         return {g:0,
1345             c:null,
1346             s:String.escape(character)};
1347     }
1348 };
1349
1350 /**
1351  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1352  * @return {String} The abbreviated timezone name (e.g. 'CST')
1353  */
1354 Date.prototype.getTimezone = function() {
1355     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1356 };
1357
1358 /**
1359  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1360  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1361  */
1362 Date.prototype.getGMTOffset = function() {
1363     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1364         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1365         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1366 };
1367
1368 /**
1369  * Get the numeric day number of the year, adjusted for leap year.
1370  * @return {Number} 0 through 364 (365 in leap years)
1371  */
1372 Date.prototype.getDayOfYear = function() {
1373     var num = 0;
1374     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1375     for (var i = 0; i < this.getMonth(); ++i) {
1376         num += Date.daysInMonth[i];
1377     }
1378     return num + this.getDate() - 1;
1379 };
1380
1381 /**
1382  * Get the string representation of the numeric week number of the year
1383  * (equivalent to the format specifier 'W').
1384  * @return {String} '00' through '52'
1385  */
1386 Date.prototype.getWeekOfYear = function() {
1387     // Skip to Thursday of this week
1388     var now = this.getDayOfYear() + (4 - this.getDay());
1389     // Find the first Thursday of the year
1390     var jan1 = new Date(this.getFullYear(), 0, 1);
1391     var then = (7 - jan1.getDay() + 4);
1392     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1393 };
1394
1395 /**
1396  * Whether or not the current date is in a leap year.
1397  * @return {Boolean} True if the current date is in a leap year, else false
1398  */
1399 Date.prototype.isLeapYear = function() {
1400     var year = this.getFullYear();
1401     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1402 };
1403
1404 /**
1405  * Get the first 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.getFirstDayOfMonth()]); //output: 'Monday'
1412 </code></pre>
1413  * @return {Number} The day number (0-6)
1414  */
1415 Date.prototype.getFirstDayOfMonth = function() {
1416     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1417     return (day < 0) ? (day + 7) : day;
1418 };
1419
1420 /**
1421  * Get the last day of the current month, adjusted for leap year.  The returned value
1422  * is the numeric day index within the week (0-6) which can be used in conjunction with
1423  * the {@link #monthNames} array to retrieve the textual day name.
1424  * Example:
1425  *<pre><code>
1426 var dt = new Date('1/10/2007');
1427 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1428 </code></pre>
1429  * @return {Number} The day number (0-6)
1430  */
1431 Date.prototype.getLastDayOfMonth = function() {
1432     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1433     return (day < 0) ? (day + 7) : day;
1434 };
1435
1436
1437 /**
1438  * Get the first date of this date's month
1439  * @return {Date}
1440  */
1441 Date.prototype.getFirstDateOfMonth = function() {
1442     return new Date(this.getFullYear(), this.getMonth(), 1);
1443 };
1444
1445 /**
1446  * Get the last date of this date's month
1447  * @return {Date}
1448  */
1449 Date.prototype.getLastDateOfMonth = function() {
1450     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1451 };
1452 /**
1453  * Get the number of days in the current month, adjusted for leap year.
1454  * @return {Number} The number of days in the month
1455  */
1456 Date.prototype.getDaysInMonth = function() {
1457     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1458     return Date.daysInMonth[this.getMonth()];
1459 };
1460
1461 /**
1462  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1463  * @return {String} 'st, 'nd', 'rd' or 'th'
1464  */
1465 Date.prototype.getSuffix = function() {
1466     switch (this.getDate()) {
1467         case 1:
1468         case 21:
1469         case 31:
1470             return "st";
1471         case 2:
1472         case 22:
1473             return "nd";
1474         case 3:
1475         case 23:
1476             return "rd";
1477         default:
1478             return "th";
1479     }
1480 };
1481
1482 // private
1483 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1484
1485 /**
1486  * An array of textual month names.
1487  * Override these values for international dates, for example...
1488  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1489  * @type Array
1490  * @static
1491  */
1492 Date.monthNames =
1493    ["January",
1494     "February",
1495     "March",
1496     "April",
1497     "May",
1498     "June",
1499     "July",
1500     "August",
1501     "September",
1502     "October",
1503     "November",
1504     "December"];
1505
1506 /**
1507  * An array of textual day names.
1508  * Override these values for international dates, for example...
1509  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1510  * @type Array
1511  * @static
1512  */
1513 Date.dayNames =
1514    ["Sunday",
1515     "Monday",
1516     "Tuesday",
1517     "Wednesday",
1518     "Thursday",
1519     "Friday",
1520     "Saturday"];
1521
1522 // private
1523 Date.y2kYear = 50;
1524 // private
1525 Date.monthNumbers = {
1526     Jan:0,
1527     Feb:1,
1528     Mar:2,
1529     Apr:3,
1530     May:4,
1531     Jun:5,
1532     Jul:6,
1533     Aug:7,
1534     Sep:8,
1535     Oct:9,
1536     Nov:10,
1537     Dec:11};
1538
1539 /**
1540  * Creates and returns a new Date instance with the exact same date value as the called instance.
1541  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1542  * variable will also be changed.  When the intention is to create a new variable that will not
1543  * modify the original instance, you should create a clone.
1544  *
1545  * Example of correctly cloning a date:
1546  * <pre><code>
1547 //wrong way:
1548 var orig = new Date('10/1/2006');
1549 var copy = orig;
1550 copy.setDate(5);
1551 document.write(orig);  //returns 'Thu Oct 05 2006'!
1552
1553 //correct way:
1554 var orig = new Date('10/1/2006');
1555 var copy = orig.clone();
1556 copy.setDate(5);
1557 document.write(orig);  //returns 'Thu Oct 01 2006'
1558 </code></pre>
1559  * @return {Date} The new Date instance
1560  */
1561 Date.prototype.clone = function() {
1562         return new Date(this.getTime());
1563 };
1564
1565 /**
1566  * Clears any time information from this date
1567  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1568  @return {Date} this or the clone
1569  */
1570 Date.prototype.clearTime = function(clone){
1571     if(clone){
1572         return this.clone().clearTime();
1573     }
1574     this.setHours(0);
1575     this.setMinutes(0);
1576     this.setSeconds(0);
1577     this.setMilliseconds(0);
1578     return this;
1579 };
1580
1581 // private
1582 // safari setMonth is broken
1583 if(Roo.isSafari){
1584     Date.brokenSetMonth = Date.prototype.setMonth;
1585         Date.prototype.setMonth = function(num){
1586                 if(num <= -1){
1587                         var n = Math.ceil(-num);
1588                         var back_year = Math.ceil(n/12);
1589                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1590                         this.setFullYear(this.getFullYear() - back_year);
1591                         return Date.brokenSetMonth.call(this, month);
1592                 } else {
1593                         return Date.brokenSetMonth.apply(this, arguments);
1594                 }
1595         };
1596 }
1597
1598 /** Date interval constant 
1599 * @static 
1600 * @type String */
1601 Date.MILLI = "ms";
1602 /** Date interval constant 
1603 * @static 
1604 * @type String */
1605 Date.SECOND = "s";
1606 /** Date interval constant 
1607 * @static 
1608 * @type String */
1609 Date.MINUTE = "mi";
1610 /** Date interval constant 
1611 * @static 
1612 * @type String */
1613 Date.HOUR = "h";
1614 /** Date interval constant 
1615 * @static 
1616 * @type String */
1617 Date.DAY = "d";
1618 /** Date interval constant 
1619 * @static 
1620 * @type String */
1621 Date.MONTH = "mo";
1622 /** Date interval constant 
1623 * @static 
1624 * @type String */
1625 Date.YEAR = "y";
1626
1627 /**
1628  * Provides a convenient method of performing basic date arithmetic.  This method
1629  * does not modify the Date instance being called - it creates and returns
1630  * a new Date instance containing the resulting date value.
1631  *
1632  * Examples:
1633  * <pre><code>
1634 //Basic usage:
1635 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1636 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1637
1638 //Negative values will subtract correctly:
1639 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1640 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1641
1642 //You can even chain several calls together in one line!
1643 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1644 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1645  </code></pre>
1646  *
1647  * @param {String} interval   A valid date interval enum value
1648  * @param {Number} value      The amount to add to the current date
1649  * @return {Date} The new Date instance
1650  */
1651 Date.prototype.add = function(interval, value){
1652   var d = this.clone();
1653   if (!interval || value === 0) return d;
1654   switch(interval.toLowerCase()){
1655     case Date.MILLI:
1656       d.setMilliseconds(this.getMilliseconds() + value);
1657       break;
1658     case Date.SECOND:
1659       d.setSeconds(this.getSeconds() + value);
1660       break;
1661     case Date.MINUTE:
1662       d.setMinutes(this.getMinutes() + value);
1663       break;
1664     case Date.HOUR:
1665       d.setHours(this.getHours() + value);
1666       break;
1667     case Date.DAY:
1668       d.setDate(this.getDate() + value);
1669       break;
1670     case Date.MONTH:
1671       var day = this.getDate();
1672       if(day > 28){
1673           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1674       }
1675       d.setDate(day);
1676       d.setMonth(this.getMonth() + value);
1677       break;
1678     case Date.YEAR:
1679       d.setFullYear(this.getFullYear() + value);
1680       break;
1681   }
1682   return d;
1683 };/*
1684  * Based on:
1685  * Ext JS Library 1.1.1
1686  * Copyright(c) 2006-2007, Ext JS, LLC.
1687  *
1688  * Originally Released Under LGPL - original licence link has changed is not relivant.
1689  *
1690  * Fork - LGPL
1691  * <script type="text/javascript">
1692  */
1693
1694 Roo.lib.Dom = {
1695     getViewWidth : function(full) {
1696         return full ? this.getDocumentWidth() : this.getViewportWidth();
1697     },
1698
1699     getViewHeight : function(full) {
1700         return full ? this.getDocumentHeight() : this.getViewportHeight();
1701     },
1702
1703     getDocumentHeight: function() {
1704         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1705         return Math.max(scrollHeight, this.getViewportHeight());
1706     },
1707
1708     getDocumentWidth: function() {
1709         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1710         return Math.max(scrollWidth, this.getViewportWidth());
1711     },
1712
1713     getViewportHeight: function() {
1714         var height = self.innerHeight;
1715         var mode = document.compatMode;
1716
1717         if ((mode || Roo.isIE) && !Roo.isOpera) {
1718             height = (mode == "CSS1Compat") ?
1719                      document.documentElement.clientHeight :
1720                      document.body.clientHeight;
1721         }
1722
1723         return height;
1724     },
1725
1726     getViewportWidth: function() {
1727         var width = self.innerWidth;
1728         var mode = document.compatMode;
1729
1730         if (mode || Roo.isIE) {
1731             width = (mode == "CSS1Compat") ?
1732                     document.documentElement.clientWidth :
1733                     document.body.clientWidth;
1734         }
1735         return width;
1736     },
1737
1738     isAncestor : function(p, c) {
1739         p = Roo.getDom(p);
1740         c = Roo.getDom(c);
1741         if (!p || !c) {
1742             return false;
1743         }
1744
1745         if (p.contains && !Roo.isSafari) {
1746             return p.contains(c);
1747         } else if (p.compareDocumentPosition) {
1748             return !!(p.compareDocumentPosition(c) & 16);
1749         } else {
1750             var parent = c.parentNode;
1751             while (parent) {
1752                 if (parent == p) {
1753                     return true;
1754                 }
1755                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1756                     return false;
1757                 }
1758                 parent = parent.parentNode;
1759             }
1760             return false;
1761         }
1762     },
1763
1764     getRegion : function(el) {
1765         return Roo.lib.Region.getRegion(el);
1766     },
1767
1768     getY : function(el) {
1769         return this.getXY(el)[1];
1770     },
1771
1772     getX : function(el) {
1773         return this.getXY(el)[0];
1774     },
1775
1776     getXY : function(el) {
1777         var p, pe, b, scroll, bd = document.body;
1778         el = Roo.getDom(el);
1779         var fly = Roo.lib.AnimBase.fly;
1780         if (el.getBoundingClientRect) {
1781             b = el.getBoundingClientRect();
1782             scroll = fly(document).getScroll();
1783             return [b.left + scroll.left, b.top + scroll.top];
1784         }
1785         var x = 0, y = 0;
1786
1787         p = el;
1788
1789         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1790
1791         while (p) {
1792
1793             x += p.offsetLeft;
1794             y += p.offsetTop;
1795
1796             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1797                 hasAbsolute = true;
1798             }
1799
1800             if (Roo.isGecko) {
1801                 pe = fly(p);
1802
1803                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1804                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1805
1806
1807                 x += bl;
1808                 y += bt;
1809
1810
1811                 if (p != el && pe.getStyle('overflow') != 'visible') {
1812                     x += bl;
1813                     y += bt;
1814                 }
1815             }
1816             p = p.offsetParent;
1817         }
1818
1819         if (Roo.isSafari && hasAbsolute) {
1820             x -= bd.offsetLeft;
1821             y -= bd.offsetTop;
1822         }
1823
1824         if (Roo.isGecko && !hasAbsolute) {
1825             var dbd = fly(bd);
1826             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1827             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1828         }
1829
1830         p = el.parentNode;
1831         while (p && p != bd) {
1832             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1833                 x -= p.scrollLeft;
1834                 y -= p.scrollTop;
1835             }
1836             p = p.parentNode;
1837         }
1838         return [x, y];
1839     },
1840  
1841   
1842
1843
1844     setXY : function(el, xy) {
1845         el = Roo.fly(el, '_setXY');
1846         el.position();
1847         var pts = el.translatePoints(xy);
1848         if (xy[0] !== false) {
1849             el.dom.style.left = pts.left + "px";
1850         }
1851         if (xy[1] !== false) {
1852             el.dom.style.top = pts.top + "px";
1853         }
1854     },
1855
1856     setX : function(el, x) {
1857         this.setXY(el, [x, false]);
1858     },
1859
1860     setY : function(el, y) {
1861         this.setXY(el, [false, y]);
1862     }
1863 };
1864 /*
1865  * Portions of this file are based on pieces of Yahoo User Interface Library
1866  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1867  * YUI licensed under the BSD License:
1868  * http://developer.yahoo.net/yui/license.txt
1869  * <script type="text/javascript">
1870  *
1871  */
1872
1873 Roo.lib.Event = function() {
1874     var loadComplete = false;
1875     var listeners = [];
1876     var unloadListeners = [];
1877     var retryCount = 0;
1878     var onAvailStack = [];
1879     var counter = 0;
1880     var lastError = null;
1881
1882     return {
1883         POLL_RETRYS: 200,
1884         POLL_INTERVAL: 20,
1885         EL: 0,
1886         TYPE: 1,
1887         FN: 2,
1888         WFN: 3,
1889         OBJ: 3,
1890         ADJ_SCOPE: 4,
1891         _interval: null,
1892
1893         startInterval: function() {
1894             if (!this._interval) {
1895                 var self = this;
1896                 var callback = function() {
1897                     self._tryPreloadAttach();
1898                 };
1899                 this._interval = setInterval(callback, this.POLL_INTERVAL);
1900
1901             }
1902         },
1903
1904         onAvailable: function(p_id, p_fn, p_obj, p_override) {
1905             onAvailStack.push({ id:         p_id,
1906                 fn:         p_fn,
1907                 obj:        p_obj,
1908                 override:   p_override,
1909                 checkReady: false    });
1910
1911             retryCount = this.POLL_RETRYS;
1912             this.startInterval();
1913         },
1914
1915
1916         addListener: function(el, eventName, fn) {
1917             el = Roo.getDom(el);
1918             if (!el || !fn) {
1919                 return false;
1920             }
1921
1922             if ("unload" == eventName) {
1923                 unloadListeners[unloadListeners.length] =
1924                 [el, eventName, fn];
1925                 return true;
1926             }
1927
1928             var wrappedFn = function(e) {
1929                 return fn(Roo.lib.Event.getEvent(e));
1930             };
1931
1932             var li = [el, eventName, fn, wrappedFn];
1933
1934             var index = listeners.length;
1935             listeners[index] = li;
1936
1937             this.doAdd(el, eventName, wrappedFn, false);
1938             return true;
1939
1940         },
1941
1942
1943         removeListener: function(el, eventName, fn) {
1944             var i, len;
1945
1946             el = Roo.getDom(el);
1947
1948             if(!fn) {
1949                 return this.purgeElement(el, false, eventName);
1950             }
1951
1952
1953             if ("unload" == eventName) {
1954
1955                 for (i = 0,len = unloadListeners.length; i < len; i++) {
1956                     var li = unloadListeners[i];
1957                     if (li &&
1958                         li[0] == el &&
1959                         li[1] == eventName &&
1960                         li[2] == fn) {
1961                         unloadListeners.splice(i, 1);
1962                         return true;
1963                     }
1964                 }
1965
1966                 return false;
1967             }
1968
1969             var cacheItem = null;
1970
1971
1972             var index = arguments[3];
1973
1974             if ("undefined" == typeof index) {
1975                 index = this._getCacheIndex(el, eventName, fn);
1976             }
1977
1978             if (index >= 0) {
1979                 cacheItem = listeners[index];
1980             }
1981
1982             if (!el || !cacheItem) {
1983                 return false;
1984             }
1985
1986             this.doRemove(el, eventName, cacheItem[this.WFN], false);
1987
1988             delete listeners[index][this.WFN];
1989             delete listeners[index][this.FN];
1990             listeners.splice(index, 1);
1991
1992             return true;
1993
1994         },
1995
1996
1997         getTarget: function(ev, resolveTextNode) {
1998             ev = ev.browserEvent || ev;
1999             var t = ev.target || ev.srcElement;
2000             return this.resolveTextNode(t);
2001         },
2002
2003
2004         resolveTextNode: function(node) {
2005             if (Roo.isSafari && node && 3 == node.nodeType) {
2006                 return node.parentNode;
2007             } else {
2008                 return node;
2009             }
2010         },
2011
2012
2013         getPageX: function(ev) {
2014             ev = ev.browserEvent || ev;
2015             var x = ev.pageX;
2016             if (!x && 0 !== x) {
2017                 x = ev.clientX || 0;
2018
2019                 if (Roo.isIE) {
2020                     x += this.getScroll()[1];
2021                 }
2022             }
2023
2024             return x;
2025         },
2026
2027
2028         getPageY: function(ev) {
2029             ev = ev.browserEvent || ev;
2030             var y = ev.pageY;
2031             if (!y && 0 !== y) {
2032                 y = ev.clientY || 0;
2033
2034                 if (Roo.isIE) {
2035                     y += this.getScroll()[0];
2036                 }
2037             }
2038
2039
2040             return y;
2041         },
2042
2043
2044         getXY: function(ev) {
2045             ev = ev.browserEvent || ev;
2046             return [this.getPageX(ev), this.getPageY(ev)];
2047         },
2048
2049
2050         getRelatedTarget: function(ev) {
2051             ev = ev.browserEvent || ev;
2052             var t = ev.relatedTarget;
2053             if (!t) {
2054                 if (ev.type == "mouseout") {
2055                     t = ev.toElement;
2056                 } else if (ev.type == "mouseover") {
2057                     t = ev.fromElement;
2058                 }
2059             }
2060
2061             return this.resolveTextNode(t);
2062         },
2063
2064
2065         getTime: function(ev) {
2066             ev = ev.browserEvent || ev;
2067             if (!ev.time) {
2068                 var t = new Date().getTime();
2069                 try {
2070                     ev.time = t;
2071                 } catch(ex) {
2072                     this.lastError = ex;
2073                     return t;
2074                 }
2075             }
2076
2077             return ev.time;
2078         },
2079
2080
2081         stopEvent: function(ev) {
2082             this.stopPropagation(ev);
2083             this.preventDefault(ev);
2084         },
2085
2086
2087         stopPropagation: function(ev) {
2088             ev = ev.browserEvent || ev;
2089             if (ev.stopPropagation) {
2090                 ev.stopPropagation();
2091             } else {
2092                 ev.cancelBubble = true;
2093             }
2094         },
2095
2096
2097         preventDefault: function(ev) {
2098             ev = ev.browserEvent || ev;
2099             if(ev.preventDefault) {
2100                 ev.preventDefault();
2101             } else {
2102                 ev.returnValue = false;
2103             }
2104         },
2105
2106
2107         getEvent: function(e) {
2108             var ev = e || window.event;
2109             if (!ev) {
2110                 var c = this.getEvent.caller;
2111                 while (c) {
2112                     ev = c.arguments[0];
2113                     if (ev && Event == ev.constructor) {
2114                         break;
2115                     }
2116                     c = c.caller;
2117                 }
2118             }
2119             return ev;
2120         },
2121
2122
2123         getCharCode: function(ev) {
2124             ev = ev.browserEvent || ev;
2125             return ev.charCode || ev.keyCode || 0;
2126         },
2127
2128
2129         _getCacheIndex: function(el, eventName, fn) {
2130             for (var i = 0,len = listeners.length; i < len; ++i) {
2131                 var li = listeners[i];
2132                 if (li &&
2133                     li[this.FN] == fn &&
2134                     li[this.EL] == el &&
2135                     li[this.TYPE] == eventName) {
2136                     return i;
2137                 }
2138             }
2139
2140             return -1;
2141         },
2142
2143
2144         elCache: {},
2145
2146
2147         getEl: function(id) {
2148             return document.getElementById(id);
2149         },
2150
2151
2152         clearCache: function() {
2153         },
2154
2155
2156         _load: function(e) {
2157             loadComplete = true;
2158             var EU = Roo.lib.Event;
2159
2160
2161             if (Roo.isIE) {
2162                 EU.doRemove(window, "load", EU._load);
2163             }
2164         },
2165
2166
2167         _tryPreloadAttach: function() {
2168
2169             if (this.locked) {
2170                 return false;
2171             }
2172
2173             this.locked = true;
2174
2175
2176             var tryAgain = !loadComplete;
2177             if (!tryAgain) {
2178                 tryAgain = (retryCount > 0);
2179             }
2180
2181
2182             var notAvail = [];
2183             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2184                 var item = onAvailStack[i];
2185                 if (item) {
2186                     var el = this.getEl(item.id);
2187
2188                     if (el) {
2189                         if (!item.checkReady ||
2190                             loadComplete ||
2191                             el.nextSibling ||
2192                             (document && document.body)) {
2193
2194                             var scope = el;
2195                             if (item.override) {
2196                                 if (item.override === true) {
2197                                     scope = item.obj;
2198                                 } else {
2199                                     scope = item.override;
2200                                 }
2201                             }
2202                             item.fn.call(scope, item.obj);
2203                             onAvailStack[i] = null;
2204                         }
2205                     } else {
2206                         notAvail.push(item);
2207                     }
2208                 }
2209             }
2210
2211             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2212
2213             if (tryAgain) {
2214
2215                 this.startInterval();
2216             } else {
2217                 clearInterval(this._interval);
2218                 this._interval = null;
2219             }
2220
2221             this.locked = false;
2222
2223             return true;
2224
2225         },
2226
2227
2228         purgeElement: function(el, recurse, eventName) {
2229             var elListeners = this.getListeners(el, eventName);
2230             if (elListeners) {
2231                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2232                     var l = elListeners[i];
2233                     this.removeListener(el, l.type, l.fn);
2234                 }
2235             }
2236
2237             if (recurse && el && el.childNodes) {
2238                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2239                     this.purgeElement(el.childNodes[i], recurse, eventName);
2240                 }
2241             }
2242         },
2243
2244
2245         getListeners: function(el, eventName) {
2246             var results = [], searchLists;
2247             if (!eventName) {
2248                 searchLists = [listeners, unloadListeners];
2249             } else if (eventName == "unload") {
2250                 searchLists = [unloadListeners];
2251             } else {
2252                 searchLists = [listeners];
2253             }
2254
2255             for (var j = 0; j < searchLists.length; ++j) {
2256                 var searchList = searchLists[j];
2257                 if (searchList && searchList.length > 0) {
2258                     for (var i = 0,len = searchList.length; i < len; ++i) {
2259                         var l = searchList[i];
2260                         if (l && l[this.EL] === el &&
2261                             (!eventName || eventName === l[this.TYPE])) {
2262                             results.push({
2263                                 type:   l[this.TYPE],
2264                                 fn:     l[this.FN],
2265                                 obj:    l[this.OBJ],
2266                                 adjust: l[this.ADJ_SCOPE],
2267                                 index:  i
2268                             });
2269                         }
2270                     }
2271                 }
2272             }
2273
2274             return (results.length) ? results : null;
2275         },
2276
2277
2278         _unload: function(e) {
2279
2280             var EU = Roo.lib.Event, i, j, l, len, index;
2281
2282             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2283                 l = unloadListeners[i];
2284                 if (l) {
2285                     var scope = window;
2286                     if (l[EU.ADJ_SCOPE]) {
2287                         if (l[EU.ADJ_SCOPE] === true) {
2288                             scope = l[EU.OBJ];
2289                         } else {
2290                             scope = l[EU.ADJ_SCOPE];
2291                         }
2292                     }
2293                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2294                     unloadListeners[i] = null;
2295                     l = null;
2296                     scope = null;
2297                 }
2298             }
2299
2300             unloadListeners = null;
2301
2302             if (listeners && listeners.length > 0) {
2303                 j = listeners.length;
2304                 while (j) {
2305                     index = j - 1;
2306                     l = listeners[index];
2307                     if (l) {
2308                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2309                                 l[EU.FN], index);
2310                     }
2311                     j = j - 1;
2312                 }
2313                 l = null;
2314
2315                 EU.clearCache();
2316             }
2317
2318             EU.doRemove(window, "unload", EU._unload);
2319
2320         },
2321
2322
2323         getScroll: function() {
2324             var dd = document.documentElement, db = document.body;
2325             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2326                 return [dd.scrollTop, dd.scrollLeft];
2327             } else if (db) {
2328                 return [db.scrollTop, db.scrollLeft];
2329             } else {
2330                 return [0, 0];
2331             }
2332         },
2333
2334
2335         doAdd: function () {
2336             if (window.addEventListener) {
2337                 return function(el, eventName, fn, capture) {
2338                     el.addEventListener(eventName, fn, (capture));
2339                 };
2340             } else if (window.attachEvent) {
2341                 return function(el, eventName, fn, capture) {
2342                     el.attachEvent("on" + eventName, fn);
2343                 };
2344             } else {
2345                 return function() {
2346                 };
2347             }
2348         }(),
2349
2350
2351         doRemove: function() {
2352             if (window.removeEventListener) {
2353                 return function (el, eventName, fn, capture) {
2354                     el.removeEventListener(eventName, fn, (capture));
2355                 };
2356             } else if (window.detachEvent) {
2357                 return function (el, eventName, fn) {
2358                     el.detachEvent("on" + eventName, fn);
2359                 };
2360             } else {
2361                 return function() {
2362                 };
2363             }
2364         }()
2365     };
2366     
2367 }();
2368 (function() {     
2369    
2370     var E = Roo.lib.Event;
2371     E.on = E.addListener;
2372     E.un = E.removeListener;
2373
2374     if (document && document.body) {
2375         E._load();
2376     } else {
2377         E.doAdd(window, "load", E._load);
2378     }
2379     E.doAdd(window, "unload", E._unload);
2380     E._tryPreloadAttach();
2381 })();
2382
2383 /*
2384  * Portions of this file are based on pieces of Yahoo User Interface Library
2385  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2386  * YUI licensed under the BSD License:
2387  * http://developer.yahoo.net/yui/license.txt
2388  * <script type="text/javascript">
2389  *
2390  */
2391
2392 (function() {
2393     
2394     Roo.lib.Ajax = {
2395         request : function(method, uri, cb, data, options) {
2396             if(options){
2397                 var hs = options.headers;
2398                 if(hs){
2399                     for(var h in hs){
2400                         if(hs.hasOwnProperty(h)){
2401                             this.initHeader(h, hs[h], false);
2402                         }
2403                     }
2404                 }
2405                 if(options.xmlData){
2406                     this.initHeader('Content-Type', 'text/xml', false);
2407                     method = 'POST';
2408                     data = options.xmlData;
2409                 }
2410             }
2411
2412             return this.asyncRequest(method, uri, cb, data);
2413         },
2414
2415         serializeForm : function(form) {
2416             if(typeof form == 'string') {
2417                 form = (document.getElementById(form) || document.forms[form]);
2418             }
2419
2420             var el, name, val, disabled, data = '', hasSubmit = false;
2421             for (var i = 0; i < form.elements.length; i++) {
2422                 el = form.elements[i];
2423                 disabled = form.elements[i].disabled;
2424                 name = form.elements[i].name;
2425                 val = form.elements[i].value;
2426
2427                 if (!disabled && name){
2428                     switch (el.type)
2429                             {
2430                         case 'select-one':
2431                         case 'select-multiple':
2432                             for (var j = 0; j < el.options.length; j++) {
2433                                 if (el.options[j].selected) {
2434                                     if (Roo.isIE) {
2435                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2436                                     }
2437                                     else {
2438                                         data += encodeURIComponent(name) + '=' + encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2439                                     }
2440                                 }
2441                             }
2442                             break;
2443                         case 'radio':
2444                         case 'checkbox':
2445                             if (el.checked) {
2446                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2447                             }
2448                             break;
2449                         case 'file':
2450
2451                         case undefined:
2452
2453                         case 'reset':
2454
2455                         case 'button':
2456
2457                             break;
2458                         case 'submit':
2459                             if(hasSubmit == false) {
2460                                 data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2461                                 hasSubmit = true;
2462                             }
2463                             break;
2464                         default:
2465                             data += encodeURIComponent(name) + '=' + encodeURIComponent(val) + '&';
2466                             break;
2467                     }
2468                 }
2469             }
2470             data = data.substr(0, data.length - 1);
2471             return data;
2472         },
2473
2474         headers:{},
2475
2476         hasHeaders:false,
2477
2478         useDefaultHeader:true,
2479
2480         defaultPostHeader:'application/x-www-form-urlencoded',
2481
2482         useDefaultXhrHeader:true,
2483
2484         defaultXhrHeader:'XMLHttpRequest',
2485
2486         hasDefaultHeaders:true,
2487
2488         defaultHeaders:{},
2489
2490         poll:{},
2491
2492         timeout:{},
2493
2494         pollInterval:50,
2495
2496         transactionId:0,
2497
2498         setProgId:function(id)
2499         {
2500             this.activeX.unshift(id);
2501         },
2502
2503         setDefaultPostHeader:function(b)
2504         {
2505             this.useDefaultHeader = b;
2506         },
2507
2508         setDefaultXhrHeader:function(b)
2509         {
2510             this.useDefaultXhrHeader = b;
2511         },
2512
2513         setPollingInterval:function(i)
2514         {
2515             if (typeof i == 'number' && isFinite(i)) {
2516                 this.pollInterval = i;
2517             }
2518         },
2519
2520         createXhrObject:function(transactionId)
2521         {
2522             var obj,http;
2523             try
2524             {
2525
2526                 http = new XMLHttpRequest();
2527
2528                 obj = { conn:http, tId:transactionId };
2529             }
2530             catch(e)
2531             {
2532                 for (var i = 0; i < this.activeX.length; ++i) {
2533                     try
2534                     {
2535
2536                         http = new ActiveXObject(this.activeX[i]);
2537
2538                         obj = { conn:http, tId:transactionId };
2539                         break;
2540                     }
2541                     catch(e) {
2542                     }
2543                 }
2544             }
2545             finally
2546             {
2547                 return obj;
2548             }
2549         },
2550
2551         getConnectionObject:function()
2552         {
2553             var o;
2554             var tId = this.transactionId;
2555
2556             try
2557             {
2558                 o = this.createXhrObject(tId);
2559                 if (o) {
2560                     this.transactionId++;
2561                 }
2562             }
2563             catch(e) {
2564             }
2565             finally
2566             {
2567                 return o;
2568             }
2569         },
2570
2571         asyncRequest:function(method, uri, callback, postData)
2572         {
2573             var o = this.getConnectionObject();
2574
2575             if (!o) {
2576                 return null;
2577             }
2578             else {
2579                 o.conn.open(method, uri, true);
2580
2581                 if (this.useDefaultXhrHeader) {
2582                     if (!this.defaultHeaders['X-Requested-With']) {
2583                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2584                     }
2585                 }
2586
2587                 if(postData && this.useDefaultHeader){
2588                     this.initHeader('Content-Type', this.defaultPostHeader);
2589                 }
2590
2591                  if (this.hasDefaultHeaders || this.hasHeaders) {
2592                     this.setHeader(o);
2593                 }
2594
2595                 this.handleReadyState(o, callback);
2596                 o.conn.send(postData || null);
2597
2598                 return o;
2599             }
2600         },
2601
2602         handleReadyState:function(o, callback)
2603         {
2604             var oConn = this;
2605
2606             if (callback && callback.timeout) {
2607                 this.timeout[o.tId] = window.setTimeout(function() {
2608                     oConn.abort(o, callback, true);
2609                 }, callback.timeout);
2610             }
2611
2612             this.poll[o.tId] = window.setInterval(
2613                     function() {
2614                         if (o.conn && o.conn.readyState == 4) {
2615                             window.clearInterval(oConn.poll[o.tId]);
2616                             delete oConn.poll[o.tId];
2617
2618                             if(callback && callback.timeout) {
2619                                 window.clearTimeout(oConn.timeout[o.tId]);
2620                                 delete oConn.timeout[o.tId];
2621                             }
2622
2623                             oConn.handleTransactionResponse(o, callback);
2624                         }
2625                     }
2626                     , this.pollInterval);
2627         },
2628
2629         handleTransactionResponse:function(o, callback, isAbort)
2630         {
2631
2632             if (!callback) {
2633                 this.releaseObject(o);
2634                 return;
2635             }
2636
2637             var httpStatus, responseObject;
2638
2639             try
2640             {
2641                 if (o.conn.status !== undefined && o.conn.status != 0) {
2642                     httpStatus = o.conn.status;
2643                 }
2644                 else {
2645                     httpStatus = 13030;
2646                 }
2647             }
2648             catch(e) {
2649
2650
2651                 httpStatus = 13030;
2652             }
2653
2654             if (httpStatus >= 200 && httpStatus < 300) {
2655                 responseObject = this.createResponseObject(o, callback.argument);
2656                 if (callback.success) {
2657                     if (!callback.scope) {
2658                         callback.success(responseObject);
2659                     }
2660                     else {
2661
2662
2663                         callback.success.apply(callback.scope, [responseObject]);
2664                     }
2665                 }
2666             }
2667             else {
2668                 switch (httpStatus) {
2669
2670                     case 12002:
2671                     case 12029:
2672                     case 12030:
2673                     case 12031:
2674                     case 12152:
2675                     case 13030:
2676                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2677                         if (callback.failure) {
2678                             if (!callback.scope) {
2679                                 callback.failure(responseObject);
2680                             }
2681                             else {
2682                                 callback.failure.apply(callback.scope, [responseObject]);
2683                             }
2684                         }
2685                         break;
2686                     default:
2687                         responseObject = this.createResponseObject(o, callback.argument);
2688                         if (callback.failure) {
2689                             if (!callback.scope) {
2690                                 callback.failure(responseObject);
2691                             }
2692                             else {
2693                                 callback.failure.apply(callback.scope, [responseObject]);
2694                             }
2695                         }
2696                 }
2697             }
2698
2699             this.releaseObject(o);
2700             responseObject = null;
2701         },
2702
2703         createResponseObject:function(o, callbackArg)
2704         {
2705             var obj = {};
2706             var headerObj = {};
2707
2708             try
2709             {
2710                 var headerStr = o.conn.getAllResponseHeaders();
2711                 var header = headerStr.split('\n');
2712                 for (var i = 0; i < header.length; i++) {
2713                     var delimitPos = header[i].indexOf(':');
2714                     if (delimitPos != -1) {
2715                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2716                     }
2717                 }
2718             }
2719             catch(e) {
2720             }
2721
2722             obj.tId = o.tId;
2723             obj.status = o.conn.status;
2724             obj.statusText = o.conn.statusText;
2725             obj.getResponseHeader = headerObj;
2726             obj.getAllResponseHeaders = headerStr;
2727             obj.responseText = o.conn.responseText;
2728             obj.responseXML = o.conn.responseXML;
2729
2730             if (typeof callbackArg !== undefined) {
2731                 obj.argument = callbackArg;
2732             }
2733
2734             return obj;
2735         },
2736
2737         createExceptionObject:function(tId, callbackArg, isAbort)
2738         {
2739             var COMM_CODE = 0;
2740             var COMM_ERROR = 'communication failure';
2741             var ABORT_CODE = -1;
2742             var ABORT_ERROR = 'transaction aborted';
2743
2744             var obj = {};
2745
2746             obj.tId = tId;
2747             if (isAbort) {
2748                 obj.status = ABORT_CODE;
2749                 obj.statusText = ABORT_ERROR;
2750             }
2751             else {
2752                 obj.status = COMM_CODE;
2753                 obj.statusText = COMM_ERROR;
2754             }
2755
2756             if (callbackArg) {
2757                 obj.argument = callbackArg;
2758             }
2759
2760             return obj;
2761         },
2762
2763         initHeader:function(label, value, isDefault)
2764         {
2765             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2766
2767             if (headerObj[label] === undefined) {
2768                 headerObj[label] = value;
2769             }
2770             else {
2771
2772
2773                 headerObj[label] = value + "," + headerObj[label];
2774             }
2775
2776             if (isDefault) {
2777                 this.hasDefaultHeaders = true;
2778             }
2779             else {
2780                 this.hasHeaders = true;
2781             }
2782         },
2783
2784
2785         setHeader:function(o)
2786         {
2787             if (this.hasDefaultHeaders) {
2788                 for (var prop in this.defaultHeaders) {
2789                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2790                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2791                     }
2792                 }
2793             }
2794
2795             if (this.hasHeaders) {
2796                 for (var prop in this.headers) {
2797                     if (this.headers.hasOwnProperty(prop)) {
2798                         o.conn.setRequestHeader(prop, this.headers[prop]);
2799                     }
2800                 }
2801                 this.headers = {};
2802                 this.hasHeaders = false;
2803             }
2804         },
2805
2806         resetDefaultHeaders:function() {
2807             delete this.defaultHeaders;
2808             this.defaultHeaders = {};
2809             this.hasDefaultHeaders = false;
2810         },
2811
2812         abort:function(o, callback, isTimeout)
2813         {
2814             if(this.isCallInProgress(o)) {
2815                 o.conn.abort();
2816                 window.clearInterval(this.poll[o.tId]);
2817                 delete this.poll[o.tId];
2818                 if (isTimeout) {
2819                     delete this.timeout[o.tId];
2820                 }
2821
2822                 this.handleTransactionResponse(o, callback, true);
2823
2824                 return true;
2825             }
2826             else {
2827                 return false;
2828             }
2829         },
2830
2831
2832         isCallInProgress:function(o)
2833         {
2834             if (o && o.conn) {
2835                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2836             }
2837             else {
2838
2839                 return false;
2840             }
2841         },
2842
2843
2844         releaseObject:function(o)
2845         {
2846
2847             o.conn = null;
2848
2849             o = null;
2850         },
2851
2852         activeX:[
2853         'MSXML2.XMLHTTP.3.0',
2854         'MSXML2.XMLHTTP',
2855         'Microsoft.XMLHTTP'
2856         ]
2857
2858
2859     };
2860 })();/*
2861  * Portions of this file are based on pieces of Yahoo User Interface Library
2862  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2863  * YUI licensed under the BSD License:
2864  * http://developer.yahoo.net/yui/license.txt
2865  * <script type="text/javascript">
2866  *
2867  */
2868
2869 Roo.lib.Region = function(t, r, b, l) {
2870     this.top = t;
2871     this[1] = t;
2872     this.right = r;
2873     this.bottom = b;
2874     this.left = l;
2875     this[0] = l;
2876 };
2877
2878
2879 Roo.lib.Region.prototype = {
2880     contains : function(region) {
2881         return ( region.left >= this.left &&
2882                  region.right <= this.right &&
2883                  region.top >= this.top &&
2884                  region.bottom <= this.bottom    );
2885
2886     },
2887
2888     getArea : function() {
2889         return ( (this.bottom - this.top) * (this.right - this.left) );
2890     },
2891
2892     intersect : function(region) {
2893         var t = Math.max(this.top, region.top);
2894         var r = Math.min(this.right, region.right);
2895         var b = Math.min(this.bottom, region.bottom);
2896         var l = Math.max(this.left, region.left);
2897
2898         if (b >= t && r >= l) {
2899             return new Roo.lib.Region(t, r, b, l);
2900         } else {
2901             return null;
2902         }
2903     },
2904     union : function(region) {
2905         var t = Math.min(this.top, region.top);
2906         var r = Math.max(this.right, region.right);
2907         var b = Math.max(this.bottom, region.bottom);
2908         var l = Math.min(this.left, region.left);
2909
2910         return new Roo.lib.Region(t, r, b, l);
2911     },
2912
2913     adjust : function(t, l, b, r) {
2914         this.top += t;
2915         this.left += l;
2916         this.right += r;
2917         this.bottom += b;
2918         return this;
2919     }
2920 };
2921
2922 Roo.lib.Region.getRegion = function(el) {
2923     var p = Roo.lib.Dom.getXY(el);
2924
2925     var t = p[1];
2926     var r = p[0] + el.offsetWidth;
2927     var b = p[1] + el.offsetHeight;
2928     var l = p[0];
2929
2930     return new Roo.lib.Region(t, r, b, l);
2931 };
2932 /*
2933  * Portions of this file are based on pieces of Yahoo User Interface Library
2934  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2935  * YUI licensed under the BSD License:
2936  * http://developer.yahoo.net/yui/license.txt
2937  * <script type="text/javascript">
2938  *
2939  */
2940 //@@dep Roo.lib.Region
2941
2942
2943 Roo.lib.Point = function(x, y) {
2944     if (x instanceof Array) {
2945         y = x[1];
2946         x = x[0];
2947     }
2948     this.x = this.right = this.left = this[0] = x;
2949     this.y = this.top = this.bottom = this[1] = y;
2950 };
2951
2952 Roo.lib.Point.prototype = new Roo.lib.Region();
2953 /*
2954  * Portions of this file are based on pieces of Yahoo User Interface Library
2955  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2956  * YUI licensed under the BSD License:
2957  * http://developer.yahoo.net/yui/license.txt
2958  * <script type="text/javascript">
2959  *
2960  */
2961  
2962 (function() {   
2963
2964     Roo.lib.Anim = {
2965         scroll : function(el, args, duration, easing, cb, scope) {
2966             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
2967         },
2968
2969         motion : function(el, args, duration, easing, cb, scope) {
2970             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
2971         },
2972
2973         color : function(el, args, duration, easing, cb, scope) {
2974             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
2975         },
2976
2977         run : function(el, args, duration, easing, cb, scope, type) {
2978             type = type || Roo.lib.AnimBase;
2979             if (typeof easing == "string") {
2980                 easing = Roo.lib.Easing[easing];
2981             }
2982             var anim = new type(el, args, duration, easing);
2983             anim.animateX(function() {
2984                 Roo.callback(cb, scope);
2985             });
2986             return anim;
2987         }
2988     };
2989 })();/*
2990  * Portions of this file are based on pieces of Yahoo User Interface Library
2991  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2992  * YUI licensed under the BSD License:
2993  * http://developer.yahoo.net/yui/license.txt
2994  * <script type="text/javascript">
2995  *
2996  */
2997
2998 (function() {    
2999     var libFlyweight;
3000     
3001     function fly(el) {
3002         if (!libFlyweight) {
3003             libFlyweight = new Roo.Element.Flyweight();
3004         }
3005         libFlyweight.dom = el;
3006         return libFlyweight;
3007     }
3008
3009     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3010     
3011    
3012     
3013     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3014         if (el) {
3015             this.init(el, attributes, duration, method);
3016         }
3017     };
3018
3019     Roo.lib.AnimBase.fly = fly;
3020     
3021     
3022     
3023     Roo.lib.AnimBase.prototype = {
3024
3025         toString: function() {
3026             var el = this.getEl();
3027             var id = el.id || el.tagName;
3028             return ("Anim " + id);
3029         },
3030
3031         patterns: {
3032             noNegatives:        /width|height|opacity|padding/i,
3033             offsetAttribute:  /^((width|height)|(top|left))$/,
3034             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3035             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3036         },
3037
3038
3039         doMethod: function(attr, start, end) {
3040             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3041         },
3042
3043
3044         setAttribute: function(attr, val, unit) {
3045             if (this.patterns.noNegatives.test(attr)) {
3046                 val = (val > 0) ? val : 0;
3047             }
3048
3049             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3050         },
3051
3052
3053         getAttribute: function(attr) {
3054             var el = this.getEl();
3055             var val = fly(el).getStyle(attr);
3056
3057             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3058                 return parseFloat(val);
3059             }
3060
3061             var a = this.patterns.offsetAttribute.exec(attr) || [];
3062             var pos = !!( a[3] );
3063             var box = !!( a[2] );
3064
3065
3066             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3067                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3068             } else {
3069                 val = 0;
3070             }
3071
3072             return val;
3073         },
3074
3075
3076         getDefaultUnit: function(attr) {
3077             if (this.patterns.defaultUnit.test(attr)) {
3078                 return 'px';
3079             }
3080
3081             return '';
3082         },
3083
3084         animateX : function(callback, scope) {
3085             var f = function() {
3086                 this.onComplete.removeListener(f);
3087                 if (typeof callback == "function") {
3088                     callback.call(scope || this, this);
3089                 }
3090             };
3091             this.onComplete.addListener(f, this);
3092             this.animate();
3093         },
3094
3095
3096         setRuntimeAttribute: function(attr) {
3097             var start;
3098             var end;
3099             var attributes = this.attributes;
3100
3101             this.runtimeAttributes[attr] = {};
3102
3103             var isset = function(prop) {
3104                 return (typeof prop !== 'undefined');
3105             };
3106
3107             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3108                 return false;
3109             }
3110
3111             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3112
3113
3114             if (isset(attributes[attr]['to'])) {
3115                 end = attributes[attr]['to'];
3116             } else if (isset(attributes[attr]['by'])) {
3117                 if (start.constructor == Array) {
3118                     end = [];
3119                     for (var i = 0, len = start.length; i < len; ++i) {
3120                         end[i] = start[i] + attributes[attr]['by'][i];
3121                     }
3122                 } else {
3123                     end = start + attributes[attr]['by'];
3124                 }
3125             }
3126
3127             this.runtimeAttributes[attr].start = start;
3128             this.runtimeAttributes[attr].end = end;
3129
3130
3131             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3132         },
3133
3134
3135         init: function(el, attributes, duration, method) {
3136
3137             var isAnimated = false;
3138
3139
3140             var startTime = null;
3141
3142
3143             var actualFrames = 0;
3144
3145
3146             el = Roo.getDom(el);
3147
3148
3149             this.attributes = attributes || {};
3150
3151
3152             this.duration = duration || 1;
3153
3154
3155             this.method = method || Roo.lib.Easing.easeNone;
3156
3157
3158             this.useSeconds = true;
3159
3160
3161             this.currentFrame = 0;
3162
3163
3164             this.totalFrames = Roo.lib.AnimMgr.fps;
3165
3166
3167             this.getEl = function() {
3168                 return el;
3169             };
3170
3171
3172             this.isAnimated = function() {
3173                 return isAnimated;
3174             };
3175
3176
3177             this.getStartTime = function() {
3178                 return startTime;
3179             };
3180
3181             this.runtimeAttributes = {};
3182
3183
3184             this.animate = function() {
3185                 if (this.isAnimated()) {
3186                     return false;
3187                 }
3188
3189                 this.currentFrame = 0;
3190
3191                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3192
3193                 Roo.lib.AnimMgr.registerElement(this);
3194             };
3195
3196
3197             this.stop = function(finish) {
3198                 if (finish) {
3199                     this.currentFrame = this.totalFrames;
3200                     this._onTween.fire();
3201                 }
3202                 Roo.lib.AnimMgr.stop(this);
3203             };
3204
3205             var onStart = function() {
3206                 this.onStart.fire();
3207
3208                 this.runtimeAttributes = {};
3209                 for (var attr in this.attributes) {
3210                     this.setRuntimeAttribute(attr);
3211                 }
3212
3213                 isAnimated = true;
3214                 actualFrames = 0;
3215                 startTime = new Date();
3216             };
3217
3218
3219             var onTween = function() {
3220                 var data = {
3221                     duration: new Date() - this.getStartTime(),
3222                     currentFrame: this.currentFrame
3223                 };
3224
3225                 data.toString = function() {
3226                     return (
3227                             'duration: ' + data.duration +
3228                             ', currentFrame: ' + data.currentFrame
3229                             );
3230                 };
3231
3232                 this.onTween.fire(data);
3233
3234                 var runtimeAttributes = this.runtimeAttributes;
3235
3236                 for (var attr in runtimeAttributes) {
3237                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3238                 }
3239
3240                 actualFrames += 1;
3241             };
3242
3243             var onComplete = function() {
3244                 var actual_duration = (new Date() - startTime) / 1000 ;
3245
3246                 var data = {
3247                     duration: actual_duration,
3248                     frames: actualFrames,
3249                     fps: actualFrames / actual_duration
3250                 };
3251
3252                 data.toString = function() {
3253                     return (
3254                             'duration: ' + data.duration +
3255                             ', frames: ' + data.frames +
3256                             ', fps: ' + data.fps
3257                             );
3258                 };
3259
3260                 isAnimated = false;
3261                 actualFrames = 0;
3262                 this.onComplete.fire(data);
3263             };
3264
3265
3266             this._onStart = new Roo.util.Event(this);
3267             this.onStart = new Roo.util.Event(this);
3268             this.onTween = new Roo.util.Event(this);
3269             this._onTween = new Roo.util.Event(this);
3270             this.onComplete = new Roo.util.Event(this);
3271             this._onComplete = new Roo.util.Event(this);
3272             this._onStart.addListener(onStart);
3273             this._onTween.addListener(onTween);
3274             this._onComplete.addListener(onComplete);
3275         }
3276     };
3277 })();
3278 /*
3279  * Portions of this file are based on pieces of Yahoo User Interface Library
3280  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3281  * YUI licensed under the BSD License:
3282  * http://developer.yahoo.net/yui/license.txt
3283  * <script type="text/javascript">
3284  *
3285  */
3286
3287 Roo.lib.AnimMgr = new function() {
3288
3289         var thread = null;
3290
3291
3292         var queue = [];
3293
3294
3295         var tweenCount = 0;
3296
3297
3298         this.fps = 1000;
3299
3300
3301         this.delay = 1;
3302
3303
3304         this.registerElement = function(tween) {
3305             queue[queue.length] = tween;
3306             tweenCount += 1;
3307             tween._onStart.fire();
3308             this.start();
3309         };
3310
3311
3312         this.unRegister = function(tween, index) {
3313             tween._onComplete.fire();
3314             index = index || getIndex(tween);
3315             if (index != -1) {
3316                 queue.splice(index, 1);
3317             }
3318
3319             tweenCount -= 1;
3320             if (tweenCount <= 0) {
3321                 this.stop();
3322             }
3323         };
3324
3325
3326         this.start = function() {
3327             if (thread === null) {
3328                 thread = setInterval(this.run, this.delay);
3329             }
3330         };
3331
3332
3333         this.stop = function(tween) {
3334             if (!tween) {
3335                 clearInterval(thread);
3336
3337                 for (var i = 0, len = queue.length; i < len; ++i) {
3338                     if (queue[0].isAnimated()) {
3339                         this.unRegister(queue[0], 0);
3340                     }
3341                 }
3342
3343                 queue = [];
3344                 thread = null;
3345                 tweenCount = 0;
3346             }
3347             else {
3348                 this.unRegister(tween);
3349             }
3350         };
3351
3352
3353         this.run = function() {
3354             for (var i = 0, len = queue.length; i < len; ++i) {
3355                 var tween = queue[i];
3356                 if (!tween || !tween.isAnimated()) {
3357                     continue;
3358                 }
3359
3360                 if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3361                 {
3362                     tween.currentFrame += 1;
3363
3364                     if (tween.useSeconds) {
3365                         correctFrame(tween);
3366                     }
3367                     tween._onTween.fire();
3368                 }
3369                 else {
3370                     Roo.lib.AnimMgr.stop(tween, i);
3371                 }
3372             }
3373         };
3374
3375         var getIndex = function(anim) {
3376             for (var i = 0, len = queue.length; i < len; ++i) {
3377                 if (queue[i] == anim) {
3378                     return i;
3379                 }
3380             }
3381             return -1;
3382         };
3383
3384
3385         var correctFrame = function(tween) {
3386             var frames = tween.totalFrames;
3387             var frame = tween.currentFrame;
3388             var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3389             var elapsed = (new Date() - tween.getStartTime());
3390             var tweak = 0;
3391
3392             if (elapsed < tween.duration * 1000) {
3393                 tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3394             } else {
3395                 tweak = frames - (frame + 1);
3396             }
3397             if (tweak > 0 && isFinite(tweak)) {
3398                 if (tween.currentFrame + tweak >= frames) {
3399                     tweak = frames - (frame + 1);
3400                 }
3401
3402                 tween.currentFrame += tweak;
3403             }
3404         };
3405     };/*
3406  * Portions of this file are based on pieces of Yahoo User Interface Library
3407  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3408  * YUI licensed under the BSD License:
3409  * http://developer.yahoo.net/yui/license.txt
3410  * <script type="text/javascript">
3411  *
3412  */
3413 Roo.lib.Bezier = new function() {
3414
3415         this.getPosition = function(points, t) {
3416             var n = points.length;
3417             var tmp = [];
3418
3419             for (var i = 0; i < n; ++i) {
3420                 tmp[i] = [points[i][0], points[i][1]];
3421             }
3422
3423             for (var j = 1; j < n; ++j) {
3424                 for (i = 0; i < n - j; ++i) {
3425                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3426                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3427                 }
3428             }
3429
3430             return [ tmp[0][0], tmp[0][1] ];
3431
3432         };
3433     };/*
3434  * Portions of this file are based on pieces of Yahoo User Interface Library
3435  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3436  * YUI licensed under the BSD License:
3437  * http://developer.yahoo.net/yui/license.txt
3438  * <script type="text/javascript">
3439  *
3440  */
3441 (function() {
3442
3443     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3444         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3445     };
3446
3447     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3448
3449     var fly = Roo.lib.AnimBase.fly;
3450     var Y = Roo.lib;
3451     var superclass = Y.ColorAnim.superclass;
3452     var proto = Y.ColorAnim.prototype;
3453
3454     proto.toString = function() {
3455         var el = this.getEl();
3456         var id = el.id || el.tagName;
3457         return ("ColorAnim " + id);
3458     };
3459
3460     proto.patterns.color = /color$/i;
3461     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3462     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3463     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3464     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3465
3466
3467     proto.parseColor = function(s) {
3468         if (s.length == 3) {
3469             return s;
3470         }
3471
3472         var c = this.patterns.hex.exec(s);
3473         if (c && c.length == 4) {
3474             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3475         }
3476
3477         c = this.patterns.rgb.exec(s);
3478         if (c && c.length == 4) {
3479             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3480         }
3481
3482         c = this.patterns.hex3.exec(s);
3483         if (c && c.length == 4) {
3484             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3485         }
3486
3487         return null;
3488     };
3489     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3490     proto.getAttribute = function(attr) {
3491         var el = this.getEl();
3492         if (this.patterns.color.test(attr)) {
3493             var val = fly(el).getStyle(attr);
3494
3495             if (this.patterns.transparent.test(val)) {
3496                 var parent = el.parentNode;
3497                 val = fly(parent).getStyle(attr);
3498
3499                 while (parent && this.patterns.transparent.test(val)) {
3500                     parent = parent.parentNode;
3501                     val = fly(parent).getStyle(attr);
3502                     if (parent.tagName.toUpperCase() == 'HTML') {
3503                         val = '#fff';
3504                     }
3505                 }
3506             }
3507         } else {
3508             val = superclass.getAttribute.call(this, attr);
3509         }
3510
3511         return val;
3512     };
3513     proto.getAttribute = function(attr) {
3514         var el = this.getEl();
3515         if (this.patterns.color.test(attr)) {
3516             var val = fly(el).getStyle(attr);
3517
3518             if (this.patterns.transparent.test(val)) {
3519                 var parent = el.parentNode;
3520                 val = fly(parent).getStyle(attr);
3521
3522                 while (parent && this.patterns.transparent.test(val)) {
3523                     parent = parent.parentNode;
3524                     val = fly(parent).getStyle(attr);
3525                     if (parent.tagName.toUpperCase() == 'HTML') {
3526                         val = '#fff';
3527                     }
3528                 }
3529             }
3530         } else {
3531             val = superclass.getAttribute.call(this, attr);
3532         }
3533
3534         return val;
3535     };
3536
3537     proto.doMethod = function(attr, start, end) {
3538         var val;
3539
3540         if (this.patterns.color.test(attr)) {
3541             val = [];
3542             for (var i = 0, len = start.length; i < len; ++i) {
3543                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3544             }
3545
3546             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3547         }
3548         else {
3549             val = superclass.doMethod.call(this, attr, start, end);
3550         }
3551
3552         return val;
3553     };
3554
3555     proto.setRuntimeAttribute = function(attr) {
3556         superclass.setRuntimeAttribute.call(this, attr);
3557
3558         if (this.patterns.color.test(attr)) {
3559             var attributes = this.attributes;
3560             var start = this.parseColor(this.runtimeAttributes[attr].start);
3561             var end = this.parseColor(this.runtimeAttributes[attr].end);
3562
3563             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3564                 end = this.parseColor(attributes[attr].by);
3565
3566                 for (var i = 0, len = start.length; i < len; ++i) {
3567                     end[i] = start[i] + end[i];
3568                 }
3569             }
3570
3571             this.runtimeAttributes[attr].start = start;
3572             this.runtimeAttributes[attr].end = end;
3573         }
3574     };
3575 })();
3576
3577 /*
3578  * Portions of this file are based on pieces of Yahoo User Interface Library
3579  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3580  * YUI licensed under the BSD License:
3581  * http://developer.yahoo.net/yui/license.txt
3582  * <script type="text/javascript">
3583  *
3584  */
3585 Roo.lib.Easing = {
3586
3587
3588     easeNone: function (t, b, c, d) {
3589         return c * t / d + b;
3590     },
3591
3592
3593     easeIn: function (t, b, c, d) {
3594         return c * (t /= d) * t + b;
3595     },
3596
3597
3598     easeOut: function (t, b, c, d) {
3599         return -c * (t /= d) * (t - 2) + b;
3600     },
3601
3602
3603     easeBoth: function (t, b, c, d) {
3604         if ((t /= d / 2) < 1) {
3605             return c / 2 * t * t + b;
3606         }
3607
3608         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3609     },
3610
3611
3612     easeInStrong: function (t, b, c, d) {
3613         return c * (t /= d) * t * t * t + b;
3614     },
3615
3616
3617     easeOutStrong: function (t, b, c, d) {
3618         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3619     },
3620
3621
3622     easeBothStrong: function (t, b, c, d) {
3623         if ((t /= d / 2) < 1) {
3624             return c / 2 * t * t * t * t + b;
3625         }
3626
3627         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3628     },
3629
3630
3631
3632     elasticIn: function (t, b, c, d, a, p) {
3633         if (t == 0) {
3634             return b;
3635         }
3636         if ((t /= d) == 1) {
3637             return b + c;
3638         }
3639         if (!p) {
3640             p = d * .3;
3641         }
3642
3643         if (!a || a < Math.abs(c)) {
3644             a = c;
3645             var s = p / 4;
3646         }
3647         else {
3648             var s = p / (2 * Math.PI) * Math.asin(c / a);
3649         }
3650
3651         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3652     },
3653
3654
3655     elasticOut: function (t, b, c, d, a, p) {
3656         if (t == 0) {
3657             return b;
3658         }
3659         if ((t /= d) == 1) {
3660             return b + c;
3661         }
3662         if (!p) {
3663             p = d * .3;
3664         }
3665
3666         if (!a || a < Math.abs(c)) {
3667             a = c;
3668             var s = p / 4;
3669         }
3670         else {
3671             var s = p / (2 * Math.PI) * Math.asin(c / a);
3672         }
3673
3674         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3675     },
3676
3677
3678     elasticBoth: function (t, b, c, d, a, p) {
3679         if (t == 0) {
3680             return b;
3681         }
3682
3683         if ((t /= d / 2) == 2) {
3684             return b + c;
3685         }
3686
3687         if (!p) {
3688             p = d * (.3 * 1.5);
3689         }
3690
3691         if (!a || a < Math.abs(c)) {
3692             a = c;
3693             var s = p / 4;
3694         }
3695         else {
3696             var s = p / (2 * Math.PI) * Math.asin(c / a);
3697         }
3698
3699         if (t < 1) {
3700             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3701                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3702         }
3703         return a * Math.pow(2, -10 * (t -= 1)) *
3704                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3705     },
3706
3707
3708
3709     backIn: function (t, b, c, d, s) {
3710         if (typeof s == 'undefined') {
3711             s = 1.70158;
3712         }
3713         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3714     },
3715
3716
3717     backOut: function (t, b, c, d, s) {
3718         if (typeof s == 'undefined') {
3719             s = 1.70158;
3720         }
3721         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3722     },
3723
3724
3725     backBoth: function (t, b, c, d, s) {
3726         if (typeof s == 'undefined') {
3727             s = 1.70158;
3728         }
3729
3730         if ((t /= d / 2 ) < 1) {
3731             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3732         }
3733         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3734     },
3735
3736
3737     bounceIn: function (t, b, c, d) {
3738         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3739     },
3740
3741
3742     bounceOut: function (t, b, c, d) {
3743         if ((t /= d) < (1 / 2.75)) {
3744             return c * (7.5625 * t * t) + b;
3745         } else if (t < (2 / 2.75)) {
3746             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3747         } else if (t < (2.5 / 2.75)) {
3748             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3749         }
3750         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3751     },
3752
3753
3754     bounceBoth: function (t, b, c, d) {
3755         if (t < d / 2) {
3756             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3757         }
3758         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3759     }
3760 };/*
3761  * Portions of this file are based on pieces of Yahoo User Interface Library
3762  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3763  * YUI licensed under the BSD License:
3764  * http://developer.yahoo.net/yui/license.txt
3765  * <script type="text/javascript">
3766  *
3767  */
3768     (function() {
3769         Roo.lib.Motion = function(el, attributes, duration, method) {
3770             if (el) {
3771                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3772             }
3773         };
3774
3775         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3776
3777
3778         var Y = Roo.lib;
3779         var superclass = Y.Motion.superclass;
3780         var proto = Y.Motion.prototype;
3781
3782         proto.toString = function() {
3783             var el = this.getEl();
3784             var id = el.id || el.tagName;
3785             return ("Motion " + id);
3786         };
3787
3788         proto.patterns.points = /^points$/i;
3789
3790         proto.setAttribute = function(attr, val, unit) {
3791             if (this.patterns.points.test(attr)) {
3792                 unit = unit || 'px';
3793                 superclass.setAttribute.call(this, 'left', val[0], unit);
3794                 superclass.setAttribute.call(this, 'top', val[1], unit);
3795             } else {
3796                 superclass.setAttribute.call(this, attr, val, unit);
3797             }
3798         };
3799
3800         proto.getAttribute = function(attr) {
3801             if (this.patterns.points.test(attr)) {
3802                 var val = [
3803                         superclass.getAttribute.call(this, 'left'),
3804                         superclass.getAttribute.call(this, 'top')
3805                         ];
3806             } else {
3807                 val = superclass.getAttribute.call(this, attr);
3808             }
3809
3810             return val;
3811         };
3812
3813         proto.doMethod = function(attr, start, end) {
3814             var val = null;
3815
3816             if (this.patterns.points.test(attr)) {
3817                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3818                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3819             } else {
3820                 val = superclass.doMethod.call(this, attr, start, end);
3821             }
3822             return val;
3823         };
3824
3825         proto.setRuntimeAttribute = function(attr) {
3826             if (this.patterns.points.test(attr)) {
3827                 var el = this.getEl();
3828                 var attributes = this.attributes;
3829                 var start;
3830                 var control = attributes['points']['control'] || [];
3831                 var end;
3832                 var i, len;
3833
3834                 if (control.length > 0 && !(control[0] instanceof Array)) {
3835                     control = [control];
3836                 } else {
3837                     var tmp = [];
3838                     for (i = 0,len = control.length; i < len; ++i) {
3839                         tmp[i] = control[i];
3840                     }
3841                     control = tmp;
3842                 }
3843
3844                 Roo.fly(el).position();
3845
3846                 if (isset(attributes['points']['from'])) {
3847                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3848                 }
3849                 else {
3850                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3851                 }
3852
3853                 start = this.getAttribute('points');
3854
3855
3856                 if (isset(attributes['points']['to'])) {
3857                     end = translateValues.call(this, attributes['points']['to'], start);
3858
3859                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3860                     for (i = 0,len = control.length; i < len; ++i) {
3861                         control[i] = translateValues.call(this, control[i], start);
3862                     }
3863
3864
3865                 } else if (isset(attributes['points']['by'])) {
3866                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3867
3868                     for (i = 0,len = control.length; i < len; ++i) {
3869                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3870                     }
3871                 }
3872
3873                 this.runtimeAttributes[attr] = [start];
3874
3875                 if (control.length > 0) {
3876                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3877                 }
3878
3879                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
3880             }
3881             else {
3882                 superclass.setRuntimeAttribute.call(this, attr);
3883             }
3884         };
3885
3886         var translateValues = function(val, start) {
3887             var pageXY = Roo.lib.Dom.getXY(this.getEl());
3888             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
3889
3890             return val;
3891         };
3892
3893         var isset = function(prop) {
3894             return (typeof prop !== 'undefined');
3895         };
3896     })();
3897 /*
3898  * Portions of this file are based on pieces of Yahoo User Interface Library
3899  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3900  * YUI licensed under the BSD License:
3901  * http://developer.yahoo.net/yui/license.txt
3902  * <script type="text/javascript">
3903  *
3904  */
3905     (function() {
3906         Roo.lib.Scroll = function(el, attributes, duration, method) {
3907             if (el) {
3908                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
3909             }
3910         };
3911
3912         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
3913
3914
3915         var Y = Roo.lib;
3916         var superclass = Y.Scroll.superclass;
3917         var proto = Y.Scroll.prototype;
3918
3919         proto.toString = function() {
3920             var el = this.getEl();
3921             var id = el.id || el.tagName;
3922             return ("Scroll " + id);
3923         };
3924
3925         proto.doMethod = function(attr, start, end) {
3926             var val = null;
3927
3928             if (attr == 'scroll') {
3929                 val = [
3930                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
3931                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
3932                         ];
3933
3934             } else {
3935                 val = superclass.doMethod.call(this, attr, start, end);
3936             }
3937             return val;
3938         };
3939
3940         proto.getAttribute = function(attr) {
3941             var val = null;
3942             var el = this.getEl();
3943
3944             if (attr == 'scroll') {
3945                 val = [ el.scrollLeft, el.scrollTop ];
3946             } else {
3947                 val = superclass.getAttribute.call(this, attr);
3948             }
3949
3950             return val;
3951         };
3952
3953         proto.setAttribute = function(attr, val, unit) {
3954             var el = this.getEl();
3955
3956             if (attr == 'scroll') {
3957                 el.scrollLeft = val[0];
3958                 el.scrollTop = val[1];
3959             } else {
3960                 superclass.setAttribute.call(this, attr, val, unit);
3961             }
3962         };
3963     })();
3964 /*
3965  * Based on:
3966  * Ext JS Library 1.1.1
3967  * Copyright(c) 2006-2007, Ext JS, LLC.
3968  *
3969  * Originally Released Under LGPL - original licence link has changed is not relivant.
3970  *
3971  * Fork - LGPL
3972  * <script type="text/javascript">
3973  */
3974  
3975
3976 /**
3977  * @class Roo.DomHelper
3978  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
3979  * 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>.
3980  * @singleton
3981  */
3982 Roo.DomHelper = function(){
3983     var tempTableEl = null;
3984     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
3985     var tableRe = /^table|tbody|tr|td$/i;
3986     var xmlns = {};
3987     // build as innerHTML where available
3988     /** @ignore */
3989     var createHtml = function(o){
3990         if(typeof o == 'string'){
3991             return o;
3992         }
3993         var b = "";
3994         if(!o.tag){
3995             o.tag = "div";
3996         }
3997         b += "<" + o.tag;
3998         for(var attr in o){
3999             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4000             if(attr == "style"){
4001                 var s = o["style"];
4002                 if(typeof s == "function"){
4003                     s = s.call();
4004                 }
4005                 if(typeof s == "string"){
4006                     b += ' style="' + s + '"';
4007                 }else if(typeof s == "object"){
4008                     b += ' style="';
4009                     for(var key in s){
4010                         if(typeof s[key] != "function"){
4011                             b += key + ":" + s[key] + ";";
4012                         }
4013                     }
4014                     b += '"';
4015                 }
4016             }else{
4017                 if(attr == "cls"){
4018                     b += ' class="' + o["cls"] + '"';
4019                 }else if(attr == "htmlFor"){
4020                     b += ' for="' + o["htmlFor"] + '"';
4021                 }else{
4022                     b += " " + attr + '="' + o[attr] + '"';
4023                 }
4024             }
4025         }
4026         if(emptyTags.test(o.tag)){
4027             b += "/>";
4028         }else{
4029             b += ">";
4030             var cn = o.children || o.cn;
4031             if(cn){
4032                 //http://bugs.kde.org/show_bug.cgi?id=71506
4033                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4034                     for(var i = 0, len = cn.length; i < len; i++) {
4035                         b += createHtml(cn[i], b);
4036                     }
4037                 }else{
4038                     b += createHtml(cn, b);
4039                 }
4040             }
4041             if(o.html){
4042                 b += o.html;
4043             }
4044             b += "</" + o.tag + ">";
4045         }
4046         return b;
4047     };
4048
4049     // build as dom
4050     /** @ignore */
4051     var createDom = function(o, parentNode){
4052          
4053         // defininition craeted..
4054         var ns = false;
4055         if (o.ns && o.ns != 'html') {
4056                
4057             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4058                 xmlns[o.ns] = o.xmlns;
4059                 ns = o.xmlns;
4060             }
4061             if (typeof(xmlns[o.ns]) == 'undefined') {
4062                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4063             }
4064             ns = xmlns[o.ns];
4065         }
4066         
4067         
4068         if (typeof(o) == 'string') {
4069             return parentNode.appendChild(document.createTextNode(o));
4070         }
4071         o.tag = o.tag || div;
4072         if (o.ns && Roo.isIE) {
4073             ns = false;
4074             o.tag = o.ns + ':' + o.tag;
4075             
4076         }
4077         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4078         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4079         for(var attr in o){
4080             
4081             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4082                     attr == "style" || typeof o[attr] == "function") continue;
4083                     
4084             if(attr=="cls" && Roo.isIE){
4085                 el.className = o["cls"];
4086             }else{
4087                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4088                 else el[attr] = o[attr];
4089             }
4090         }
4091         Roo.DomHelper.applyStyles(el, o.style);
4092         var cn = o.children || o.cn;
4093         if(cn){
4094             //http://bugs.kde.org/show_bug.cgi?id=71506
4095              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4096                 for(var i = 0, len = cn.length; i < len; i++) {
4097                     createDom(cn[i], el);
4098                 }
4099             }else{
4100                 createDom(cn, el);
4101             }
4102         }
4103         if(o.html){
4104             el.innerHTML = o.html;
4105         }
4106         if(parentNode){
4107            parentNode.appendChild(el);
4108         }
4109         return el;
4110     };
4111
4112     var ieTable = function(depth, s, h, e){
4113         tempTableEl.innerHTML = [s, h, e].join('');
4114         var i = -1, el = tempTableEl;
4115         while(++i < depth){
4116             el = el.firstChild;
4117         }
4118         return el;
4119     };
4120
4121     // kill repeat to save bytes
4122     var ts = '<table>',
4123         te = '</table>',
4124         tbs = ts+'<tbody>',
4125         tbe = '</tbody>'+te,
4126         trs = tbs + '<tr>',
4127         tre = '</tr>'+tbe;
4128
4129     /**
4130      * @ignore
4131      * Nasty code for IE's broken table implementation
4132      */
4133     var insertIntoTable = function(tag, where, el, html){
4134         if(!tempTableEl){
4135             tempTableEl = document.createElement('div');
4136         }
4137         var node;
4138         var before = null;
4139         if(tag == 'td'){
4140             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4141                 return;
4142             }
4143             if(where == 'beforebegin'){
4144                 before = el;
4145                 el = el.parentNode;
4146             } else{
4147                 before = el.nextSibling;
4148                 el = el.parentNode;
4149             }
4150             node = ieTable(4, trs, html, tre);
4151         }
4152         else if(tag == 'tr'){
4153             if(where == 'beforebegin'){
4154                 before = el;
4155                 el = el.parentNode;
4156                 node = ieTable(3, tbs, html, tbe);
4157             } else if(where == 'afterend'){
4158                 before = el.nextSibling;
4159                 el = el.parentNode;
4160                 node = ieTable(3, tbs, html, tbe);
4161             } else{ // INTO a TR
4162                 if(where == 'afterbegin'){
4163                     before = el.firstChild;
4164                 }
4165                 node = ieTable(4, trs, html, tre);
4166             }
4167         } else if(tag == 'tbody'){
4168             if(where == 'beforebegin'){
4169                 before = el;
4170                 el = el.parentNode;
4171                 node = ieTable(2, ts, html, te);
4172             } else if(where == 'afterend'){
4173                 before = el.nextSibling;
4174                 el = el.parentNode;
4175                 node = ieTable(2, ts, html, te);
4176             } else{
4177                 if(where == 'afterbegin'){
4178                     before = el.firstChild;
4179                 }
4180                 node = ieTable(3, tbs, html, tbe);
4181             }
4182         } else{ // TABLE
4183             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4184                 return;
4185             }
4186             if(where == 'afterbegin'){
4187                 before = el.firstChild;
4188             }
4189             node = ieTable(2, ts, html, te);
4190         }
4191         el.insertBefore(node, before);
4192         return node;
4193     };
4194
4195     return {
4196     /** True to force the use of DOM instead of html fragments @type Boolean */
4197     useDom : false,
4198
4199     /**
4200      * Returns the markup for the passed Element(s) config
4201      * @param {Object} o The Dom object spec (and children)
4202      * @return {String}
4203      */
4204     markup : function(o){
4205         return createHtml(o);
4206     },
4207
4208     /**
4209      * Applies a style specification to an element
4210      * @param {String/HTMLElement} el The element to apply styles to
4211      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4212      * a function which returns such a specification.
4213      */
4214     applyStyles : function(el, styles){
4215         if(styles){
4216            el = Roo.fly(el);
4217            if(typeof styles == "string"){
4218                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4219                var matches;
4220                while ((matches = re.exec(styles)) != null){
4221                    el.setStyle(matches[1], matches[2]);
4222                }
4223            }else if (typeof styles == "object"){
4224                for (var style in styles){
4225                   el.setStyle(style, styles[style]);
4226                }
4227            }else if (typeof styles == "function"){
4228                 Roo.DomHelper.applyStyles(el, styles.call());
4229            }
4230         }
4231     },
4232
4233     /**
4234      * Inserts an HTML fragment into the Dom
4235      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4236      * @param {HTMLElement} el The context element
4237      * @param {String} html The HTML fragmenet
4238      * @return {HTMLElement} The new node
4239      */
4240     insertHtml : function(where, el, html){
4241         where = where.toLowerCase();
4242         if(el.insertAdjacentHTML){
4243             if(tableRe.test(el.tagName)){
4244                 var rs;
4245                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4246                     return rs;
4247                 }
4248             }
4249             switch(where){
4250                 case "beforebegin":
4251                     el.insertAdjacentHTML('BeforeBegin', html);
4252                     return el.previousSibling;
4253                 case "afterbegin":
4254                     el.insertAdjacentHTML('AfterBegin', html);
4255                     return el.firstChild;
4256                 case "beforeend":
4257                     el.insertAdjacentHTML('BeforeEnd', html);
4258                     return el.lastChild;
4259                 case "afterend":
4260                     el.insertAdjacentHTML('AfterEnd', html);
4261                     return el.nextSibling;
4262             }
4263             throw 'Illegal insertion point -> "' + where + '"';
4264         }
4265         var range = el.ownerDocument.createRange();
4266         var frag;
4267         switch(where){
4268              case "beforebegin":
4269                 range.setStartBefore(el);
4270                 frag = range.createContextualFragment(html);
4271                 el.parentNode.insertBefore(frag, el);
4272                 return el.previousSibling;
4273              case "afterbegin":
4274                 if(el.firstChild){
4275                     range.setStartBefore(el.firstChild);
4276                     frag = range.createContextualFragment(html);
4277                     el.insertBefore(frag, el.firstChild);
4278                     return el.firstChild;
4279                 }else{
4280                     el.innerHTML = html;
4281                     return el.firstChild;
4282                 }
4283             case "beforeend":
4284                 if(el.lastChild){
4285                     range.setStartAfter(el.lastChild);
4286                     frag = range.createContextualFragment(html);
4287                     el.appendChild(frag);
4288                     return el.lastChild;
4289                 }else{
4290                     el.innerHTML = html;
4291                     return el.lastChild;
4292                 }
4293             case "afterend":
4294                 range.setStartAfter(el);
4295                 frag = range.createContextualFragment(html);
4296                 el.parentNode.insertBefore(frag, el.nextSibling);
4297                 return el.nextSibling;
4298             }
4299             throw 'Illegal insertion point -> "' + where + '"';
4300     },
4301
4302     /**
4303      * Creates new Dom element(s) and inserts them before el
4304      * @param {String/HTMLElement/Element} el The context element
4305      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4306      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4307      * @return {HTMLElement/Roo.Element} The new node
4308      */
4309     insertBefore : function(el, o, returnElement){
4310         return this.doInsert(el, o, returnElement, "beforeBegin");
4311     },
4312
4313     /**
4314      * Creates new Dom element(s) and inserts them after el
4315      * @param {String/HTMLElement/Element} el The context element
4316      * @param {Object} o The Dom object spec (and children)
4317      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4318      * @return {HTMLElement/Roo.Element} The new node
4319      */
4320     insertAfter : function(el, o, returnElement){
4321         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4322     },
4323
4324     /**
4325      * Creates new Dom element(s) and inserts them as the first child of el
4326      * @param {String/HTMLElement/Element} el The context element
4327      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4328      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4329      * @return {HTMLElement/Roo.Element} The new node
4330      */
4331     insertFirst : function(el, o, returnElement){
4332         return this.doInsert(el, o, returnElement, "afterBegin");
4333     },
4334
4335     // private
4336     doInsert : function(el, o, returnElement, pos, sibling){
4337         el = Roo.getDom(el);
4338         var newNode;
4339         if(this.useDom || o.ns){
4340             newNode = createDom(o, null);
4341             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4342         }else{
4343             var html = createHtml(o);
4344             newNode = this.insertHtml(pos, el, html);
4345         }
4346         return returnElement ? Roo.get(newNode, true) : newNode;
4347     },
4348
4349     /**
4350      * Creates new Dom element(s) and appends them to el
4351      * @param {String/HTMLElement/Element} el The context element
4352      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4353      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4354      * @return {HTMLElement/Roo.Element} The new node
4355      */
4356     append : function(el, o, returnElement){
4357         el = Roo.getDom(el);
4358         var newNode;
4359         if(this.useDom || o.ns){
4360             newNode = createDom(o, null);
4361             el.appendChild(newNode);
4362         }else{
4363             var html = createHtml(o);
4364             newNode = this.insertHtml("beforeEnd", el, html);
4365         }
4366         return returnElement ? Roo.get(newNode, true) : newNode;
4367     },
4368
4369     /**
4370      * Creates new Dom element(s) and overwrites the contents of el with them
4371      * @param {String/HTMLElement/Element} el The context element
4372      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4373      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4374      * @return {HTMLElement/Roo.Element} The new node
4375      */
4376     overwrite : function(el, o, returnElement){
4377         el = Roo.getDom(el);
4378         if (o.ns) {
4379           
4380             while (el.childNodes.length) {
4381                 el.removeChild(el.firstChild);
4382             }
4383             createDom(o, el);
4384         } else {
4385             el.innerHTML = createHtml(o);   
4386         }
4387         
4388         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4389     },
4390
4391     /**
4392      * Creates a new Roo.DomHelper.Template from the Dom object spec
4393      * @param {Object} o The Dom object spec (and children)
4394      * @return {Roo.DomHelper.Template} The new template
4395      */
4396     createTemplate : function(o){
4397         var html = createHtml(o);
4398         return new Roo.Template(html);
4399     }
4400     };
4401 }();
4402 /*
4403  * Based on:
4404  * Ext JS Library 1.1.1
4405  * Copyright(c) 2006-2007, Ext JS, LLC.
4406  *
4407  * Originally Released Under LGPL - original licence link has changed is not relivant.
4408  *
4409  * Fork - LGPL
4410  * <script type="text/javascript">
4411  */
4412  
4413 /**
4414 * @class Roo.Template
4415 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4416 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4417 * Usage:
4418 <pre><code>
4419 var t = new Roo.Template({
4420     html :  '&lt;div name="{id}"&gt;' + 
4421         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4422         '&lt;/div&gt;',
4423     myformat: function (value, allValues) {
4424         return 'XX' + value;
4425     }
4426 });
4427 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4428 </code></pre>
4429 * 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>. 
4430 * @constructor
4431 * @param {Object} cfg - Configuration object.
4432 */
4433 Roo.Template = function(cfg){
4434     // BC!
4435     if(cfg instanceof Array){
4436         cfg = cfg.join("");
4437     }else if(arguments.length > 1){
4438         cfg = Array.prototype.join.call(arguments, "");
4439     }
4440     
4441     
4442     if (typeof(cfg) == 'object') {
4443         Roo.apply(this,cfg)
4444     } else {
4445         // bc
4446         this.html = cfg;
4447     }
4448     
4449     
4450 };
4451 Roo.Template.prototype = {
4452     
4453     /**
4454      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4455      */
4456     html : '',
4457     /**
4458      * Returns an HTML fragment of this template with the specified values applied.
4459      * @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'})
4460      * @return {String} The HTML fragment
4461      */
4462     applyTemplate : function(values){
4463         try {
4464             
4465             if(this.compiled){
4466                 return this.compiled(values);
4467             }
4468             var useF = this.disableFormats !== true;
4469             var fm = Roo.util.Format, tpl = this;
4470             var fn = function(m, name, format, args){
4471                 if(format && useF){
4472                     if(format.substr(0, 5) == "this."){
4473                         return tpl.call(format.substr(5), values[name], values);
4474                     }else{
4475                         if(args){
4476                             // quoted values are required for strings in compiled templates, 
4477                             // but for non compiled we need to strip them
4478                             // quoted reversed for jsmin
4479                             var re = /^\s*['"](.*)["']\s*$/;
4480                             args = args.split(',');
4481                             for(var i = 0, len = args.length; i < len; i++){
4482                                 args[i] = args[i].replace(re, "$1");
4483                             }
4484                             args = [values[name]].concat(args);
4485                         }else{
4486                             args = [values[name]];
4487                         }
4488                         return fm[format].apply(fm, args);
4489                     }
4490                 }else{
4491                     return values[name] !== undefined ? values[name] : "";
4492                 }
4493             };
4494             return this.html.replace(this.re, fn);
4495         } catch (e) {
4496             Roo.log(e);
4497             throw e;
4498         }
4499          
4500     },
4501     
4502     /**
4503      * Sets the HTML used as the template and optionally compiles it.
4504      * @param {String} html
4505      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4506      * @return {Roo.Template} this
4507      */
4508     set : function(html, compile){
4509         this.html = html;
4510         this.compiled = null;
4511         if(compile){
4512             this.compile();
4513         }
4514         return this;
4515     },
4516     
4517     /**
4518      * True to disable format functions (defaults to false)
4519      * @type Boolean
4520      */
4521     disableFormats : false,
4522     
4523     /**
4524     * The regular expression used to match template variables 
4525     * @type RegExp
4526     * @property 
4527     */
4528     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4529     
4530     /**
4531      * Compiles the template into an internal function, eliminating the RegEx overhead.
4532      * @return {Roo.Template} this
4533      */
4534     compile : function(){
4535         var fm = Roo.util.Format;
4536         var useF = this.disableFormats !== true;
4537         var sep = Roo.isGecko ? "+" : ",";
4538         var fn = function(m, name, format, args){
4539             if(format && useF){
4540                 args = args ? ',' + args : "";
4541                 if(format.substr(0, 5) != "this."){
4542                     format = "fm." + format + '(';
4543                 }else{
4544                     format = 'this.call("'+ format.substr(5) + '", ';
4545                     args = ", values";
4546                 }
4547             }else{
4548                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4549             }
4550             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4551         };
4552         var body;
4553         // branched to use + in gecko and [].join() in others
4554         if(Roo.isGecko){
4555             body = "this.compiled = function(values){ return '" +
4556                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4557                     "';};";
4558         }else{
4559             body = ["this.compiled = function(values){ return ['"];
4560             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4561             body.push("'].join('');};");
4562             body = body.join('');
4563         }
4564         /**
4565          * eval:var:values
4566          * eval:var:fm
4567          */
4568         eval(body);
4569         return this;
4570     },
4571     
4572     // private function used to call members
4573     call : function(fnName, value, allValues){
4574         return this[fnName](value, allValues);
4575     },
4576     
4577     /**
4578      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4579      * @param {String/HTMLElement/Roo.Element} el The context element
4580      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4581      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4582      * @return {HTMLElement/Roo.Element} The new node or Element
4583      */
4584     insertFirst: function(el, values, returnElement){
4585         return this.doInsert('afterBegin', el, values, returnElement);
4586     },
4587
4588     /**
4589      * Applies the supplied values to the template and inserts the new node(s) before el.
4590      * @param {String/HTMLElement/Roo.Element} el The context element
4591      * @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'})
4592      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4593      * @return {HTMLElement/Roo.Element} The new node or Element
4594      */
4595     insertBefore: function(el, values, returnElement){
4596         return this.doInsert('beforeBegin', el, values, returnElement);
4597     },
4598
4599     /**
4600      * Applies the supplied values to the template and inserts the new node(s) after el.
4601      * @param {String/HTMLElement/Roo.Element} el The context element
4602      * @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'})
4603      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4604      * @return {HTMLElement/Roo.Element} The new node or Element
4605      */
4606     insertAfter : function(el, values, returnElement){
4607         return this.doInsert('afterEnd', el, values, returnElement);
4608     },
4609     
4610     /**
4611      * Applies the supplied values to the template and appends the new node(s) to el.
4612      * @param {String/HTMLElement/Roo.Element} el The context element
4613      * @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'})
4614      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4615      * @return {HTMLElement/Roo.Element} The new node or Element
4616      */
4617     append : function(el, values, returnElement){
4618         return this.doInsert('beforeEnd', el, values, returnElement);
4619     },
4620
4621     doInsert : function(where, el, values, returnEl){
4622         el = Roo.getDom(el);
4623         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4624         return returnEl ? Roo.get(newNode, true) : newNode;
4625     },
4626
4627     /**
4628      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4629      * @param {String/HTMLElement/Roo.Element} el The context element
4630      * @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'})
4631      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4632      * @return {HTMLElement/Roo.Element} The new node or Element
4633      */
4634     overwrite : function(el, values, returnElement){
4635         el = Roo.getDom(el);
4636         el.innerHTML = this.applyTemplate(values);
4637         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4638     }
4639 };
4640 /**
4641  * Alias for {@link #applyTemplate}
4642  * @method
4643  */
4644 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4645
4646 // backwards compat
4647 Roo.DomHelper.Template = Roo.Template;
4648
4649 /**
4650  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4651  * @param {String/HTMLElement} el A DOM element or its id
4652  * @returns {Roo.Template} The created template
4653  * @static
4654  */
4655 Roo.Template.from = function(el){
4656     el = Roo.getDom(el);
4657     return new Roo.Template(el.value || el.innerHTML);
4658 };/*
4659  * Based on:
4660  * Ext JS Library 1.1.1
4661  * Copyright(c) 2006-2007, Ext JS, LLC.
4662  *
4663  * Originally Released Under LGPL - original licence link has changed is not relivant.
4664  *
4665  * Fork - LGPL
4666  * <script type="text/javascript">
4667  */
4668  
4669
4670 /*
4671  * This is code is also distributed under MIT license for use
4672  * with jQuery and prototype JavaScript libraries.
4673  */
4674 /**
4675  * @class Roo.DomQuery
4676 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).
4677 <p>
4678 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>
4679
4680 <p>
4681 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.
4682 </p>
4683 <h4>Element Selectors:</h4>
4684 <ul class="list">
4685     <li> <b>*</b> any element</li>
4686     <li> <b>E</b> an element with the tag E</li>
4687     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4688     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4689     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4690     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4691 </ul>
4692 <h4>Attribute Selectors:</h4>
4693 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4694 <ul class="list">
4695     <li> <b>E[foo]</b> has an attribute "foo"</li>
4696     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4697     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4698     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4699     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4700     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4701     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4702 </ul>
4703 <h4>Pseudo Classes:</h4>
4704 <ul class="list">
4705     <li> <b>E:first-child</b> E is the first child of its parent</li>
4706     <li> <b>E:last-child</b> E is the last child of its parent</li>
4707     <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>
4708     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4709     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4710     <li> <b>E:only-child</b> E is the only child of its parent</li>
4711     <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>
4712     <li> <b>E:first</b> the first E in the resultset</li>
4713     <li> <b>E:last</b> the last E in the resultset</li>
4714     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4715     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4716     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4717     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4718     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4719     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4720     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4721     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4722     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4723 </ul>
4724 <h4>CSS Value Selectors:</h4>
4725 <ul class="list">
4726     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4727     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4728     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4729     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4730     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4731     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4732 </ul>
4733  * @singleton
4734  */
4735 Roo.DomQuery = function(){
4736     var cache = {}, simpleCache = {}, valueCache = {};
4737     var nonSpace = /\S/;
4738     var trimRe = /^\s+|\s+$/g;
4739     var tplRe = /\{(\d+)\}/g;
4740     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4741     var tagTokenRe = /^(#)?([\w-\*]+)/;
4742     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4743
4744     function child(p, index){
4745         var i = 0;
4746         var n = p.firstChild;
4747         while(n){
4748             if(n.nodeType == 1){
4749                if(++i == index){
4750                    return n;
4751                }
4752             }
4753             n = n.nextSibling;
4754         }
4755         return null;
4756     };
4757
4758     function next(n){
4759         while((n = n.nextSibling) && n.nodeType != 1);
4760         return n;
4761     };
4762
4763     function prev(n){
4764         while((n = n.previousSibling) && n.nodeType != 1);
4765         return n;
4766     };
4767
4768     function children(d){
4769         var n = d.firstChild, ni = -1;
4770             while(n){
4771                 var nx = n.nextSibling;
4772                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4773                     d.removeChild(n);
4774                 }else{
4775                     n.nodeIndex = ++ni;
4776                 }
4777                 n = nx;
4778             }
4779             return this;
4780         };
4781
4782     function byClassName(c, a, v){
4783         if(!v){
4784             return c;
4785         }
4786         var r = [], ri = -1, cn;
4787         for(var i = 0, ci; ci = c[i]; i++){
4788             if((' '+ci.className+' ').indexOf(v) != -1){
4789                 r[++ri] = ci;
4790             }
4791         }
4792         return r;
4793     };
4794
4795     function attrValue(n, attr){
4796         if(!n.tagName && typeof n.length != "undefined"){
4797             n = n[0];
4798         }
4799         if(!n){
4800             return null;
4801         }
4802         if(attr == "for"){
4803             return n.htmlFor;
4804         }
4805         if(attr == "class" || attr == "className"){
4806             return n.className;
4807         }
4808         return n.getAttribute(attr) || n[attr];
4809
4810     };
4811
4812     function getNodes(ns, mode, tagName){
4813         var result = [], ri = -1, cs;
4814         if(!ns){
4815             return result;
4816         }
4817         tagName = tagName || "*";
4818         if(typeof ns.getElementsByTagName != "undefined"){
4819             ns = [ns];
4820         }
4821         if(!mode){
4822             for(var i = 0, ni; ni = ns[i]; i++){
4823                 cs = ni.getElementsByTagName(tagName);
4824                 for(var j = 0, ci; ci = cs[j]; j++){
4825                     result[++ri] = ci;
4826                 }
4827             }
4828         }else if(mode == "/" || mode == ">"){
4829             var utag = tagName.toUpperCase();
4830             for(var i = 0, ni, cn; ni = ns[i]; i++){
4831                 cn = ni.children || ni.childNodes;
4832                 for(var j = 0, cj; cj = cn[j]; j++){
4833                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
4834                         result[++ri] = cj;
4835                     }
4836                 }
4837             }
4838         }else if(mode == "+"){
4839             var utag = tagName.toUpperCase();
4840             for(var i = 0, n; n = ns[i]; i++){
4841                 while((n = n.nextSibling) && n.nodeType != 1);
4842                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
4843                     result[++ri] = n;
4844                 }
4845             }
4846         }else if(mode == "~"){
4847             for(var i = 0, n; n = ns[i]; i++){
4848                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
4849                 if(n){
4850                     result[++ri] = n;
4851                 }
4852             }
4853         }
4854         return result;
4855     };
4856
4857     function concat(a, b){
4858         if(b.slice){
4859             return a.concat(b);
4860         }
4861         for(var i = 0, l = b.length; i < l; i++){
4862             a[a.length] = b[i];
4863         }
4864         return a;
4865     }
4866
4867     function byTag(cs, tagName){
4868         if(cs.tagName || cs == document){
4869             cs = [cs];
4870         }
4871         if(!tagName){
4872             return cs;
4873         }
4874         var r = [], ri = -1;
4875         tagName = tagName.toLowerCase();
4876         for(var i = 0, ci; ci = cs[i]; i++){
4877             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
4878                 r[++ri] = ci;
4879             }
4880         }
4881         return r;
4882     };
4883
4884     function byId(cs, attr, id){
4885         if(cs.tagName || cs == document){
4886             cs = [cs];
4887         }
4888         if(!id){
4889             return cs;
4890         }
4891         var r = [], ri = -1;
4892         for(var i = 0,ci; ci = cs[i]; i++){
4893             if(ci && ci.id == id){
4894                 r[++ri] = ci;
4895                 return r;
4896             }
4897         }
4898         return r;
4899     };
4900
4901     function byAttribute(cs, attr, value, op, custom){
4902         var r = [], ri = -1, st = custom=="{";
4903         var f = Roo.DomQuery.operators[op];
4904         for(var i = 0, ci; ci = cs[i]; i++){
4905             var a;
4906             if(st){
4907                 a = Roo.DomQuery.getStyle(ci, attr);
4908             }
4909             else if(attr == "class" || attr == "className"){
4910                 a = ci.className;
4911             }else if(attr == "for"){
4912                 a = ci.htmlFor;
4913             }else if(attr == "href"){
4914                 a = ci.getAttribute("href", 2);
4915             }else{
4916                 a = ci.getAttribute(attr);
4917             }
4918             if((f && f(a, value)) || (!f && a)){
4919                 r[++ri] = ci;
4920             }
4921         }
4922         return r;
4923     };
4924
4925     function byPseudo(cs, name, value){
4926         return Roo.DomQuery.pseudos[name](cs, value);
4927     };
4928
4929     // This is for IE MSXML which does not support expandos.
4930     // IE runs the same speed using setAttribute, however FF slows way down
4931     // and Safari completely fails so they need to continue to use expandos.
4932     var isIE = window.ActiveXObject ? true : false;
4933
4934     // this eval is stop the compressor from
4935     // renaming the variable to something shorter
4936     
4937     /** eval:var:batch */
4938     var batch = 30803; 
4939
4940     var key = 30803;
4941
4942     function nodupIEXml(cs){
4943         var d = ++key;
4944         cs[0].setAttribute("_nodup", d);
4945         var r = [cs[0]];
4946         for(var i = 1, len = cs.length; i < len; i++){
4947             var c = cs[i];
4948             if(!c.getAttribute("_nodup") != d){
4949                 c.setAttribute("_nodup", d);
4950                 r[r.length] = c;
4951             }
4952         }
4953         for(var i = 0, len = cs.length; i < len; i++){
4954             cs[i].removeAttribute("_nodup");
4955         }
4956         return r;
4957     }
4958
4959     function nodup(cs){
4960         if(!cs){
4961             return [];
4962         }
4963         var len = cs.length, c, i, r = cs, cj, ri = -1;
4964         if(!len || typeof cs.nodeType != "undefined" || len == 1){
4965             return cs;
4966         }
4967         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
4968             return nodupIEXml(cs);
4969         }
4970         var d = ++key;
4971         cs[0]._nodup = d;
4972         for(i = 1; c = cs[i]; i++){
4973             if(c._nodup != d){
4974                 c._nodup = d;
4975             }else{
4976                 r = [];
4977                 for(var j = 0; j < i; j++){
4978                     r[++ri] = cs[j];
4979                 }
4980                 for(j = i+1; cj = cs[j]; j++){
4981                     if(cj._nodup != d){
4982                         cj._nodup = d;
4983                         r[++ri] = cj;
4984                     }
4985                 }
4986                 return r;
4987             }
4988         }
4989         return r;
4990     }
4991
4992     function quickDiffIEXml(c1, c2){
4993         var d = ++key;
4994         for(var i = 0, len = c1.length; i < len; i++){
4995             c1[i].setAttribute("_qdiff", d);
4996         }
4997         var r = [];
4998         for(var i = 0, len = c2.length; i < len; i++){
4999             if(c2[i].getAttribute("_qdiff") != d){
5000                 r[r.length] = c2[i];
5001             }
5002         }
5003         for(var i = 0, len = c1.length; i < len; i++){
5004            c1[i].removeAttribute("_qdiff");
5005         }
5006         return r;
5007     }
5008
5009     function quickDiff(c1, c2){
5010         var len1 = c1.length;
5011         if(!len1){
5012             return c2;
5013         }
5014         if(isIE && c1[0].selectSingleNode){
5015             return quickDiffIEXml(c1, c2);
5016         }
5017         var d = ++key;
5018         for(var i = 0; i < len1; i++){
5019             c1[i]._qdiff = d;
5020         }
5021         var r = [];
5022         for(var i = 0, len = c2.length; i < len; i++){
5023             if(c2[i]._qdiff != d){
5024                 r[r.length] = c2[i];
5025             }
5026         }
5027         return r;
5028     }
5029
5030     function quickId(ns, mode, root, id){
5031         if(ns == root){
5032            var d = root.ownerDocument || root;
5033            return d.getElementById(id);
5034         }
5035         ns = getNodes(ns, mode, "*");
5036         return byId(ns, null, id);
5037     }
5038
5039     return {
5040         getStyle : function(el, name){
5041             return Roo.fly(el).getStyle(name);
5042         },
5043         /**
5044          * Compiles a selector/xpath query into a reusable function. The returned function
5045          * takes one parameter "root" (optional), which is the context node from where the query should start.
5046          * @param {String} selector The selector/xpath query
5047          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5048          * @return {Function}
5049          */
5050         compile : function(path, type){
5051             type = type || "select";
5052             
5053             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5054             var q = path, mode, lq;
5055             var tk = Roo.DomQuery.matchers;
5056             var tklen = tk.length;
5057             var mm;
5058
5059             // accept leading mode switch
5060             var lmode = q.match(modeRe);
5061             if(lmode && lmode[1]){
5062                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5063                 q = q.replace(lmode[1], "");
5064             }
5065             // strip leading slashes
5066             while(path.substr(0, 1)=="/"){
5067                 path = path.substr(1);
5068             }
5069
5070             while(q && lq != q){
5071                 lq = q;
5072                 var tm = q.match(tagTokenRe);
5073                 if(type == "select"){
5074                     if(tm){
5075                         if(tm[1] == "#"){
5076                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5077                         }else{
5078                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5079                         }
5080                         q = q.replace(tm[0], "");
5081                     }else if(q.substr(0, 1) != '@'){
5082                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5083                     }
5084                 }else{
5085                     if(tm){
5086                         if(tm[1] == "#"){
5087                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5088                         }else{
5089                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5090                         }
5091                         q = q.replace(tm[0], "");
5092                     }
5093                 }
5094                 while(!(mm = q.match(modeRe))){
5095                     var matched = false;
5096                     for(var j = 0; j < tklen; j++){
5097                         var t = tk[j];
5098                         var m = q.match(t.re);
5099                         if(m){
5100                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5101                                                     return m[i];
5102                                                 });
5103                             q = q.replace(m[0], "");
5104                             matched = true;
5105                             break;
5106                         }
5107                     }
5108                     // prevent infinite loop on bad selector
5109                     if(!matched){
5110                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5111                     }
5112                 }
5113                 if(mm[1]){
5114                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5115                     q = q.replace(mm[1], "");
5116                 }
5117             }
5118             fn[fn.length] = "return nodup(n);\n}";
5119             
5120              /** 
5121               * list of variables that need from compression as they are used by eval.
5122              *  eval:var:batch 
5123              *  eval:var:nodup
5124              *  eval:var:byTag
5125              *  eval:var:ById
5126              *  eval:var:getNodes
5127              *  eval:var:quickId
5128              *  eval:var:mode
5129              *  eval:var:root
5130              *  eval:var:n
5131              *  eval:var:byClassName
5132              *  eval:var:byPseudo
5133              *  eval:var:byAttribute
5134              *  eval:var:attrValue
5135              * 
5136              **/ 
5137             eval(fn.join(""));
5138             return f;
5139         },
5140
5141         /**
5142          * Selects a group of elements.
5143          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5144          * @param {Node} root (optional) The start of the query (defaults to document).
5145          * @return {Array}
5146          */
5147         select : function(path, root, type){
5148             if(!root || root == document){
5149                 root = document;
5150             }
5151             if(typeof root == "string"){
5152                 root = document.getElementById(root);
5153             }
5154             var paths = path.split(",");
5155             var results = [];
5156             for(var i = 0, len = paths.length; i < len; i++){
5157                 var p = paths[i].replace(trimRe, "");
5158                 if(!cache[p]){
5159                     cache[p] = Roo.DomQuery.compile(p);
5160                     if(!cache[p]){
5161                         throw p + " is not a valid selector";
5162                     }
5163                 }
5164                 var result = cache[p](root);
5165                 if(result && result != document){
5166                     results = results.concat(result);
5167                 }
5168             }
5169             if(paths.length > 1){
5170                 return nodup(results);
5171             }
5172             return results;
5173         },
5174
5175         /**
5176          * Selects a single element.
5177          * @param {String} selector The selector/xpath query
5178          * @param {Node} root (optional) The start of the query (defaults to document).
5179          * @return {Element}
5180          */
5181         selectNode : function(path, root){
5182             return Roo.DomQuery.select(path, root)[0];
5183         },
5184
5185         /**
5186          * Selects the value of a node, optionally replacing null with the defaultValue.
5187          * @param {String} selector The selector/xpath query
5188          * @param {Node} root (optional) The start of the query (defaults to document).
5189          * @param {String} defaultValue
5190          */
5191         selectValue : function(path, root, defaultValue){
5192             path = path.replace(trimRe, "");
5193             if(!valueCache[path]){
5194                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5195             }
5196             var n = valueCache[path](root);
5197             n = n[0] ? n[0] : n;
5198             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5199             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5200         },
5201
5202         /**
5203          * Selects the value of a node, parsing integers and floats.
5204          * @param {String} selector The selector/xpath query
5205          * @param {Node} root (optional) The start of the query (defaults to document).
5206          * @param {Number} defaultValue
5207          * @return {Number}
5208          */
5209         selectNumber : function(path, root, defaultValue){
5210             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5211             return parseFloat(v);
5212         },
5213
5214         /**
5215          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5216          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5217          * @param {String} selector The simple selector to test
5218          * @return {Boolean}
5219          */
5220         is : function(el, ss){
5221             if(typeof el == "string"){
5222                 el = document.getElementById(el);
5223             }
5224             var isArray = (el instanceof Array);
5225             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5226             return isArray ? (result.length == el.length) : (result.length > 0);
5227         },
5228
5229         /**
5230          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5231          * @param {Array} el An array of elements to filter
5232          * @param {String} selector The simple selector to test
5233          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5234          * the selector instead of the ones that match
5235          * @return {Array}
5236          */
5237         filter : function(els, ss, nonMatches){
5238             ss = ss.replace(trimRe, "");
5239             if(!simpleCache[ss]){
5240                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5241             }
5242             var result = simpleCache[ss](els);
5243             return nonMatches ? quickDiff(result, els) : result;
5244         },
5245
5246         /**
5247          * Collection of matching regular expressions and code snippets.
5248          */
5249         matchers : [{
5250                 re: /^\.([\w-]+)/,
5251                 select: 'n = byClassName(n, null, " {1} ");'
5252             }, {
5253                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5254                 select: 'n = byPseudo(n, "{1}", "{2}");'
5255             },{
5256                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5257                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5258             }, {
5259                 re: /^#([\w-]+)/,
5260                 select: 'n = byId(n, null, "{1}");'
5261             },{
5262                 re: /^@([\w-]+)/,
5263                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5264             }
5265         ],
5266
5267         /**
5268          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5269          * 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;.
5270          */
5271         operators : {
5272             "=" : function(a, v){
5273                 return a == v;
5274             },
5275             "!=" : function(a, v){
5276                 return a != v;
5277             },
5278             "^=" : function(a, v){
5279                 return a && a.substr(0, v.length) == v;
5280             },
5281             "$=" : function(a, v){
5282                 return a && a.substr(a.length-v.length) == v;
5283             },
5284             "*=" : function(a, v){
5285                 return a && a.indexOf(v) !== -1;
5286             },
5287             "%=" : function(a, v){
5288                 return (a % v) == 0;
5289             },
5290             "|=" : function(a, v){
5291                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5292             },
5293             "~=" : function(a, v){
5294                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5295             }
5296         },
5297
5298         /**
5299          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5300          * and the argument (if any) supplied in the selector.
5301          */
5302         pseudos : {
5303             "first-child" : function(c){
5304                 var r = [], ri = -1, n;
5305                 for(var i = 0, ci; ci = n = c[i]; i++){
5306                     while((n = n.previousSibling) && n.nodeType != 1);
5307                     if(!n){
5308                         r[++ri] = ci;
5309                     }
5310                 }
5311                 return r;
5312             },
5313
5314             "last-child" : function(c){
5315                 var r = [], ri = -1, n;
5316                 for(var i = 0, ci; ci = n = c[i]; i++){
5317                     while((n = n.nextSibling) && n.nodeType != 1);
5318                     if(!n){
5319                         r[++ri] = ci;
5320                     }
5321                 }
5322                 return r;
5323             },
5324
5325             "nth-child" : function(c, a) {
5326                 var r = [], ri = -1;
5327                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5328                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5329                 for(var i = 0, n; n = c[i]; i++){
5330                     var pn = n.parentNode;
5331                     if (batch != pn._batch) {
5332                         var j = 0;
5333                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5334                             if(cn.nodeType == 1){
5335                                cn.nodeIndex = ++j;
5336                             }
5337                         }
5338                         pn._batch = batch;
5339                     }
5340                     if (f == 1) {
5341                         if (l == 0 || n.nodeIndex == l){
5342                             r[++ri] = n;
5343                         }
5344                     } else if ((n.nodeIndex + l) % f == 0){
5345                         r[++ri] = n;
5346                     }
5347                 }
5348
5349                 return r;
5350             },
5351
5352             "only-child" : function(c){
5353                 var r = [], ri = -1;;
5354                 for(var i = 0, ci; ci = c[i]; i++){
5355                     if(!prev(ci) && !next(ci)){
5356                         r[++ri] = ci;
5357                     }
5358                 }
5359                 return r;
5360             },
5361
5362             "empty" : function(c){
5363                 var r = [], ri = -1;
5364                 for(var i = 0, ci; ci = c[i]; i++){
5365                     var cns = ci.childNodes, j = 0, cn, empty = true;
5366                     while(cn = cns[j]){
5367                         ++j;
5368                         if(cn.nodeType == 1 || cn.nodeType == 3){
5369                             empty = false;
5370                             break;
5371                         }
5372                     }
5373                     if(empty){
5374                         r[++ri] = ci;
5375                     }
5376                 }
5377                 return r;
5378             },
5379
5380             "contains" : function(c, v){
5381                 var r = [], ri = -1;
5382                 for(var i = 0, ci; ci = c[i]; i++){
5383                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5384                         r[++ri] = ci;
5385                     }
5386                 }
5387                 return r;
5388             },
5389
5390             "nodeValue" : function(c, v){
5391                 var r = [], ri = -1;
5392                 for(var i = 0, ci; ci = c[i]; i++){
5393                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5394                         r[++ri] = ci;
5395                     }
5396                 }
5397                 return r;
5398             },
5399
5400             "checked" : function(c){
5401                 var r = [], ri = -1;
5402                 for(var i = 0, ci; ci = c[i]; i++){
5403                     if(ci.checked == true){
5404                         r[++ri] = ci;
5405                     }
5406                 }
5407                 return r;
5408             },
5409
5410             "not" : function(c, ss){
5411                 return Roo.DomQuery.filter(c, ss, true);
5412             },
5413
5414             "odd" : function(c){
5415                 return this["nth-child"](c, "odd");
5416             },
5417
5418             "even" : function(c){
5419                 return this["nth-child"](c, "even");
5420             },
5421
5422             "nth" : function(c, a){
5423                 return c[a-1] || [];
5424             },
5425
5426             "first" : function(c){
5427                 return c[0] || [];
5428             },
5429
5430             "last" : function(c){
5431                 return c[c.length-1] || [];
5432             },
5433
5434             "has" : function(c, ss){
5435                 var s = Roo.DomQuery.select;
5436                 var r = [], ri = -1;
5437                 for(var i = 0, ci; ci = c[i]; i++){
5438                     if(s(ss, ci).length > 0){
5439                         r[++ri] = ci;
5440                     }
5441                 }
5442                 return r;
5443             },
5444
5445             "next" : function(c, ss){
5446                 var is = Roo.DomQuery.is;
5447                 var r = [], ri = -1;
5448                 for(var i = 0, ci; ci = c[i]; i++){
5449                     var n = next(ci);
5450                     if(n && is(n, ss)){
5451                         r[++ri] = ci;
5452                     }
5453                 }
5454                 return r;
5455             },
5456
5457             "prev" : function(c, ss){
5458                 var is = Roo.DomQuery.is;
5459                 var r = [], ri = -1;
5460                 for(var i = 0, ci; ci = c[i]; i++){
5461                     var n = prev(ci);
5462                     if(n && is(n, ss)){
5463                         r[++ri] = ci;
5464                     }
5465                 }
5466                 return r;
5467             }
5468         }
5469     };
5470 }();
5471
5472 /**
5473  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5474  * @param {String} path The selector/xpath query
5475  * @param {Node} root (optional) The start of the query (defaults to document).
5476  * @return {Array}
5477  * @member Roo
5478  * @method query
5479  */
5480 Roo.query = Roo.DomQuery.select;
5481 /*
5482  * Based on:
5483  * Ext JS Library 1.1.1
5484  * Copyright(c) 2006-2007, Ext JS, LLC.
5485  *
5486  * Originally Released Under LGPL - original licence link has changed is not relivant.
5487  *
5488  * Fork - LGPL
5489  * <script type="text/javascript">
5490  */
5491
5492 /**
5493  * @class Roo.util.Observable
5494  * Base class that provides a common interface for publishing events. Subclasses are expected to
5495  * to have a property "events" with all the events defined.<br>
5496  * For example:
5497  * <pre><code>
5498  Employee = function(name){
5499     this.name = name;
5500     this.addEvents({
5501         "fired" : true,
5502         "quit" : true
5503     });
5504  }
5505  Roo.extend(Employee, Roo.util.Observable);
5506 </code></pre>
5507  * @param {Object} config properties to use (incuding events / listeners)
5508  */
5509
5510 Roo.util.Observable = function(cfg){
5511     
5512     cfg = cfg|| {};
5513     this.addEvents(cfg.events || {});
5514     if (cfg.events) {
5515         delete cfg.events; // make sure
5516     }
5517      
5518     Roo.apply(this, cfg);
5519     
5520     if(this.listeners){
5521         this.on(this.listeners);
5522         delete this.listeners;
5523     }
5524 };
5525 Roo.util.Observable.prototype = {
5526     /** 
5527  * @cfg {Object} listeners  list of events and functions to call for this object, 
5528  * For example :
5529  * <pre><code>
5530     listeners :  { 
5531        'click' : function(e) {
5532            ..... 
5533         } ,
5534         .... 
5535     } 
5536   </code></pre>
5537  */
5538     
5539     
5540     /**
5541      * Fires the specified event with the passed parameters (minus the event name).
5542      * @param {String} eventName
5543      * @param {Object...} args Variable number of parameters are passed to handlers
5544      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5545      */
5546     fireEvent : function(){
5547         var ce = this.events[arguments[0].toLowerCase()];
5548         if(typeof ce == "object"){
5549             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5550         }else{
5551             return true;
5552         }
5553     },
5554
5555     // private
5556     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5557
5558     /**
5559      * Appends an event handler to this component
5560      * @param {String}   eventName The type of event to listen for
5561      * @param {Function} handler The method the event invokes
5562      * @param {Object}   scope (optional) The scope in which to execute the handler
5563      * function. The handler function's "this" context.
5564      * @param {Object}   options (optional) An object containing handler configuration
5565      * properties. This may contain any of the following properties:<ul>
5566      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5567      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5568      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5569      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5570      * by the specified number of milliseconds. If the event fires again within that time, the original
5571      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5572      * </ul><br>
5573      * <p>
5574      * <b>Combining Options</b><br>
5575      * Using the options argument, it is possible to combine different types of listeners:<br>
5576      * <br>
5577      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5578                 <pre><code>
5579                 el.on('click', this.onClick, this, {
5580                         single: true,
5581                 delay: 100,
5582                 forumId: 4
5583                 });
5584                 </code></pre>
5585      * <p>
5586      * <b>Attaching multiple handlers in 1 call</b><br>
5587      * The method also allows for a single argument to be passed which is a config object containing properties
5588      * which specify multiple handlers.
5589      * <pre><code>
5590                 el.on({
5591                         'click': {
5592                         fn: this.onClick,
5593                         scope: this,
5594                         delay: 100
5595                 }, 
5596                 'mouseover': {
5597                         fn: this.onMouseOver,
5598                         scope: this
5599                 },
5600                 'mouseout': {
5601                         fn: this.onMouseOut,
5602                         scope: this
5603                 }
5604                 });
5605                 </code></pre>
5606      * <p>
5607      * Or a shorthand syntax which passes the same scope object to all handlers:
5608         <pre><code>
5609                 el.on({
5610                         'click': this.onClick,
5611                 'mouseover': this.onMouseOver,
5612                 'mouseout': this.onMouseOut,
5613                 scope: this
5614                 });
5615                 </code></pre>
5616      */
5617     addListener : function(eventName, fn, scope, o){
5618         if(typeof eventName == "object"){
5619             o = eventName;
5620             for(var e in o){
5621                 if(this.filterOptRe.test(e)){
5622                     continue;
5623                 }
5624                 if(typeof o[e] == "function"){
5625                     // shared options
5626                     this.addListener(e, o[e], o.scope,  o);
5627                 }else{
5628                     // individual options
5629                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5630                 }
5631             }
5632             return;
5633         }
5634         o = (!o || typeof o == "boolean") ? {} : o;
5635         eventName = eventName.toLowerCase();
5636         var ce = this.events[eventName] || true;
5637         if(typeof ce == "boolean"){
5638             ce = new Roo.util.Event(this, eventName);
5639             this.events[eventName] = ce;
5640         }
5641         ce.addListener(fn, scope, o);
5642     },
5643
5644     /**
5645      * Removes a listener
5646      * @param {String}   eventName     The type of event to listen for
5647      * @param {Function} handler        The handler to remove
5648      * @param {Object}   scope  (optional) The scope (this object) for the handler
5649      */
5650     removeListener : function(eventName, fn, scope){
5651         var ce = this.events[eventName.toLowerCase()];
5652         if(typeof ce == "object"){
5653             ce.removeListener(fn, scope);
5654         }
5655     },
5656
5657     /**
5658      * Removes all listeners for this object
5659      */
5660     purgeListeners : function(){
5661         for(var evt in this.events){
5662             if(typeof this.events[evt] == "object"){
5663                  this.events[evt].clearListeners();
5664             }
5665         }
5666     },
5667
5668     relayEvents : function(o, events){
5669         var createHandler = function(ename){
5670             return function(){
5671                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5672             };
5673         };
5674         for(var i = 0, len = events.length; i < len; i++){
5675             var ename = events[i];
5676             if(!this.events[ename]){ this.events[ename] = true; };
5677             o.on(ename, createHandler(ename), this);
5678         }
5679     },
5680
5681     /**
5682      * Used to define events on this Observable
5683      * @param {Object} object The object with the events defined
5684      */
5685     addEvents : function(o){
5686         if(!this.events){
5687             this.events = {};
5688         }
5689         Roo.applyIf(this.events, o);
5690     },
5691
5692     /**
5693      * Checks to see if this object has any listeners for a specified event
5694      * @param {String} eventName The name of the event to check for
5695      * @return {Boolean} True if the event is being listened for, else false
5696      */
5697     hasListener : function(eventName){
5698         var e = this.events[eventName];
5699         return typeof e == "object" && e.listeners.length > 0;
5700     }
5701 };
5702 /**
5703  * Appends an event handler to this element (shorthand for addListener)
5704  * @param {String}   eventName     The type of event to listen for
5705  * @param {Function} handler        The method the event invokes
5706  * @param {Object}   scope (optional) The scope in which to execute the handler
5707  * function. The handler function's "this" context.
5708  * @param {Object}   options  (optional)
5709  * @method
5710  */
5711 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5712 /**
5713  * Removes a listener (shorthand for removeListener)
5714  * @param {String}   eventName     The type of event to listen for
5715  * @param {Function} handler        The handler to remove
5716  * @param {Object}   scope  (optional) The scope (this object) for the handler
5717  * @method
5718  */
5719 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5720
5721 /**
5722  * Starts capture on the specified Observable. All events will be passed
5723  * to the supplied function with the event name + standard signature of the event
5724  * <b>before</b> the event is fired. If the supplied function returns false,
5725  * the event will not fire.
5726  * @param {Observable} o The Observable to capture
5727  * @param {Function} fn The function to call
5728  * @param {Object} scope (optional) The scope (this object) for the fn
5729  * @static
5730  */
5731 Roo.util.Observable.capture = function(o, fn, scope){
5732     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5733 };
5734
5735 /**
5736  * Removes <b>all</b> added captures from the Observable.
5737  * @param {Observable} o The Observable to release
5738  * @static
5739  */
5740 Roo.util.Observable.releaseCapture = function(o){
5741     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5742 };
5743
5744 (function(){
5745
5746     var createBuffered = function(h, o, scope){
5747         var task = new Roo.util.DelayedTask();
5748         return function(){
5749             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5750         };
5751     };
5752
5753     var createSingle = function(h, e, fn, scope){
5754         return function(){
5755             e.removeListener(fn, scope);
5756             return h.apply(scope, arguments);
5757         };
5758     };
5759
5760     var createDelayed = function(h, o, scope){
5761         return function(){
5762             var args = Array.prototype.slice.call(arguments, 0);
5763             setTimeout(function(){
5764                 h.apply(scope, args);
5765             }, o.delay || 10);
5766         };
5767     };
5768
5769     Roo.util.Event = function(obj, name){
5770         this.name = name;
5771         this.obj = obj;
5772         this.listeners = [];
5773     };
5774
5775     Roo.util.Event.prototype = {
5776         addListener : function(fn, scope, options){
5777             var o = options || {};
5778             scope = scope || this.obj;
5779             if(!this.isListening(fn, scope)){
5780                 var l = {fn: fn, scope: scope, options: o};
5781                 var h = fn;
5782                 if(o.delay){
5783                     h = createDelayed(h, o, scope);
5784                 }
5785                 if(o.single){
5786                     h = createSingle(h, this, fn, scope);
5787                 }
5788                 if(o.buffer){
5789                     h = createBuffered(h, o, scope);
5790                 }
5791                 l.fireFn = h;
5792                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5793                     this.listeners.push(l);
5794                 }else{
5795                     this.listeners = this.listeners.slice(0);
5796                     this.listeners.push(l);
5797                 }
5798             }
5799         },
5800
5801         findListener : function(fn, scope){
5802             scope = scope || this.obj;
5803             var ls = this.listeners;
5804             for(var i = 0, len = ls.length; i < len; i++){
5805                 var l = ls[i];
5806                 if(l.fn == fn && l.scope == scope){
5807                     return i;
5808                 }
5809             }
5810             return -1;
5811         },
5812
5813         isListening : function(fn, scope){
5814             return this.findListener(fn, scope) != -1;
5815         },
5816
5817         removeListener : function(fn, scope){
5818             var index;
5819             if((index = this.findListener(fn, scope)) != -1){
5820                 if(!this.firing){
5821                     this.listeners.splice(index, 1);
5822                 }else{
5823                     this.listeners = this.listeners.slice(0);
5824                     this.listeners.splice(index, 1);
5825                 }
5826                 return true;
5827             }
5828             return false;
5829         },
5830
5831         clearListeners : function(){
5832             this.listeners = [];
5833         },
5834
5835         fire : function(){
5836             var ls = this.listeners, scope, len = ls.length;
5837             if(len > 0){
5838                 this.firing = true;
5839                 var args = Array.prototype.slice.call(arguments, 0);
5840                 for(var i = 0; i < len; i++){
5841                     var l = ls[i];
5842                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
5843                         this.firing = false;
5844                         return false;
5845                     }
5846                 }
5847                 this.firing = false;
5848             }
5849             return true;
5850         }
5851     };
5852 })();/*
5853  * Based on:
5854  * Ext JS Library 1.1.1
5855  * Copyright(c) 2006-2007, Ext JS, LLC.
5856  *
5857  * Originally Released Under LGPL - original licence link has changed is not relivant.
5858  *
5859  * Fork - LGPL
5860  * <script type="text/javascript">
5861  */
5862
5863 /**
5864  * @class Roo.EventManager
5865  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
5866  * several useful events directly.
5867  * See {@link Roo.EventObject} for more details on normalized event objects.
5868  * @singleton
5869  */
5870 Roo.EventManager = function(){
5871     var docReadyEvent, docReadyProcId, docReadyState = false;
5872     var resizeEvent, resizeTask, textEvent, textSize;
5873     var E = Roo.lib.Event;
5874     var D = Roo.lib.Dom;
5875
5876
5877     var fireDocReady = function(){
5878         if(!docReadyState){
5879             docReadyState = true;
5880             Roo.isReady = true;
5881             if(docReadyProcId){
5882                 clearInterval(docReadyProcId);
5883             }
5884             if(Roo.isGecko || Roo.isOpera) {
5885                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
5886             }
5887             if(Roo.isIE){
5888                 var defer = document.getElementById("ie-deferred-loader");
5889                 if(defer){
5890                     defer.onreadystatechange = null;
5891                     defer.parentNode.removeChild(defer);
5892                 }
5893             }
5894             if(docReadyEvent){
5895                 docReadyEvent.fire();
5896                 docReadyEvent.clearListeners();
5897             }
5898         }
5899     };
5900     
5901     var initDocReady = function(){
5902         docReadyEvent = new Roo.util.Event();
5903         if(Roo.isGecko || Roo.isOpera) {
5904             document.addEventListener("DOMContentLoaded", fireDocReady, false);
5905         }else if(Roo.isIE){
5906             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
5907             var defer = document.getElementById("ie-deferred-loader");
5908             defer.onreadystatechange = function(){
5909                 if(this.readyState == "complete"){
5910                     fireDocReady();
5911                 }
5912             };
5913         }else if(Roo.isSafari){ 
5914             docReadyProcId = setInterval(function(){
5915                 var rs = document.readyState;
5916                 if(rs == "complete") {
5917                     fireDocReady();     
5918                  }
5919             }, 10);
5920         }
5921         // no matter what, make sure it fires on load
5922         E.on(window, "load", fireDocReady);
5923     };
5924
5925     var createBuffered = function(h, o){
5926         var task = new Roo.util.DelayedTask(h);
5927         return function(e){
5928             // create new event object impl so new events don't wipe out properties
5929             e = new Roo.EventObjectImpl(e);
5930             task.delay(o.buffer, h, null, [e]);
5931         };
5932     };
5933
5934     var createSingle = function(h, el, ename, fn){
5935         return function(e){
5936             Roo.EventManager.removeListener(el, ename, fn);
5937             h(e);
5938         };
5939     };
5940
5941     var createDelayed = function(h, o){
5942         return function(e){
5943             // create new event object impl so new events don't wipe out properties
5944             e = new Roo.EventObjectImpl(e);
5945             setTimeout(function(){
5946                 h(e);
5947             }, o.delay || 10);
5948         };
5949     };
5950
5951     var listen = function(element, ename, opt, fn, scope){
5952         var o = (!opt || typeof opt == "boolean") ? {} : opt;
5953         fn = fn || o.fn; scope = scope || o.scope;
5954         var el = Roo.getDom(element);
5955         if(!el){
5956             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
5957         }
5958         var h = function(e){
5959             e = Roo.EventObject.setEvent(e);
5960             var t;
5961             if(o.delegate){
5962                 t = e.getTarget(o.delegate, el);
5963                 if(!t){
5964                     return;
5965                 }
5966             }else{
5967                 t = e.target;
5968             }
5969             if(o.stopEvent === true){
5970                 e.stopEvent();
5971             }
5972             if(o.preventDefault === true){
5973                e.preventDefault();
5974             }
5975             if(o.stopPropagation === true){
5976                 e.stopPropagation();
5977             }
5978
5979             if(o.normalized === false){
5980                 e = e.browserEvent;
5981             }
5982
5983             fn.call(scope || el, e, t, o);
5984         };
5985         if(o.delay){
5986             h = createDelayed(h, o);
5987         }
5988         if(o.single){
5989             h = createSingle(h, el, ename, fn);
5990         }
5991         if(o.buffer){
5992             h = createBuffered(h, o);
5993         }
5994         fn._handlers = fn._handlers || [];
5995         fn._handlers.push([Roo.id(el), ename, h]);
5996
5997         E.on(el, ename, h);
5998         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
5999             el.addEventListener("DOMMouseScroll", h, false);
6000             E.on(window, 'unload', function(){
6001                 el.removeEventListener("DOMMouseScroll", h, false);
6002             });
6003         }
6004         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6005             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6006         }
6007         return h;
6008     };
6009
6010     var stopListening = function(el, ename, fn){
6011         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6012         if(hds){
6013             for(var i = 0, len = hds.length; i < len; i++){
6014                 var h = hds[i];
6015                 if(h[0] == id && h[1] == ename){
6016                     hd = h[2];
6017                     hds.splice(i, 1);
6018                     break;
6019                 }
6020             }
6021         }
6022         E.un(el, ename, hd);
6023         el = Roo.getDom(el);
6024         if(ename == "mousewheel" && el.addEventListener){
6025             el.removeEventListener("DOMMouseScroll", hd, false);
6026         }
6027         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6028             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6029         }
6030     };
6031
6032     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6033     
6034     var pub = {
6035         
6036         
6037         /** 
6038          * Fix for doc tools
6039          * @scope Roo.EventManager
6040          */
6041         
6042         
6043         /** 
6044          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6045          * object with a Roo.EventObject
6046          * @param {Function} fn        The method the event invokes
6047          * @param {Object}   scope    An object that becomes the scope of the handler
6048          * @param {boolean}  override If true, the obj passed in becomes
6049          *                             the execution scope of the listener
6050          * @return {Function} The wrapped function
6051          * @deprecated
6052          */
6053         wrap : function(fn, scope, override){
6054             return function(e){
6055                 Roo.EventObject.setEvent(e);
6056                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6057             };
6058         },
6059         
6060         /**
6061      * Appends an event handler to an element (shorthand for addListener)
6062      * @param {String/HTMLElement}   element        The html element or id to assign the
6063      * @param {String}   eventName The type of event to listen for
6064      * @param {Function} handler The method the event invokes
6065      * @param {Object}   scope (optional) The scope in which to execute the handler
6066      * function. The handler function's "this" context.
6067      * @param {Object}   options (optional) An object containing handler configuration
6068      * properties. This may contain any of the following properties:<ul>
6069      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6070      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6071      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6072      * <li>preventDefault {Boolean} True to prevent the default action</li>
6073      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6074      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6075      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6076      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6077      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6078      * by the specified number of milliseconds. If the event fires again within that time, the original
6079      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6080      * </ul><br>
6081      * <p>
6082      * <b>Combining Options</b><br>
6083      * Using the options argument, it is possible to combine different types of listeners:<br>
6084      * <br>
6085      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6086      * Code:<pre><code>
6087 el.on('click', this.onClick, this, {
6088     single: true,
6089     delay: 100,
6090     stopEvent : true,
6091     forumId: 4
6092 });</code></pre>
6093      * <p>
6094      * <b>Attaching multiple handlers in 1 call</b><br>
6095       * The method also allows for a single argument to be passed which is a config object containing properties
6096      * which specify multiple handlers.
6097      * <p>
6098      * Code:<pre><code>
6099 el.on({
6100     'click' : {
6101         fn: this.onClick
6102         scope: this,
6103         delay: 100
6104     },
6105     'mouseover' : {
6106         fn: this.onMouseOver
6107         scope: this
6108     },
6109     'mouseout' : {
6110         fn: this.onMouseOut
6111         scope: this
6112     }
6113 });</code></pre>
6114      * <p>
6115      * Or a shorthand syntax:<br>
6116      * Code:<pre><code>
6117 el.on({
6118     'click' : this.onClick,
6119     'mouseover' : this.onMouseOver,
6120     'mouseout' : this.onMouseOut
6121     scope: this
6122 });</code></pre>
6123      */
6124         addListener : function(element, eventName, fn, scope, options){
6125             if(typeof eventName == "object"){
6126                 var o = eventName;
6127                 for(var e in o){
6128                     if(propRe.test(e)){
6129                         continue;
6130                     }
6131                     if(typeof o[e] == "function"){
6132                         // shared options
6133                         listen(element, e, o, o[e], o.scope);
6134                     }else{
6135                         // individual options
6136                         listen(element, e, o[e]);
6137                     }
6138                 }
6139                 return;
6140             }
6141             return listen(element, eventName, options, fn, scope);
6142         },
6143         
6144         /**
6145          * Removes an event handler
6146          *
6147          * @param {String/HTMLElement}   element        The id or html element to remove the 
6148          *                             event from
6149          * @param {String}   eventName     The type of event
6150          * @param {Function} fn
6151          * @return {Boolean} True if a listener was actually removed
6152          */
6153         removeListener : function(element, eventName, fn){
6154             return stopListening(element, eventName, fn);
6155         },
6156         
6157         /**
6158          * Fires when the document is ready (before onload and before images are loaded). Can be 
6159          * accessed shorthanded Roo.onReady().
6160          * @param {Function} fn        The method the event invokes
6161          * @param {Object}   scope    An  object that becomes the scope of the handler
6162          * @param {boolean}  options
6163          */
6164         onDocumentReady : function(fn, scope, options){
6165             if(docReadyState){ // if it already fired
6166                 docReadyEvent.addListener(fn, scope, options);
6167                 docReadyEvent.fire();
6168                 docReadyEvent.clearListeners();
6169                 return;
6170             }
6171             if(!docReadyEvent){
6172                 initDocReady();
6173             }
6174             docReadyEvent.addListener(fn, scope, options);
6175         },
6176         
6177         /**
6178          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6179          * @param {Function} fn        The method the event invokes
6180          * @param {Object}   scope    An object that becomes the scope of the handler
6181          * @param {boolean}  options
6182          */
6183         onWindowResize : function(fn, scope, options){
6184             if(!resizeEvent){
6185                 resizeEvent = new Roo.util.Event();
6186                 resizeTask = new Roo.util.DelayedTask(function(){
6187                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6188                 });
6189                 E.on(window, "resize", function(){
6190                     if(Roo.isIE){
6191                         resizeTask.delay(50);
6192                     }else{
6193                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6194                     }
6195                 });
6196             }
6197             resizeEvent.addListener(fn, scope, options);
6198         },
6199
6200         /**
6201          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6202          * @param {Function} fn        The method the event invokes
6203          * @param {Object}   scope    An object that becomes the scope of the handler
6204          * @param {boolean}  options
6205          */
6206         onTextResize : function(fn, scope, options){
6207             if(!textEvent){
6208                 textEvent = new Roo.util.Event();
6209                 var textEl = new Roo.Element(document.createElement('div'));
6210                 textEl.dom.className = 'x-text-resize';
6211                 textEl.dom.innerHTML = 'X';
6212                 textEl.appendTo(document.body);
6213                 textSize = textEl.dom.offsetHeight;
6214                 setInterval(function(){
6215                     if(textEl.dom.offsetHeight != textSize){
6216                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6217                     }
6218                 }, this.textResizeInterval);
6219             }
6220             textEvent.addListener(fn, scope, options);
6221         },
6222
6223         /**
6224          * Removes the passed window resize listener.
6225          * @param {Function} fn        The method the event invokes
6226          * @param {Object}   scope    The scope of handler
6227          */
6228         removeResizeListener : function(fn, scope){
6229             if(resizeEvent){
6230                 resizeEvent.removeListener(fn, scope);
6231             }
6232         },
6233
6234         // private
6235         fireResize : function(){
6236             if(resizeEvent){
6237                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6238             }   
6239         },
6240         /**
6241          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6242          */
6243         ieDeferSrc : false,
6244         /**
6245          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6246          */
6247         textResizeInterval : 50
6248     };
6249     
6250     /**
6251      * Fix for doc tools
6252      * @scopeAlias pub=Roo.EventManager
6253      */
6254     
6255      /**
6256      * Appends an event handler to an element (shorthand for addListener)
6257      * @param {String/HTMLElement}   element        The html element or id to assign the
6258      * @param {String}   eventName The type of event to listen for
6259      * @param {Function} handler The method the event invokes
6260      * @param {Object}   scope (optional) The scope in which to execute the handler
6261      * function. The handler function's "this" context.
6262      * @param {Object}   options (optional) An object containing handler configuration
6263      * properties. This may contain any of the following properties:<ul>
6264      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6265      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6266      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6267      * <li>preventDefault {Boolean} True to prevent the default action</li>
6268      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6269      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6270      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6271      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6272      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6273      * by the specified number of milliseconds. If the event fires again within that time, the original
6274      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6275      * </ul><br>
6276      * <p>
6277      * <b>Combining Options</b><br>
6278      * Using the options argument, it is possible to combine different types of listeners:<br>
6279      * <br>
6280      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6281      * Code:<pre><code>
6282 el.on('click', this.onClick, this, {
6283     single: true,
6284     delay: 100,
6285     stopEvent : true,
6286     forumId: 4
6287 });</code></pre>
6288      * <p>
6289      * <b>Attaching multiple handlers in 1 call</b><br>
6290       * The method also allows for a single argument to be passed which is a config object containing properties
6291      * which specify multiple handlers.
6292      * <p>
6293      * Code:<pre><code>
6294 el.on({
6295     'click' : {
6296         fn: this.onClick
6297         scope: this,
6298         delay: 100
6299     },
6300     'mouseover' : {
6301         fn: this.onMouseOver
6302         scope: this
6303     },
6304     'mouseout' : {
6305         fn: this.onMouseOut
6306         scope: this
6307     }
6308 });</code></pre>
6309      * <p>
6310      * Or a shorthand syntax:<br>
6311      * Code:<pre><code>
6312 el.on({
6313     'click' : this.onClick,
6314     'mouseover' : this.onMouseOver,
6315     'mouseout' : this.onMouseOut
6316     scope: this
6317 });</code></pre>
6318      */
6319     pub.on = pub.addListener;
6320     pub.un = pub.removeListener;
6321
6322     pub.stoppedMouseDownEvent = new Roo.util.Event();
6323     return pub;
6324 }();
6325 /**
6326   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6327   * @param {Function} fn        The method the event invokes
6328   * @param {Object}   scope    An  object that becomes the scope of the handler
6329   * @param {boolean}  override If true, the obj passed in becomes
6330   *                             the execution scope of the listener
6331   * @member Roo
6332   * @method onReady
6333  */
6334 Roo.onReady = Roo.EventManager.onDocumentReady;
6335
6336 Roo.onReady(function(){
6337     var bd = Roo.get(document.body);
6338     if(!bd){ return; }
6339
6340     var cls = [
6341             Roo.isIE ? "roo-ie"
6342             : Roo.isGecko ? "roo-gecko"
6343             : Roo.isOpera ? "roo-opera"
6344             : Roo.isSafari ? "roo-safari" : ""];
6345
6346     if(Roo.isMac){
6347         cls.push("roo-mac");
6348     }
6349     if(Roo.isLinux){
6350         cls.push("roo-linux");
6351     }
6352     if(Roo.isBorderBox){
6353         cls.push('roo-border-box');
6354     }
6355     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6356         var p = bd.dom.parentNode;
6357         if(p){
6358             p.className += ' roo-strict';
6359         }
6360     }
6361     bd.addClass(cls.join(' '));
6362 });
6363
6364 /**
6365  * @class Roo.EventObject
6366  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6367  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6368  * Example:
6369  * <pre><code>
6370  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6371     e.preventDefault();
6372     var target = e.getTarget();
6373     ...
6374  }
6375  var myDiv = Roo.get("myDiv");
6376  myDiv.on("click", handleClick);
6377  //or
6378  Roo.EventManager.on("myDiv", 'click', handleClick);
6379  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6380  </code></pre>
6381  * @singleton
6382  */
6383 Roo.EventObject = function(){
6384     
6385     var E = Roo.lib.Event;
6386     
6387     // safari keypress events for special keys return bad keycodes
6388     var safariKeys = {
6389         63234 : 37, // left
6390         63235 : 39, // right
6391         63232 : 38, // up
6392         63233 : 40, // down
6393         63276 : 33, // page up
6394         63277 : 34, // page down
6395         63272 : 46, // delete
6396         63273 : 36, // home
6397         63275 : 35  // end
6398     };
6399
6400     // normalize button clicks
6401     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6402                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6403
6404     Roo.EventObjectImpl = function(e){
6405         if(e){
6406             this.setEvent(e.browserEvent || e);
6407         }
6408     };
6409     Roo.EventObjectImpl.prototype = {
6410         /**
6411          * Used to fix doc tools.
6412          * @scope Roo.EventObject.prototype
6413          */
6414             
6415
6416         
6417         
6418         /** The normal browser event */
6419         browserEvent : null,
6420         /** The button pressed in a mouse event */
6421         button : -1,
6422         /** True if the shift key was down during the event */
6423         shiftKey : false,
6424         /** True if the control key was down during the event */
6425         ctrlKey : false,
6426         /** True if the alt key was down during the event */
6427         altKey : false,
6428
6429         /** Key constant 
6430         * @type Number */
6431         BACKSPACE : 8,
6432         /** Key constant 
6433         * @type Number */
6434         TAB : 9,
6435         /** Key constant 
6436         * @type Number */
6437         RETURN : 13,
6438         /** Key constant 
6439         * @type Number */
6440         ENTER : 13,
6441         /** Key constant 
6442         * @type Number */
6443         SHIFT : 16,
6444         /** Key constant 
6445         * @type Number */
6446         CONTROL : 17,
6447         /** Key constant 
6448         * @type Number */
6449         ESC : 27,
6450         /** Key constant 
6451         * @type Number */
6452         SPACE : 32,
6453         /** Key constant 
6454         * @type Number */
6455         PAGEUP : 33,
6456         /** Key constant 
6457         * @type Number */
6458         PAGEDOWN : 34,
6459         /** Key constant 
6460         * @type Number */
6461         END : 35,
6462         /** Key constant 
6463         * @type Number */
6464         HOME : 36,
6465         /** Key constant 
6466         * @type Number */
6467         LEFT : 37,
6468         /** Key constant 
6469         * @type Number */
6470         UP : 38,
6471         /** Key constant 
6472         * @type Number */
6473         RIGHT : 39,
6474         /** Key constant 
6475         * @type Number */
6476         DOWN : 40,
6477         /** Key constant 
6478         * @type Number */
6479         DELETE : 46,
6480         /** Key constant 
6481         * @type Number */
6482         F5 : 116,
6483
6484            /** @private */
6485         setEvent : function(e){
6486             if(e == this || (e && e.browserEvent)){ // already wrapped
6487                 return e;
6488             }
6489             this.browserEvent = e;
6490             if(e){
6491                 // normalize buttons
6492                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6493                 if(e.type == 'click' && this.button == -1){
6494                     this.button = 0;
6495                 }
6496                 this.type = e.type;
6497                 this.shiftKey = e.shiftKey;
6498                 // mac metaKey behaves like ctrlKey
6499                 this.ctrlKey = e.ctrlKey || e.metaKey;
6500                 this.altKey = e.altKey;
6501                 // in getKey these will be normalized for the mac
6502                 this.keyCode = e.keyCode;
6503                 // keyup warnings on firefox.
6504                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6505                 // cache the target for the delayed and or buffered events
6506                 this.target = E.getTarget(e);
6507                 // same for XY
6508                 this.xy = E.getXY(e);
6509             }else{
6510                 this.button = -1;
6511                 this.shiftKey = false;
6512                 this.ctrlKey = false;
6513                 this.altKey = false;
6514                 this.keyCode = 0;
6515                 this.charCode =0;
6516                 this.target = null;
6517                 this.xy = [0, 0];
6518             }
6519             return this;
6520         },
6521
6522         /**
6523          * Stop the event (preventDefault and stopPropagation)
6524          */
6525         stopEvent : function(){
6526             if(this.browserEvent){
6527                 if(this.browserEvent.type == 'mousedown'){
6528                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6529                 }
6530                 E.stopEvent(this.browserEvent);
6531             }
6532         },
6533
6534         /**
6535          * Prevents the browsers default handling of the event.
6536          */
6537         preventDefault : function(){
6538             if(this.browserEvent){
6539                 E.preventDefault(this.browserEvent);
6540             }
6541         },
6542
6543         /** @private */
6544         isNavKeyPress : function(){
6545             var k = this.keyCode;
6546             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6547             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6548         },
6549
6550         isSpecialKey : function(){
6551             var k = this.keyCode;
6552             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6553             (k == 16) || (k == 17) ||
6554             (k >= 18 && k <= 20) ||
6555             (k >= 33 && k <= 35) ||
6556             (k >= 36 && k <= 39) ||
6557             (k >= 44 && k <= 45);
6558         },
6559         /**
6560          * Cancels bubbling of the event.
6561          */
6562         stopPropagation : function(){
6563             if(this.browserEvent){
6564                 if(this.type == 'mousedown'){
6565                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6566                 }
6567                 E.stopPropagation(this.browserEvent);
6568             }
6569         },
6570
6571         /**
6572          * Gets the key code for the event.
6573          * @return {Number}
6574          */
6575         getCharCode : function(){
6576             return this.charCode || this.keyCode;
6577         },
6578
6579         /**
6580          * Returns a normalized keyCode for the event.
6581          * @return {Number} The key code
6582          */
6583         getKey : function(){
6584             var k = this.keyCode || this.charCode;
6585             return Roo.isSafari ? (safariKeys[k] || k) : k;
6586         },
6587
6588         /**
6589          * Gets the x coordinate of the event.
6590          * @return {Number}
6591          */
6592         getPageX : function(){
6593             return this.xy[0];
6594         },
6595
6596         /**
6597          * Gets the y coordinate of the event.
6598          * @return {Number}
6599          */
6600         getPageY : function(){
6601             return this.xy[1];
6602         },
6603
6604         /**
6605          * Gets the time of the event.
6606          * @return {Number}
6607          */
6608         getTime : function(){
6609             if(this.browserEvent){
6610                 return E.getTime(this.browserEvent);
6611             }
6612             return null;
6613         },
6614
6615         /**
6616          * Gets the page coordinates of the event.
6617          * @return {Array} The xy values like [x, y]
6618          */
6619         getXY : function(){
6620             return this.xy;
6621         },
6622
6623         /**
6624          * Gets the target for the event.
6625          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6626          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6627                 search as a number or element (defaults to 10 || document.body)
6628          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6629          * @return {HTMLelement}
6630          */
6631         getTarget : function(selector, maxDepth, returnEl){
6632             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6633         },
6634         /**
6635          * Gets the related target.
6636          * @return {HTMLElement}
6637          */
6638         getRelatedTarget : function(){
6639             if(this.browserEvent){
6640                 return E.getRelatedTarget(this.browserEvent);
6641             }
6642             return null;
6643         },
6644
6645         /**
6646          * Normalizes mouse wheel delta across browsers
6647          * @return {Number} The delta
6648          */
6649         getWheelDelta : function(){
6650             var e = this.browserEvent;
6651             var delta = 0;
6652             if(e.wheelDelta){ /* IE/Opera. */
6653                 delta = e.wheelDelta/120;
6654             }else if(e.detail){ /* Mozilla case. */
6655                 delta = -e.detail/3;
6656             }
6657             return delta;
6658         },
6659
6660         /**
6661          * Returns true if the control, meta, shift or alt key was pressed during this event.
6662          * @return {Boolean}
6663          */
6664         hasModifier : function(){
6665             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6666         },
6667
6668         /**
6669          * Returns true if the target of this event equals el or is a child of el
6670          * @param {String/HTMLElement/Element} el
6671          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6672          * @return {Boolean}
6673          */
6674         within : function(el, related){
6675             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6676             return t && Roo.fly(el).contains(t);
6677         },
6678
6679         getPoint : function(){
6680             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6681         }
6682     };
6683
6684     return new Roo.EventObjectImpl();
6685 }();
6686             
6687     /*
6688  * Based on:
6689  * Ext JS Library 1.1.1
6690  * Copyright(c) 2006-2007, Ext JS, LLC.
6691  *
6692  * Originally Released Under LGPL - original licence link has changed is not relivant.
6693  *
6694  * Fork - LGPL
6695  * <script type="text/javascript">
6696  */
6697
6698  
6699 // was in Composite Element!??!?!
6700  
6701 (function(){
6702     var D = Roo.lib.Dom;
6703     var E = Roo.lib.Event;
6704     var A = Roo.lib.Anim;
6705
6706     // local style camelizing for speed
6707     var propCache = {};
6708     var camelRe = /(-[a-z])/gi;
6709     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6710     var view = document.defaultView;
6711
6712 /**
6713  * @class Roo.Element
6714  * Represents an Element in the DOM.<br><br>
6715  * Usage:<br>
6716 <pre><code>
6717 var el = Roo.get("my-div");
6718
6719 // or with getEl
6720 var el = getEl("my-div");
6721
6722 // or with a DOM element
6723 var el = Roo.get(myDivElement);
6724 </code></pre>
6725  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6726  * each call instead of constructing a new one.<br><br>
6727  * <b>Animations</b><br />
6728  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6729  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6730 <pre>
6731 Option    Default   Description
6732 --------- --------  ---------------------------------------------
6733 duration  .35       The duration of the animation in seconds
6734 easing    easeOut   The YUI easing method
6735 callback  none      A function to execute when the anim completes
6736 scope     this      The scope (this) of the callback function
6737 </pre>
6738 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6739 * manipulate the animation. Here's an example:
6740 <pre><code>
6741 var el = Roo.get("my-div");
6742
6743 // no animation
6744 el.setWidth(100);
6745
6746 // default animation
6747 el.setWidth(100, true);
6748
6749 // animation with some options set
6750 el.setWidth(100, {
6751     duration: 1,
6752     callback: this.foo,
6753     scope: this
6754 });
6755
6756 // using the "anim" property to get the Anim object
6757 var opt = {
6758     duration: 1,
6759     callback: this.foo,
6760     scope: this
6761 };
6762 el.setWidth(100, opt);
6763 ...
6764 if(opt.anim.isAnimated()){
6765     opt.anim.stop();
6766 }
6767 </code></pre>
6768 * <b> Composite (Collections of) Elements</b><br />
6769  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6770  * @constructor Create a new Element directly.
6771  * @param {String/HTMLElement} element
6772  * @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).
6773  */
6774     Roo.Element = function(element, forceNew){
6775         var dom = typeof element == "string" ?
6776                 document.getElementById(element) : element;
6777         if(!dom){ // invalid id/element
6778             return null;
6779         }
6780         var id = dom.id;
6781         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6782             return Roo.Element.cache[id];
6783         }
6784
6785         /**
6786          * The DOM element
6787          * @type HTMLElement
6788          */
6789         this.dom = dom;
6790
6791         /**
6792          * The DOM element ID
6793          * @type String
6794          */
6795         this.id = id || Roo.id(dom);
6796     };
6797
6798     var El = Roo.Element;
6799
6800     El.prototype = {
6801         /**
6802          * The element's default display mode  (defaults to "")
6803          * @type String
6804          */
6805         originalDisplay : "",
6806
6807         visibilityMode : 1,
6808         /**
6809          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6810          * @type String
6811          */
6812         defaultUnit : "px",
6813         /**
6814          * Sets the element's visibility mode. When setVisible() is called it
6815          * will use this to determine whether to set the visibility or the display property.
6816          * @param visMode Element.VISIBILITY or Element.DISPLAY
6817          * @return {Roo.Element} this
6818          */
6819         setVisibilityMode : function(visMode){
6820             this.visibilityMode = visMode;
6821             return this;
6822         },
6823         /**
6824          * Convenience method for setVisibilityMode(Element.DISPLAY)
6825          * @param {String} display (optional) What to set display to when visible
6826          * @return {Roo.Element} this
6827          */
6828         enableDisplayMode : function(display){
6829             this.setVisibilityMode(El.DISPLAY);
6830             if(typeof display != "undefined") this.originalDisplay = display;
6831             return this;
6832         },
6833
6834         /**
6835          * 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)
6836          * @param {String} selector The simple selector to test
6837          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6838                 search as a number or element (defaults to 10 || document.body)
6839          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6840          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6841          */
6842         findParent : function(simpleSelector, maxDepth, returnEl){
6843             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
6844             maxDepth = maxDepth || 50;
6845             if(typeof maxDepth != "number"){
6846                 stopEl = Roo.getDom(maxDepth);
6847                 maxDepth = 10;
6848             }
6849             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
6850                 if(dq.is(p, simpleSelector)){
6851                     return returnEl ? Roo.get(p) : p;
6852                 }
6853                 depth++;
6854                 p = p.parentNode;
6855             }
6856             return null;
6857         },
6858
6859
6860         /**
6861          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
6862          * @param {String} selector The simple selector to test
6863          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6864                 search as a number or element (defaults to 10 || document.body)
6865          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6866          * @return {HTMLElement} The matching DOM node (or null if no match was found)
6867          */
6868         findParentNode : function(simpleSelector, maxDepth, returnEl){
6869             var p = Roo.fly(this.dom.parentNode, '_internal');
6870             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
6871         },
6872
6873         /**
6874          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
6875          * This is a shortcut for findParentNode() that always returns an Roo.Element.
6876          * @param {String} selector The simple selector to test
6877          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6878                 search as a number or element (defaults to 10 || document.body)
6879          * @return {Roo.Element} The matching DOM node (or null if no match was found)
6880          */
6881         up : function(simpleSelector, maxDepth){
6882             return this.findParentNode(simpleSelector, maxDepth, true);
6883         },
6884
6885
6886
6887         /**
6888          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
6889          * @param {String} selector The simple selector to test
6890          * @return {Boolean} True if this element matches the selector, else false
6891          */
6892         is : function(simpleSelector){
6893             return Roo.DomQuery.is(this.dom, simpleSelector);
6894         },
6895
6896         /**
6897          * Perform animation on this element.
6898          * @param {Object} args The YUI animation control args
6899          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
6900          * @param {Function} onComplete (optional) Function to call when animation completes
6901          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
6902          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
6903          * @return {Roo.Element} this
6904          */
6905         animate : function(args, duration, onComplete, easing, animType){
6906             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
6907             return this;
6908         },
6909
6910         /*
6911          * @private Internal animation call
6912          */
6913         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
6914             animType = animType || 'run';
6915             opt = opt || {};
6916             var anim = Roo.lib.Anim[animType](
6917                 this.dom, args,
6918                 (opt.duration || defaultDur) || .35,
6919                 (opt.easing || defaultEase) || 'easeOut',
6920                 function(){
6921                     Roo.callback(cb, this);
6922                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
6923                 },
6924                 this
6925             );
6926             opt.anim = anim;
6927             return anim;
6928         },
6929
6930         // private legacy anim prep
6931         preanim : function(a, i){
6932             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
6933         },
6934
6935         /**
6936          * Removes worthless text nodes
6937          * @param {Boolean} forceReclean (optional) By default the element
6938          * keeps track if it has been cleaned already so
6939          * you can call this over and over. However, if you update the element and
6940          * need to force a reclean, you can pass true.
6941          */
6942         clean : function(forceReclean){
6943             if(this.isCleaned && forceReclean !== true){
6944                 return this;
6945             }
6946             var ns = /\S/;
6947             var d = this.dom, n = d.firstChild, ni = -1;
6948             while(n){
6949                 var nx = n.nextSibling;
6950                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
6951                     d.removeChild(n);
6952                 }else{
6953                     n.nodeIndex = ++ni;
6954                 }
6955                 n = nx;
6956             }
6957             this.isCleaned = true;
6958             return this;
6959         },
6960
6961         // private
6962         calcOffsetsTo : function(el){
6963             el = Roo.get(el);
6964             var d = el.dom;
6965             var restorePos = false;
6966             if(el.getStyle('position') == 'static'){
6967                 el.position('relative');
6968                 restorePos = true;
6969             }
6970             var x = 0, y =0;
6971             var op = this.dom;
6972             while(op && op != d && op.tagName != 'HTML'){
6973                 x+= op.offsetLeft;
6974                 y+= op.offsetTop;
6975                 op = op.offsetParent;
6976             }
6977             if(restorePos){
6978                 el.position('static');
6979             }
6980             return [x, y];
6981         },
6982
6983         /**
6984          * Scrolls this element into view within the passed container.
6985          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
6986          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
6987          * @return {Roo.Element} this
6988          */
6989         scrollIntoView : function(container, hscroll){
6990             var c = Roo.getDom(container) || document.body;
6991             var el = this.dom;
6992
6993             var o = this.calcOffsetsTo(c),
6994                 l = o[0],
6995                 t = o[1],
6996                 b = t+el.offsetHeight,
6997                 r = l+el.offsetWidth;
6998
6999             var ch = c.clientHeight;
7000             var ct = parseInt(c.scrollTop, 10);
7001             var cl = parseInt(c.scrollLeft, 10);
7002             var cb = ct + ch;
7003             var cr = cl + c.clientWidth;
7004
7005             if(t < ct){
7006                 c.scrollTop = t;
7007             }else if(b > cb){
7008                 c.scrollTop = b-ch;
7009             }
7010
7011             if(hscroll !== false){
7012                 if(l < cl){
7013                     c.scrollLeft = l;
7014                 }else if(r > cr){
7015                     c.scrollLeft = r-c.clientWidth;
7016                 }
7017             }
7018             return this;
7019         },
7020
7021         // private
7022         scrollChildIntoView : function(child, hscroll){
7023             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7024         },
7025
7026         /**
7027          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7028          * the new height may not be available immediately.
7029          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7030          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7031          * @param {Function} onComplete (optional) Function to call when animation completes
7032          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7033          * @return {Roo.Element} this
7034          */
7035         autoHeight : function(animate, duration, onComplete, easing){
7036             var oldHeight = this.getHeight();
7037             this.clip();
7038             this.setHeight(1); // force clipping
7039             setTimeout(function(){
7040                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7041                 if(!animate){
7042                     this.setHeight(height);
7043                     this.unclip();
7044                     if(typeof onComplete == "function"){
7045                         onComplete();
7046                     }
7047                 }else{
7048                     this.setHeight(oldHeight); // restore original height
7049                     this.setHeight(height, animate, duration, function(){
7050                         this.unclip();
7051                         if(typeof onComplete == "function") onComplete();
7052                     }.createDelegate(this), easing);
7053                 }
7054             }.createDelegate(this), 0);
7055             return this;
7056         },
7057
7058         /**
7059          * Returns true if this element is an ancestor of the passed element
7060          * @param {HTMLElement/String} el The element to check
7061          * @return {Boolean} True if this element is an ancestor of el, else false
7062          */
7063         contains : function(el){
7064             if(!el){return false;}
7065             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7066         },
7067
7068         /**
7069          * Checks whether the element is currently visible using both visibility and display properties.
7070          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7071          * @return {Boolean} True if the element is currently visible, else false
7072          */
7073         isVisible : function(deep) {
7074             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7075             if(deep !== true || !vis){
7076                 return vis;
7077             }
7078             var p = this.dom.parentNode;
7079             while(p && p.tagName.toLowerCase() != "body"){
7080                 if(!Roo.fly(p, '_isVisible').isVisible()){
7081                     return false;
7082                 }
7083                 p = p.parentNode;
7084             }
7085             return true;
7086         },
7087
7088         /**
7089          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7090          * @param {String} selector The CSS selector
7091          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7092          * @return {CompositeElement/CompositeElementLite} The composite element
7093          */
7094         select : function(selector, unique){
7095             return El.select(selector, unique, this.dom);
7096         },
7097
7098         /**
7099          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7100          * @param {String} selector The CSS selector
7101          * @return {Array} An array of the matched nodes
7102          */
7103         query : function(selector, unique){
7104             return Roo.DomQuery.select(selector, this.dom);
7105         },
7106
7107         /**
7108          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7109          * @param {String} selector The CSS selector
7110          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7111          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7112          */
7113         child : function(selector, returnDom){
7114             var n = Roo.DomQuery.selectNode(selector, this.dom);
7115             return returnDom ? n : Roo.get(n);
7116         },
7117
7118         /**
7119          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7120          * @param {String} selector The CSS selector
7121          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7122          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7123          */
7124         down : function(selector, returnDom){
7125             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7126             return returnDom ? n : Roo.get(n);
7127         },
7128
7129         /**
7130          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7131          * @param {String} group The group the DD object is member of
7132          * @param {Object} config The DD config object
7133          * @param {Object} overrides An object containing methods to override/implement on the DD object
7134          * @return {Roo.dd.DD} The DD object
7135          */
7136         initDD : function(group, config, overrides){
7137             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7138             return Roo.apply(dd, overrides);
7139         },
7140
7141         /**
7142          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7143          * @param {String} group The group the DDProxy object is member of
7144          * @param {Object} config The DDProxy config object
7145          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7146          * @return {Roo.dd.DDProxy} The DDProxy object
7147          */
7148         initDDProxy : function(group, config, overrides){
7149             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7150             return Roo.apply(dd, overrides);
7151         },
7152
7153         /**
7154          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7155          * @param {String} group The group the DDTarget object is member of
7156          * @param {Object} config The DDTarget config object
7157          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7158          * @return {Roo.dd.DDTarget} The DDTarget object
7159          */
7160         initDDTarget : function(group, config, overrides){
7161             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7162             return Roo.apply(dd, overrides);
7163         },
7164
7165         /**
7166          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7167          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7168          * @param {Boolean} visible Whether the element is visible
7169          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7170          * @return {Roo.Element} this
7171          */
7172          setVisible : function(visible, animate){
7173             if(!animate || !A){
7174                 if(this.visibilityMode == El.DISPLAY){
7175                     this.setDisplayed(visible);
7176                 }else{
7177                     this.fixDisplay();
7178                     this.dom.style.visibility = visible ? "visible" : "hidden";
7179                 }
7180             }else{
7181                 // closure for composites
7182                 var dom = this.dom;
7183                 var visMode = this.visibilityMode;
7184                 if(visible){
7185                     this.setOpacity(.01);
7186                     this.setVisible(true);
7187                 }
7188                 this.anim({opacity: { to: (visible?1:0) }},
7189                       this.preanim(arguments, 1),
7190                       null, .35, 'easeIn', function(){
7191                          if(!visible){
7192                              if(visMode == El.DISPLAY){
7193                                  dom.style.display = "none";
7194                              }else{
7195                                  dom.style.visibility = "hidden";
7196                              }
7197                              Roo.get(dom).setOpacity(1);
7198                          }
7199                      });
7200             }
7201             return this;
7202         },
7203
7204         /**
7205          * Returns true if display is not "none"
7206          * @return {Boolean}
7207          */
7208         isDisplayed : function() {
7209             return this.getStyle("display") != "none";
7210         },
7211
7212         /**
7213          * Toggles the element's visibility or display, depending on visibility mode.
7214          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7215          * @return {Roo.Element} this
7216          */
7217         toggle : function(animate){
7218             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7219             return this;
7220         },
7221
7222         /**
7223          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7224          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7225          * @return {Roo.Element} this
7226          */
7227         setDisplayed : function(value) {
7228             if(typeof value == "boolean"){
7229                value = value ? this.originalDisplay : "none";
7230             }
7231             this.setStyle("display", value);
7232             return this;
7233         },
7234
7235         /**
7236          * Tries to focus the element. Any exceptions are caught and ignored.
7237          * @return {Roo.Element} this
7238          */
7239         focus : function() {
7240             try{
7241                 this.dom.focus();
7242             }catch(e){}
7243             return this;
7244         },
7245
7246         /**
7247          * Tries to blur the element. Any exceptions are caught and ignored.
7248          * @return {Roo.Element} this
7249          */
7250         blur : function() {
7251             try{
7252                 this.dom.blur();
7253             }catch(e){}
7254             return this;
7255         },
7256
7257         /**
7258          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7259          * @param {String/Array} className The CSS class to add, or an array of classes
7260          * @return {Roo.Element} this
7261          */
7262         addClass : function(className){
7263             if(className instanceof Array){
7264                 for(var i = 0, len = className.length; i < len; i++) {
7265                     this.addClass(className[i]);
7266                 }
7267             }else{
7268                 if(className && !this.hasClass(className)){
7269                     this.dom.className = this.dom.className + " " + className;
7270                 }
7271             }
7272             return this;
7273         },
7274
7275         /**
7276          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7277          * @param {String/Array} className The CSS class to add, or an array of classes
7278          * @return {Roo.Element} this
7279          */
7280         radioClass : function(className){
7281             var siblings = this.dom.parentNode.childNodes;
7282             for(var i = 0; i < siblings.length; i++) {
7283                 var s = siblings[i];
7284                 if(s.nodeType == 1){
7285                     Roo.get(s).removeClass(className);
7286                 }
7287             }
7288             this.addClass(className);
7289             return this;
7290         },
7291
7292         /**
7293          * Removes one or more CSS classes from the element.
7294          * @param {String/Array} className The CSS class to remove, or an array of classes
7295          * @return {Roo.Element} this
7296          */
7297         removeClass : function(className){
7298             if(!className || !this.dom.className){
7299                 return this;
7300             }
7301             if(className instanceof Array){
7302                 for(var i = 0, len = className.length; i < len; i++) {
7303                     this.removeClass(className[i]);
7304                 }
7305             }else{
7306                 if(this.hasClass(className)){
7307                     var re = this.classReCache[className];
7308                     if (!re) {
7309                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7310                        this.classReCache[className] = re;
7311                     }
7312                     this.dom.className =
7313                         this.dom.className.replace(re, " ");
7314                 }
7315             }
7316             return this;
7317         },
7318
7319         // private
7320         classReCache: {},
7321
7322         /**
7323          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7324          * @param {String} className The CSS class to toggle
7325          * @return {Roo.Element} this
7326          */
7327         toggleClass : function(className){
7328             if(this.hasClass(className)){
7329                 this.removeClass(className);
7330             }else{
7331                 this.addClass(className);
7332             }
7333             return this;
7334         },
7335
7336         /**
7337          * Checks if the specified CSS class exists on this element's DOM node.
7338          * @param {String} className The CSS class to check for
7339          * @return {Boolean} True if the class exists, else false
7340          */
7341         hasClass : function(className){
7342             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7343         },
7344
7345         /**
7346          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7347          * @param {String} oldClassName The CSS class to replace
7348          * @param {String} newClassName The replacement CSS class
7349          * @return {Roo.Element} this
7350          */
7351         replaceClass : function(oldClassName, newClassName){
7352             this.removeClass(oldClassName);
7353             this.addClass(newClassName);
7354             return this;
7355         },
7356
7357         /**
7358          * Returns an object with properties matching the styles requested.
7359          * For example, el.getStyles('color', 'font-size', 'width') might return
7360          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7361          * @param {String} style1 A style name
7362          * @param {String} style2 A style name
7363          * @param {String} etc.
7364          * @return {Object} The style object
7365          */
7366         getStyles : function(){
7367             var a = arguments, len = a.length, r = {};
7368             for(var i = 0; i < len; i++){
7369                 r[a[i]] = this.getStyle(a[i]);
7370             }
7371             return r;
7372         },
7373
7374         /**
7375          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7376          * @param {String} property The style property whose value is returned.
7377          * @return {String} The current value of the style property for this element.
7378          */
7379         getStyle : function(){
7380             return view && view.getComputedStyle ?
7381                 function(prop){
7382                     var el = this.dom, v, cs, camel;
7383                     if(prop == 'float'){
7384                         prop = "cssFloat";
7385                     }
7386                     if(el.style && (v = el.style[prop])){
7387                         return v;
7388                     }
7389                     if(cs = view.getComputedStyle(el, "")){
7390                         if(!(camel = propCache[prop])){
7391                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7392                         }
7393                         return cs[camel];
7394                     }
7395                     return null;
7396                 } :
7397                 function(prop){
7398                     var el = this.dom, v, cs, camel;
7399                     if(prop == 'opacity'){
7400                         if(typeof el.style.filter == 'string'){
7401                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7402                             if(m){
7403                                 var fv = parseFloat(m[1]);
7404                                 if(!isNaN(fv)){
7405                                     return fv ? fv / 100 : 0;
7406                                 }
7407                             }
7408                         }
7409                         return 1;
7410                     }else if(prop == 'float'){
7411                         prop = "styleFloat";
7412                     }
7413                     if(!(camel = propCache[prop])){
7414                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7415                     }
7416                     if(v = el.style[camel]){
7417                         return v;
7418                     }
7419                     if(cs = el.currentStyle){
7420                         return cs[camel];
7421                     }
7422                     return null;
7423                 };
7424         }(),
7425
7426         /**
7427          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7428          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7429          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7430          * @return {Roo.Element} this
7431          */
7432         setStyle : function(prop, value){
7433             if(typeof prop == "string"){
7434                 
7435                 if (prop == 'float') {
7436                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7437                     return this;
7438                 }
7439                 
7440                 var camel;
7441                 if(!(camel = propCache[prop])){
7442                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7443                 }
7444                 
7445                 if(camel == 'opacity') {
7446                     this.setOpacity(value);
7447                 }else{
7448                     this.dom.style[camel] = value;
7449                 }
7450             }else{
7451                 for(var style in prop){
7452                     if(typeof prop[style] != "function"){
7453                        this.setStyle(style, prop[style]);
7454                     }
7455                 }
7456             }
7457             return this;
7458         },
7459
7460         /**
7461          * More flexible version of {@link #setStyle} for setting style properties.
7462          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7463          * a function which returns such a specification.
7464          * @return {Roo.Element} this
7465          */
7466         applyStyles : function(style){
7467             Roo.DomHelper.applyStyles(this.dom, style);
7468             return this;
7469         },
7470
7471         /**
7472           * 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).
7473           * @return {Number} The X position of the element
7474           */
7475         getX : function(){
7476             return D.getX(this.dom);
7477         },
7478
7479         /**
7480           * 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).
7481           * @return {Number} The Y position of the element
7482           */
7483         getY : function(){
7484             return D.getY(this.dom);
7485         },
7486
7487         /**
7488           * 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).
7489           * @return {Array} The XY position of the element
7490           */
7491         getXY : function(){
7492             return D.getXY(this.dom);
7493         },
7494
7495         /**
7496          * 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).
7497          * @param {Number} The X position of the element
7498          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7499          * @return {Roo.Element} this
7500          */
7501         setX : function(x, animate){
7502             if(!animate || !A){
7503                 D.setX(this.dom, x);
7504             }else{
7505                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7506             }
7507             return this;
7508         },
7509
7510         /**
7511          * 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).
7512          * @param {Number} The Y position of the element
7513          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7514          * @return {Roo.Element} this
7515          */
7516         setY : function(y, animate){
7517             if(!animate || !A){
7518                 D.setY(this.dom, y);
7519             }else{
7520                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7521             }
7522             return this;
7523         },
7524
7525         /**
7526          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7527          * @param {String} left The left CSS property value
7528          * @return {Roo.Element} this
7529          */
7530         setLeft : function(left){
7531             this.setStyle("left", this.addUnits(left));
7532             return this;
7533         },
7534
7535         /**
7536          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7537          * @param {String} top The top CSS property value
7538          * @return {Roo.Element} this
7539          */
7540         setTop : function(top){
7541             this.setStyle("top", this.addUnits(top));
7542             return this;
7543         },
7544
7545         /**
7546          * Sets the element's CSS right style.
7547          * @param {String} right The right CSS property value
7548          * @return {Roo.Element} this
7549          */
7550         setRight : function(right){
7551             this.setStyle("right", this.addUnits(right));
7552             return this;
7553         },
7554
7555         /**
7556          * Sets the element's CSS bottom style.
7557          * @param {String} bottom The bottom CSS property value
7558          * @return {Roo.Element} this
7559          */
7560         setBottom : function(bottom){
7561             this.setStyle("bottom", this.addUnits(bottom));
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 {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7569          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7570          * @return {Roo.Element} this
7571          */
7572         setXY : function(pos, animate){
7573             if(!animate || !A){
7574                 D.setXY(this.dom, pos);
7575             }else{
7576                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7577             }
7578             return this;
7579         },
7580
7581         /**
7582          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7583          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7584          * @param {Number} x X value for new position (coordinates are page-based)
7585          * @param {Number} y Y value for new position (coordinates are page-based)
7586          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7587          * @return {Roo.Element} this
7588          */
7589         setLocation : function(x, y, animate){
7590             this.setXY([x, y], this.preanim(arguments, 2));
7591             return this;
7592         },
7593
7594         /**
7595          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7596          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7597          * @param {Number} x X value for new position (coordinates are page-based)
7598          * @param {Number} y Y value for new position (coordinates are page-based)
7599          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7600          * @return {Roo.Element} this
7601          */
7602         moveTo : function(x, y, animate){
7603             this.setXY([x, y], this.preanim(arguments, 2));
7604             return this;
7605         },
7606
7607         /**
7608          * Returns the region of the given element.
7609          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7610          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7611          */
7612         getRegion : function(){
7613             return D.getRegion(this.dom);
7614         },
7615
7616         /**
7617          * Returns the offset height of the element
7618          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7619          * @return {Number} The element's height
7620          */
7621         getHeight : function(contentHeight){
7622             var h = this.dom.offsetHeight || 0;
7623             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7624         },
7625
7626         /**
7627          * Returns the offset width of the element
7628          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7629          * @return {Number} The element's width
7630          */
7631         getWidth : function(contentWidth){
7632             var w = this.dom.offsetWidth || 0;
7633             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7634         },
7635
7636         /**
7637          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7638          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7639          * if a height has not been set using CSS.
7640          * @return {Number}
7641          */
7642         getComputedHeight : function(){
7643             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7644             if(!h){
7645                 h = parseInt(this.getStyle('height'), 10) || 0;
7646                 if(!this.isBorderBox()){
7647                     h += this.getFrameWidth('tb');
7648                 }
7649             }
7650             return h;
7651         },
7652
7653         /**
7654          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7655          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7656          * if a width has not been set using CSS.
7657          * @return {Number}
7658          */
7659         getComputedWidth : function(){
7660             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7661             if(!w){
7662                 w = parseInt(this.getStyle('width'), 10) || 0;
7663                 if(!this.isBorderBox()){
7664                     w += this.getFrameWidth('lr');
7665                 }
7666             }
7667             return w;
7668         },
7669
7670         /**
7671          * Returns the size of the element.
7672          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7673          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7674          */
7675         getSize : function(contentSize){
7676             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7677         },
7678
7679         /**
7680          * Returns the width and height of the viewport.
7681          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7682          */
7683         getViewSize : function(){
7684             var d = this.dom, doc = document, aw = 0, ah = 0;
7685             if(d == doc || d == doc.body){
7686                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7687             }else{
7688                 return {
7689                     width : d.clientWidth,
7690                     height: d.clientHeight
7691                 };
7692             }
7693         },
7694
7695         /**
7696          * Returns the value of the "value" attribute
7697          * @param {Boolean} asNumber true to parse the value as a number
7698          * @return {String/Number}
7699          */
7700         getValue : function(asNumber){
7701             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7702         },
7703
7704         // private
7705         adjustWidth : function(width){
7706             if(typeof width == "number"){
7707                 if(this.autoBoxAdjust && !this.isBorderBox()){
7708                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7709                 }
7710                 if(width < 0){
7711                     width = 0;
7712                 }
7713             }
7714             return width;
7715         },
7716
7717         // private
7718         adjustHeight : function(height){
7719             if(typeof height == "number"){
7720                if(this.autoBoxAdjust && !this.isBorderBox()){
7721                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7722                }
7723                if(height < 0){
7724                    height = 0;
7725                }
7726             }
7727             return height;
7728         },
7729
7730         /**
7731          * Set the width of the element
7732          * @param {Number} width The new width
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         setWidth : function(width, animate){
7737             width = this.adjustWidth(width);
7738             if(!animate || !A){
7739                 this.dom.style.width = this.addUnits(width);
7740             }else{
7741                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7742             }
7743             return this;
7744         },
7745
7746         /**
7747          * Set the height of the element
7748          * @param {Number} height The new height
7749          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7750          * @return {Roo.Element} this
7751          */
7752          setHeight : function(height, animate){
7753             height = this.adjustHeight(height);
7754             if(!animate || !A){
7755                 this.dom.style.height = this.addUnits(height);
7756             }else{
7757                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7758             }
7759             return this;
7760         },
7761
7762         /**
7763          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7764          * @param {Number} width The new width
7765          * @param {Number} height The new height
7766          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7767          * @return {Roo.Element} this
7768          */
7769          setSize : function(width, height, animate){
7770             if(typeof width == "object"){ // in case of object from getSize()
7771                 height = width.height; width = width.width;
7772             }
7773             width = this.adjustWidth(width); height = this.adjustHeight(height);
7774             if(!animate || !A){
7775                 this.dom.style.width = this.addUnits(width);
7776                 this.dom.style.height = this.addUnits(height);
7777             }else{
7778                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7779             }
7780             return this;
7781         },
7782
7783         /**
7784          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7785          * @param {Number} x X value for new position (coordinates are page-based)
7786          * @param {Number} y Y value for new position (coordinates are page-based)
7787          * @param {Number} width The new width
7788          * @param {Number} height The new height
7789          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7790          * @return {Roo.Element} this
7791          */
7792         setBounds : function(x, y, width, height, animate){
7793             if(!animate || !A){
7794                 this.setSize(width, height);
7795                 this.setLocation(x, y);
7796             }else{
7797                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7798                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7799                               this.preanim(arguments, 4), 'motion');
7800             }
7801             return this;
7802         },
7803
7804         /**
7805          * 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.
7806          * @param {Roo.lib.Region} region The region to fill
7807          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7808          * @return {Roo.Element} this
7809          */
7810         setRegion : function(region, animate){
7811             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7812             return this;
7813         },
7814
7815         /**
7816          * Appends an event handler
7817          *
7818          * @param {String}   eventName     The type of event to append
7819          * @param {Function} fn        The method the event invokes
7820          * @param {Object} scope       (optional) The scope (this object) of the fn
7821          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7822          */
7823         addListener : function(eventName, fn, scope, options){
7824             if (this.dom) {
7825                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
7826             }
7827         },
7828
7829         /**
7830          * Removes an event handler from this element
7831          * @param {String} eventName the type of event to remove
7832          * @param {Function} fn the method the event invokes
7833          * @return {Roo.Element} this
7834          */
7835         removeListener : function(eventName, fn){
7836             Roo.EventManager.removeListener(this.dom,  eventName, fn);
7837             return this;
7838         },
7839
7840         /**
7841          * Removes all previous added listeners from this element
7842          * @return {Roo.Element} this
7843          */
7844         removeAllListeners : function(){
7845             E.purgeElement(this.dom);
7846             return this;
7847         },
7848
7849         relayEvent : function(eventName, observable){
7850             this.on(eventName, function(e){
7851                 observable.fireEvent(eventName, e);
7852             });
7853         },
7854
7855         /**
7856          * Set the opacity of the element
7857          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
7858          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7859          * @return {Roo.Element} this
7860          */
7861          setOpacity : function(opacity, animate){
7862             if(!animate || !A){
7863                 var s = this.dom.style;
7864                 if(Roo.isIE){
7865                     s.zoom = 1;
7866                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
7867                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
7868                 }else{
7869                     s.opacity = opacity;
7870                 }
7871             }else{
7872                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
7873             }
7874             return this;
7875         },
7876
7877         /**
7878          * Gets the left X coordinate
7879          * @param {Boolean} local True to get the local css position instead of page coordinate
7880          * @return {Number}
7881          */
7882         getLeft : function(local){
7883             if(!local){
7884                 return this.getX();
7885             }else{
7886                 return parseInt(this.getStyle("left"), 10) || 0;
7887             }
7888         },
7889
7890         /**
7891          * Gets the right X coordinate of the element (element X position + element width)
7892          * @param {Boolean} local True to get the local css position instead of page coordinate
7893          * @return {Number}
7894          */
7895         getRight : function(local){
7896             if(!local){
7897                 return this.getX() + this.getWidth();
7898             }else{
7899                 return (this.getLeft(true) + this.getWidth()) || 0;
7900             }
7901         },
7902
7903         /**
7904          * Gets the top Y coordinate
7905          * @param {Boolean} local True to get the local css position instead of page coordinate
7906          * @return {Number}
7907          */
7908         getTop : function(local) {
7909             if(!local){
7910                 return this.getY();
7911             }else{
7912                 return parseInt(this.getStyle("top"), 10) || 0;
7913             }
7914         },
7915
7916         /**
7917          * Gets the bottom Y coordinate of the element (element Y position + element height)
7918          * @param {Boolean} local True to get the local css position instead of page coordinate
7919          * @return {Number}
7920          */
7921         getBottom : function(local){
7922             if(!local){
7923                 return this.getY() + this.getHeight();
7924             }else{
7925                 return (this.getTop(true) + this.getHeight()) || 0;
7926             }
7927         },
7928
7929         /**
7930         * Initializes positioning on this element. If a desired position is not passed, it will make the
7931         * the element positioned relative IF it is not already positioned.
7932         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
7933         * @param {Number} zIndex (optional) The zIndex to apply
7934         * @param {Number} x (optional) Set the page X position
7935         * @param {Number} y (optional) Set the page Y position
7936         */
7937         position : function(pos, zIndex, x, y){
7938             if(!pos){
7939                if(this.getStyle('position') == 'static'){
7940                    this.setStyle('position', 'relative');
7941                }
7942             }else{
7943                 this.setStyle("position", pos);
7944             }
7945             if(zIndex){
7946                 this.setStyle("z-index", zIndex);
7947             }
7948             if(x !== undefined && y !== undefined){
7949                 this.setXY([x, y]);
7950             }else if(x !== undefined){
7951                 this.setX(x);
7952             }else if(y !== undefined){
7953                 this.setY(y);
7954             }
7955         },
7956
7957         /**
7958         * Clear positioning back to the default when the document was loaded
7959         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
7960         * @return {Roo.Element} this
7961          */
7962         clearPositioning : function(value){
7963             value = value ||'';
7964             this.setStyle({
7965                 "left": value,
7966                 "right": value,
7967                 "top": value,
7968                 "bottom": value,
7969                 "z-index": "",
7970                 "position" : "static"
7971             });
7972             return this;
7973         },
7974
7975         /**
7976         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
7977         * snapshot before performing an update and then restoring the element.
7978         * @return {Object}
7979         */
7980         getPositioning : function(){
7981             var l = this.getStyle("left");
7982             var t = this.getStyle("top");
7983             return {
7984                 "position" : this.getStyle("position"),
7985                 "left" : l,
7986                 "right" : l ? "" : this.getStyle("right"),
7987                 "top" : t,
7988                 "bottom" : t ? "" : this.getStyle("bottom"),
7989                 "z-index" : this.getStyle("z-index")
7990             };
7991         },
7992
7993         /**
7994          * Gets the width of the border(s) for the specified side(s)
7995          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
7996          * passing lr would get the border (l)eft width + the border (r)ight width.
7997          * @return {Number} The width of the sides passed added together
7998          */
7999         getBorderWidth : function(side){
8000             return this.addStyles(side, El.borders);
8001         },
8002
8003         /**
8004          * Gets the width of the padding(s) for the specified side(s)
8005          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8006          * passing lr would get the padding (l)eft + the padding (r)ight.
8007          * @return {Number} The padding of the sides passed added together
8008          */
8009         getPadding : function(side){
8010             return this.addStyles(side, El.paddings);
8011         },
8012
8013         /**
8014         * Set positioning with an object returned by getPositioning().
8015         * @param {Object} posCfg
8016         * @return {Roo.Element} this
8017          */
8018         setPositioning : function(pc){
8019             this.applyStyles(pc);
8020             if(pc.right == "auto"){
8021                 this.dom.style.right = "";
8022             }
8023             if(pc.bottom == "auto"){
8024                 this.dom.style.bottom = "";
8025             }
8026             return this;
8027         },
8028
8029         // private
8030         fixDisplay : function(){
8031             if(this.getStyle("display") == "none"){
8032                 this.setStyle("visibility", "hidden");
8033                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8034                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8035                     this.setStyle("display", "block");
8036                 }
8037             }
8038         },
8039
8040         /**
8041          * Quick set left and top adding default units
8042          * @param {String} left The left CSS property value
8043          * @param {String} top The top CSS property value
8044          * @return {Roo.Element} this
8045          */
8046          setLeftTop : function(left, top){
8047             this.dom.style.left = this.addUnits(left);
8048             this.dom.style.top = this.addUnits(top);
8049             return this;
8050         },
8051
8052         /**
8053          * Move this element relative to its current position.
8054          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8055          * @param {Number} distance How far to move the element in pixels
8056          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8057          * @return {Roo.Element} this
8058          */
8059          move : function(direction, distance, animate){
8060             var xy = this.getXY();
8061             direction = direction.toLowerCase();
8062             switch(direction){
8063                 case "l":
8064                 case "left":
8065                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8066                     break;
8067                case "r":
8068                case "right":
8069                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8070                     break;
8071                case "t":
8072                case "top":
8073                case "up":
8074                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8075                     break;
8076                case "b":
8077                case "bottom":
8078                case "down":
8079                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8080                     break;
8081             }
8082             return this;
8083         },
8084
8085         /**
8086          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8087          * @return {Roo.Element} this
8088          */
8089         clip : function(){
8090             if(!this.isClipped){
8091                this.isClipped = true;
8092                this.originalClip = {
8093                    "o": this.getStyle("overflow"),
8094                    "x": this.getStyle("overflow-x"),
8095                    "y": this.getStyle("overflow-y")
8096                };
8097                this.setStyle("overflow", "hidden");
8098                this.setStyle("overflow-x", "hidden");
8099                this.setStyle("overflow-y", "hidden");
8100             }
8101             return this;
8102         },
8103
8104         /**
8105          *  Return clipping (overflow) to original clipping before clip() was called
8106          * @return {Roo.Element} this
8107          */
8108         unclip : function(){
8109             if(this.isClipped){
8110                 this.isClipped = false;
8111                 var o = this.originalClip;
8112                 if(o.o){this.setStyle("overflow", o.o);}
8113                 if(o.x){this.setStyle("overflow-x", o.x);}
8114                 if(o.y){this.setStyle("overflow-y", o.y);}
8115             }
8116             return this;
8117         },
8118
8119
8120         /**
8121          * Gets the x,y coordinates specified by the anchor position on the element.
8122          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8123          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8124          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8125          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8126          * @return {Array} [x, y] An array containing the element's x and y coordinates
8127          */
8128         getAnchorXY : function(anchor, local, s){
8129             //Passing a different size is useful for pre-calculating anchors,
8130             //especially for anchored animations that change the el size.
8131
8132             var w, h, vp = false;
8133             if(!s){
8134                 var d = this.dom;
8135                 if(d == document.body || d == document){
8136                     vp = true;
8137                     w = D.getViewWidth(); h = D.getViewHeight();
8138                 }else{
8139                     w = this.getWidth(); h = this.getHeight();
8140                 }
8141             }else{
8142                 w = s.width;  h = s.height;
8143             }
8144             var x = 0, y = 0, r = Math.round;
8145             switch((anchor || "tl").toLowerCase()){
8146                 case "c":
8147                     x = r(w*.5);
8148                     y = r(h*.5);
8149                 break;
8150                 case "t":
8151                     x = r(w*.5);
8152                     y = 0;
8153                 break;
8154                 case "l":
8155                     x = 0;
8156                     y = r(h*.5);
8157                 break;
8158                 case "r":
8159                     x = w;
8160                     y = r(h*.5);
8161                 break;
8162                 case "b":
8163                     x = r(w*.5);
8164                     y = h;
8165                 break;
8166                 case "tl":
8167                     x = 0;
8168                     y = 0;
8169                 break;
8170                 case "bl":
8171                     x = 0;
8172                     y = h;
8173                 break;
8174                 case "br":
8175                     x = w;
8176                     y = h;
8177                 break;
8178                 case "tr":
8179                     x = w;
8180                     y = 0;
8181                 break;
8182             }
8183             if(local === true){
8184                 return [x, y];
8185             }
8186             if(vp){
8187                 var sc = this.getScroll();
8188                 return [x + sc.left, y + sc.top];
8189             }
8190             //Add the element's offset xy
8191             var o = this.getXY();
8192             return [x+o[0], y+o[1]];
8193         },
8194
8195         /**
8196          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8197          * supported position values.
8198          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8199          * @param {String} position The position to align to.
8200          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8201          * @return {Array} [x, y]
8202          */
8203         getAlignToXY : function(el, p, o){
8204             el = Roo.get(el);
8205             var d = this.dom;
8206             if(!el.dom){
8207                 throw "Element.alignTo with an element that doesn't exist";
8208             }
8209             var c = false; //constrain to viewport
8210             var p1 = "", p2 = "";
8211             o = o || [0,0];
8212
8213             if(!p){
8214                 p = "tl-bl";
8215             }else if(p == "?"){
8216                 p = "tl-bl?";
8217             }else if(p.indexOf("-") == -1){
8218                 p = "tl-" + p;
8219             }
8220             p = p.toLowerCase();
8221             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8222             if(!m){
8223                throw "Element.alignTo with an invalid alignment " + p;
8224             }
8225             p1 = m[1]; p2 = m[2]; c = !!m[3];
8226
8227             //Subtract the aligned el's internal xy from the target's offset xy
8228             //plus custom offset to get the aligned el's new offset xy
8229             var a1 = this.getAnchorXY(p1, true);
8230             var a2 = el.getAnchorXY(p2, false);
8231             var x = a2[0] - a1[0] + o[0];
8232             var y = a2[1] - a1[1] + o[1];
8233             if(c){
8234                 //constrain the aligned el to viewport if necessary
8235                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8236                 // 5px of margin for ie
8237                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8238
8239                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8240                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8241                 //otherwise swap the aligned el to the opposite border of the target.
8242                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8243                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8244                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8245                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8246
8247                var doc = document;
8248                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8249                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8250
8251                if((x+w) > dw + scrollX){
8252                     x = swapX ? r.left-w : dw+scrollX-w;
8253                 }
8254                if(x < scrollX){
8255                    x = swapX ? r.right : scrollX;
8256                }
8257                if((y+h) > dh + scrollY){
8258                     y = swapY ? r.top-h : dh+scrollY-h;
8259                 }
8260                if (y < scrollY){
8261                    y = swapY ? r.bottom : scrollY;
8262                }
8263             }
8264             return [x,y];
8265         },
8266
8267         // private
8268         getConstrainToXY : function(){
8269             var os = {top:0, left:0, bottom:0, right: 0};
8270
8271             return function(el, local, offsets, proposedXY){
8272                 el = Roo.get(el);
8273                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8274
8275                 var vw, vh, vx = 0, vy = 0;
8276                 if(el.dom == document.body || el.dom == document){
8277                     vw = Roo.lib.Dom.getViewWidth();
8278                     vh = Roo.lib.Dom.getViewHeight();
8279                 }else{
8280                     vw = el.dom.clientWidth;
8281                     vh = el.dom.clientHeight;
8282                     if(!local){
8283                         var vxy = el.getXY();
8284                         vx = vxy[0];
8285                         vy = vxy[1];
8286                     }
8287                 }
8288
8289                 var s = el.getScroll();
8290
8291                 vx += offsets.left + s.left;
8292                 vy += offsets.top + s.top;
8293
8294                 vw -= offsets.right;
8295                 vh -= offsets.bottom;
8296
8297                 var vr = vx+vw;
8298                 var vb = vy+vh;
8299
8300                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8301                 var x = xy[0], y = xy[1];
8302                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8303
8304                 // only move it if it needs it
8305                 var moved = false;
8306
8307                 // first validate right/bottom
8308                 if((x + w) > vr){
8309                     x = vr - w;
8310                     moved = true;
8311                 }
8312                 if((y + h) > vb){
8313                     y = vb - h;
8314                     moved = true;
8315                 }
8316                 // then make sure top/left isn't negative
8317                 if(x < vx){
8318                     x = vx;
8319                     moved = true;
8320                 }
8321                 if(y < vy){
8322                     y = vy;
8323                     moved = true;
8324                 }
8325                 return moved ? [x, y] : false;
8326             };
8327         }(),
8328
8329         // private
8330         adjustForConstraints : function(xy, parent, offsets){
8331             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8332         },
8333
8334         /**
8335          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8336          * document it aligns it to the viewport.
8337          * The position parameter is optional, and can be specified in any one of the following formats:
8338          * <ul>
8339          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8340          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8341          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8342          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8343          *   <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
8344          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8345          * </ul>
8346          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8347          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8348          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8349          * that specified in order to enforce the viewport constraints.
8350          * Following are all of the supported anchor positions:
8351     <pre>
8352     Value  Description
8353     -----  -----------------------------
8354     tl     The top left corner (default)
8355     t      The center of the top edge
8356     tr     The top right corner
8357     l      The center of the left edge
8358     c      In the center of the element
8359     r      The center of the right edge
8360     bl     The bottom left corner
8361     b      The center of the bottom edge
8362     br     The bottom right corner
8363     </pre>
8364     Example Usage:
8365     <pre><code>
8366     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8367     el.alignTo("other-el");
8368
8369     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8370     el.alignTo("other-el", "tr?");
8371
8372     // align the bottom right corner of el with the center left edge of other-el
8373     el.alignTo("other-el", "br-l?");
8374
8375     // align the center of el with the bottom left corner of other-el and
8376     // adjust the x position by -6 pixels (and the y position by 0)
8377     el.alignTo("other-el", "c-bl", [-6, 0]);
8378     </code></pre>
8379          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8380          * @param {String} position The position to align to.
8381          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8382          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8383          * @return {Roo.Element} this
8384          */
8385         alignTo : function(element, position, offsets, animate){
8386             var xy = this.getAlignToXY(element, position, offsets);
8387             this.setXY(xy, this.preanim(arguments, 3));
8388             return this;
8389         },
8390
8391         /**
8392          * Anchors an element to another element and realigns it when the window is resized.
8393          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8394          * @param {String} position The position to align to.
8395          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8396          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8397          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8398          * is a number, it is used as the buffer delay (defaults to 50ms).
8399          * @param {Function} callback The function to call after the animation finishes
8400          * @return {Roo.Element} this
8401          */
8402         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8403             var action = function(){
8404                 this.alignTo(el, alignment, offsets, animate);
8405                 Roo.callback(callback, this);
8406             };
8407             Roo.EventManager.onWindowResize(action, this);
8408             var tm = typeof monitorScroll;
8409             if(tm != 'undefined'){
8410                 Roo.EventManager.on(window, 'scroll', action, this,
8411                     {buffer: tm == 'number' ? monitorScroll : 50});
8412             }
8413             action.call(this); // align immediately
8414             return this;
8415         },
8416         /**
8417          * Clears any opacity settings from this element. Required in some cases for IE.
8418          * @return {Roo.Element} this
8419          */
8420         clearOpacity : function(){
8421             if (window.ActiveXObject) {
8422                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8423                     this.dom.style.filter = "";
8424                 }
8425             } else {
8426                 this.dom.style.opacity = "";
8427                 this.dom.style["-moz-opacity"] = "";
8428                 this.dom.style["-khtml-opacity"] = "";
8429             }
8430             return this;
8431         },
8432
8433         /**
8434          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8435          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8436          * @return {Roo.Element} this
8437          */
8438         hide : function(animate){
8439             this.setVisible(false, this.preanim(arguments, 0));
8440             return this;
8441         },
8442
8443         /**
8444         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8445         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8446          * @return {Roo.Element} this
8447          */
8448         show : function(animate){
8449             this.setVisible(true, this.preanim(arguments, 0));
8450             return this;
8451         },
8452
8453         /**
8454          * @private Test if size has a unit, otherwise appends the default
8455          */
8456         addUnits : function(size){
8457             return Roo.Element.addUnits(size, this.defaultUnit);
8458         },
8459
8460         /**
8461          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8462          * @return {Roo.Element} this
8463          */
8464         beginMeasure : function(){
8465             var el = this.dom;
8466             if(el.offsetWidth || el.offsetHeight){
8467                 return this; // offsets work already
8468             }
8469             var changed = [];
8470             var p = this.dom, b = document.body; // start with this element
8471             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8472                 var pe = Roo.get(p);
8473                 if(pe.getStyle('display') == 'none'){
8474                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8475                     p.style.visibility = "hidden";
8476                     p.style.display = "block";
8477                 }
8478                 p = p.parentNode;
8479             }
8480             this._measureChanged = changed;
8481             return this;
8482
8483         },
8484
8485         /**
8486          * Restores displays to before beginMeasure was called
8487          * @return {Roo.Element} this
8488          */
8489         endMeasure : function(){
8490             var changed = this._measureChanged;
8491             if(changed){
8492                 for(var i = 0, len = changed.length; i < len; i++) {
8493                     var r = changed[i];
8494                     r.el.style.visibility = r.visibility;
8495                     r.el.style.display = "none";
8496                 }
8497                 this._measureChanged = null;
8498             }
8499             return this;
8500         },
8501
8502         /**
8503         * Update the innerHTML of this element, optionally searching for and processing scripts
8504         * @param {String} html The new HTML
8505         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8506         * @param {Function} callback For async script loading you can be noticed when the update completes
8507         * @return {Roo.Element} this
8508          */
8509         update : function(html, loadScripts, callback){
8510             if(typeof html == "undefined"){
8511                 html = "";
8512             }
8513             if(loadScripts !== true){
8514                 this.dom.innerHTML = html;
8515                 if(typeof callback == "function"){
8516                     callback();
8517                 }
8518                 return this;
8519             }
8520             var id = Roo.id();
8521             var dom = this.dom;
8522
8523             html += '<span id="' + id + '"></span>';
8524
8525             E.onAvailable(id, function(){
8526                 var hd = document.getElementsByTagName("head")[0];
8527                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8528                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8529                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8530
8531                 var match;
8532                 while(match = re.exec(html)){
8533                     var attrs = match[1];
8534                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8535                     if(srcMatch && srcMatch[2]){
8536                        var s = document.createElement("script");
8537                        s.src = srcMatch[2];
8538                        var typeMatch = attrs.match(typeRe);
8539                        if(typeMatch && typeMatch[2]){
8540                            s.type = typeMatch[2];
8541                        }
8542                        hd.appendChild(s);
8543                     }else if(match[2] && match[2].length > 0){
8544                         if(window.execScript) {
8545                            window.execScript(match[2]);
8546                         } else {
8547                             /**
8548                              * eval:var:id
8549                              * eval:var:dom
8550                              * eval:var:html
8551                              * 
8552                              */
8553                            window.eval(match[2]);
8554                         }
8555                     }
8556                 }
8557                 var el = document.getElementById(id);
8558                 if(el){el.parentNode.removeChild(el);}
8559                 if(typeof callback == "function"){
8560                     callback();
8561                 }
8562             });
8563             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8564             return this;
8565         },
8566
8567         /**
8568          * Direct access to the UpdateManager update() method (takes the same parameters).
8569          * @param {String/Function} url The url for this request or a function to call to get the url
8570          * @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}
8571          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8572          * @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.
8573          * @return {Roo.Element} this
8574          */
8575         load : function(){
8576             var um = this.getUpdateManager();
8577             um.update.apply(um, arguments);
8578             return this;
8579         },
8580
8581         /**
8582         * Gets this element's UpdateManager
8583         * @return {Roo.UpdateManager} The UpdateManager
8584         */
8585         getUpdateManager : function(){
8586             if(!this.updateManager){
8587                 this.updateManager = new Roo.UpdateManager(this);
8588             }
8589             return this.updateManager;
8590         },
8591
8592         /**
8593          * Disables text selection for this element (normalized across browsers)
8594          * @return {Roo.Element} this
8595          */
8596         unselectable : function(){
8597             this.dom.unselectable = "on";
8598             this.swallowEvent("selectstart", true);
8599             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8600             this.addClass("x-unselectable");
8601             return this;
8602         },
8603
8604         /**
8605         * Calculates the x, y to center this element on the screen
8606         * @return {Array} The x, y values [x, y]
8607         */
8608         getCenterXY : function(){
8609             return this.getAlignToXY(document, 'c-c');
8610         },
8611
8612         /**
8613         * Centers the Element in either the viewport, or another Element.
8614         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8615         */
8616         center : function(centerIn){
8617             this.alignTo(centerIn || document, 'c-c');
8618             return this;
8619         },
8620
8621         /**
8622          * Tests various css rules/browsers to determine if this element uses a border box
8623          * @return {Boolean}
8624          */
8625         isBorderBox : function(){
8626             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8627         },
8628
8629         /**
8630          * Return a box {x, y, width, height} that can be used to set another elements
8631          * size/location to match this element.
8632          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8633          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8634          * @return {Object} box An object in the format {x, y, width, height}
8635          */
8636         getBox : function(contentBox, local){
8637             var xy;
8638             if(!local){
8639                 xy = this.getXY();
8640             }else{
8641                 var left = parseInt(this.getStyle("left"), 10) || 0;
8642                 var top = parseInt(this.getStyle("top"), 10) || 0;
8643                 xy = [left, top];
8644             }
8645             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8646             if(!contentBox){
8647                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8648             }else{
8649                 var l = this.getBorderWidth("l")+this.getPadding("l");
8650                 var r = this.getBorderWidth("r")+this.getPadding("r");
8651                 var t = this.getBorderWidth("t")+this.getPadding("t");
8652                 var b = this.getBorderWidth("b")+this.getPadding("b");
8653                 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)};
8654             }
8655             bx.right = bx.x + bx.width;
8656             bx.bottom = bx.y + bx.height;
8657             return bx;
8658         },
8659
8660         /**
8661          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8662          for more information about the sides.
8663          * @param {String} sides
8664          * @return {Number}
8665          */
8666         getFrameWidth : function(sides, onlyContentBox){
8667             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8668         },
8669
8670         /**
8671          * 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.
8672          * @param {Object} box The box to fill {x, y, width, height}
8673          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8674          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8675          * @return {Roo.Element} this
8676          */
8677         setBox : function(box, adjust, animate){
8678             var w = box.width, h = box.height;
8679             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8680                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8681                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8682             }
8683             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8684             return this;
8685         },
8686
8687         /**
8688          * Forces the browser to repaint this element
8689          * @return {Roo.Element} this
8690          */
8691          repaint : function(){
8692             var dom = this.dom;
8693             this.addClass("x-repaint");
8694             setTimeout(function(){
8695                 Roo.get(dom).removeClass("x-repaint");
8696             }, 1);
8697             return this;
8698         },
8699
8700         /**
8701          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8702          * then it returns the calculated width of the sides (see getPadding)
8703          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8704          * @return {Object/Number}
8705          */
8706         getMargins : function(side){
8707             if(!side){
8708                 return {
8709                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8710                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8711                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8712                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8713                 };
8714             }else{
8715                 return this.addStyles(side, El.margins);
8716              }
8717         },
8718
8719         // private
8720         addStyles : function(sides, styles){
8721             var val = 0, v, w;
8722             for(var i = 0, len = sides.length; i < len; i++){
8723                 v = this.getStyle(styles[sides.charAt(i)]);
8724                 if(v){
8725                      w = parseInt(v, 10);
8726                      if(w){ val += w; }
8727                 }
8728             }
8729             return val;
8730         },
8731
8732         /**
8733          * Creates a proxy element of this element
8734          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8735          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8736          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8737          * @return {Roo.Element} The new proxy element
8738          */
8739         createProxy : function(config, renderTo, matchBox){
8740             if(renderTo){
8741                 renderTo = Roo.getDom(renderTo);
8742             }else{
8743                 renderTo = document.body;
8744             }
8745             config = typeof config == "object" ?
8746                 config : {tag : "div", cls: config};
8747             var proxy = Roo.DomHelper.append(renderTo, config, true);
8748             if(matchBox){
8749                proxy.setBox(this.getBox());
8750             }
8751             return proxy;
8752         },
8753
8754         /**
8755          * Puts a mask over this element to disable user interaction. Requires core.css.
8756          * This method can only be applied to elements which accept child nodes.
8757          * @param {String} msg (optional) A message to display in the mask
8758          * @param {String} msgCls (optional) A css class to apply to the msg element
8759          * @return {Element} The mask  element
8760          */
8761         mask : function(msg, msgCls){
8762             if(this.getStyle("position") == "static"){
8763                 this.setStyle("position", "relative");
8764             }
8765             if(!this._mask){
8766                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8767             }
8768             this.addClass("x-masked");
8769             this._mask.setDisplayed(true);
8770             if(typeof msg == 'string'){
8771                 if(!this._maskMsg){
8772                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8773                 }
8774                 var mm = this._maskMsg;
8775                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8776                 mm.dom.firstChild.innerHTML = msg;
8777                 mm.setDisplayed(true);
8778                 mm.center(this);
8779             }
8780             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8781                 this._mask.setHeight(this.getHeight());
8782             }
8783             return this._mask;
8784         },
8785
8786         /**
8787          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8788          * it is cached for reuse.
8789          */
8790         unmask : function(removeEl){
8791             if(this._mask){
8792                 if(removeEl === true){
8793                     this._mask.remove();
8794                     delete this._mask;
8795                     if(this._maskMsg){
8796                         this._maskMsg.remove();
8797                         delete this._maskMsg;
8798                     }
8799                 }else{
8800                     this._mask.setDisplayed(false);
8801                     if(this._maskMsg){
8802                         this._maskMsg.setDisplayed(false);
8803                     }
8804                 }
8805             }
8806             this.removeClass("x-masked");
8807         },
8808
8809         /**
8810          * Returns true if this element is masked
8811          * @return {Boolean}
8812          */
8813         isMasked : function(){
8814             return this._mask && this._mask.isVisible();
8815         },
8816
8817         /**
8818          * Creates an iframe shim for this element to keep selects and other windowed objects from
8819          * showing through.
8820          * @return {Roo.Element} The new shim element
8821          */
8822         createShim : function(){
8823             var el = document.createElement('iframe');
8824             el.frameBorder = 'no';
8825             el.className = 'roo-shim';
8826             if(Roo.isIE && Roo.isSecure){
8827                 el.src = Roo.SSL_SECURE_URL;
8828             }
8829             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
8830             shim.autoBoxAdjust = false;
8831             return shim;
8832         },
8833
8834         /**
8835          * Removes this element from the DOM and deletes it from the cache
8836          */
8837         remove : function(){
8838             if(this.dom.parentNode){
8839                 this.dom.parentNode.removeChild(this.dom);
8840             }
8841             delete El.cache[this.dom.id];
8842         },
8843
8844         /**
8845          * Sets up event handlers to add and remove a css class when the mouse is over this element
8846          * @param {String} className
8847          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
8848          * mouseout events for children elements
8849          * @return {Roo.Element} this
8850          */
8851         addClassOnOver : function(className, preventFlicker){
8852             this.on("mouseover", function(){
8853                 Roo.fly(this, '_internal').addClass(className);
8854             }, this.dom);
8855             var removeFn = function(e){
8856                 if(preventFlicker !== true || !e.within(this, true)){
8857                     Roo.fly(this, '_internal').removeClass(className);
8858                 }
8859             };
8860             this.on("mouseout", removeFn, this.dom);
8861             return this;
8862         },
8863
8864         /**
8865          * Sets up event handlers to add and remove a css class when this element has the focus
8866          * @param {String} className
8867          * @return {Roo.Element} this
8868          */
8869         addClassOnFocus : function(className){
8870             this.on("focus", function(){
8871                 Roo.fly(this, '_internal').addClass(className);
8872             }, this.dom);
8873             this.on("blur", function(){
8874                 Roo.fly(this, '_internal').removeClass(className);
8875             }, this.dom);
8876             return this;
8877         },
8878         /**
8879          * 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)
8880          * @param {String} className
8881          * @return {Roo.Element} this
8882          */
8883         addClassOnClick : function(className){
8884             var dom = this.dom;
8885             this.on("mousedown", function(){
8886                 Roo.fly(dom, '_internal').addClass(className);
8887                 var d = Roo.get(document);
8888                 var fn = function(){
8889                     Roo.fly(dom, '_internal').removeClass(className);
8890                     d.removeListener("mouseup", fn);
8891                 };
8892                 d.on("mouseup", fn);
8893             });
8894             return this;
8895         },
8896
8897         /**
8898          * Stops the specified event from bubbling and optionally prevents the default action
8899          * @param {String} eventName
8900          * @param {Boolean} preventDefault (optional) true to prevent the default action too
8901          * @return {Roo.Element} this
8902          */
8903         swallowEvent : function(eventName, preventDefault){
8904             var fn = function(e){
8905                 e.stopPropagation();
8906                 if(preventDefault){
8907                     e.preventDefault();
8908                 }
8909             };
8910             if(eventName instanceof Array){
8911                 for(var i = 0, len = eventName.length; i < len; i++){
8912                      this.on(eventName[i], fn);
8913                 }
8914                 return this;
8915             }
8916             this.on(eventName, fn);
8917             return this;
8918         },
8919
8920         /**
8921          * @private
8922          */
8923       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
8924
8925         /**
8926          * Sizes this element to its parent element's dimensions performing
8927          * neccessary box adjustments.
8928          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
8929          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
8930          * @return {Roo.Element} this
8931          */
8932         fitToParent : function(monitorResize, targetParent) {
8933           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
8934           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
8935           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
8936             return;
8937           }
8938           var p = Roo.get(targetParent || this.dom.parentNode);
8939           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
8940           if (monitorResize === true) {
8941             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
8942             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
8943           }
8944           return this;
8945         },
8946
8947         /**
8948          * Gets the next sibling, skipping text nodes
8949          * @return {HTMLElement} The next sibling or null
8950          */
8951         getNextSibling : function(){
8952             var n = this.dom.nextSibling;
8953             while(n && n.nodeType != 1){
8954                 n = n.nextSibling;
8955             }
8956             return n;
8957         },
8958
8959         /**
8960          * Gets the previous sibling, skipping text nodes
8961          * @return {HTMLElement} The previous sibling or null
8962          */
8963         getPrevSibling : function(){
8964             var n = this.dom.previousSibling;
8965             while(n && n.nodeType != 1){
8966                 n = n.previousSibling;
8967             }
8968             return n;
8969         },
8970
8971
8972         /**
8973          * Appends the passed element(s) to this element
8974          * @param {String/HTMLElement/Array/Element/CompositeElement} el
8975          * @return {Roo.Element} this
8976          */
8977         appendChild: function(el){
8978             el = Roo.get(el);
8979             el.appendTo(this);
8980             return this;
8981         },
8982
8983         /**
8984          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
8985          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
8986          * automatically generated with the specified attributes.
8987          * @param {HTMLElement} insertBefore (optional) a child element of this element
8988          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
8989          * @return {Roo.Element} The new child element
8990          */
8991         createChild: function(config, insertBefore, returnDom){
8992             config = config || {tag:'div'};
8993             if(insertBefore){
8994                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
8995             }
8996             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
8997         },
8998
8999         /**
9000          * Appends this element to the passed element
9001          * @param {String/HTMLElement/Element} el The new parent element
9002          * @return {Roo.Element} this
9003          */
9004         appendTo: function(el){
9005             el = Roo.getDom(el);
9006             el.appendChild(this.dom);
9007             return this;
9008         },
9009
9010         /**
9011          * Inserts this element before the passed element in the DOM
9012          * @param {String/HTMLElement/Element} el The element to insert before
9013          * @return {Roo.Element} this
9014          */
9015         insertBefore: function(el){
9016             el = Roo.getDom(el);
9017             el.parentNode.insertBefore(this.dom, el);
9018             return this;
9019         },
9020
9021         /**
9022          * Inserts this element after the passed element in the DOM
9023          * @param {String/HTMLElement/Element} el The element to insert after
9024          * @return {Roo.Element} this
9025          */
9026         insertAfter: function(el){
9027             el = Roo.getDom(el);
9028             el.parentNode.insertBefore(this.dom, el.nextSibling);
9029             return this;
9030         },
9031
9032         /**
9033          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9034          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9035          * @return {Roo.Element} The new child
9036          */
9037         insertFirst: function(el, returnDom){
9038             el = el || {};
9039             if(typeof el == 'object' && !el.nodeType){ // dh config
9040                 return this.createChild(el, this.dom.firstChild, returnDom);
9041             }else{
9042                 el = Roo.getDom(el);
9043                 this.dom.insertBefore(el, this.dom.firstChild);
9044                 return !returnDom ? Roo.get(el) : el;
9045             }
9046         },
9047
9048         /**
9049          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9050          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9051          * @param {String} where (optional) 'before' or 'after' defaults to before
9052          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9053          * @return {Roo.Element} the inserted Element
9054          */
9055         insertSibling: function(el, where, returnDom){
9056             where = where ? where.toLowerCase() : 'before';
9057             el = el || {};
9058             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9059
9060             if(typeof el == 'object' && !el.nodeType){ // dh config
9061                 if(where == 'after' && !this.dom.nextSibling){
9062                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9063                 }else{
9064                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9065                 }
9066
9067             }else{
9068                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9069                             where == 'before' ? this.dom : this.dom.nextSibling);
9070                 if(!returnDom){
9071                     rt = Roo.get(rt);
9072                 }
9073             }
9074             return rt;
9075         },
9076
9077         /**
9078          * Creates and wraps this element with another element
9079          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9080          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9081          * @return {HTMLElement/Element} The newly created wrapper element
9082          */
9083         wrap: function(config, returnDom){
9084             if(!config){
9085                 config = {tag: "div"};
9086             }
9087             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9088             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9089             return newEl;
9090         },
9091
9092         /**
9093          * Replaces the passed element with this element
9094          * @param {String/HTMLElement/Element} el The element to replace
9095          * @return {Roo.Element} this
9096          */
9097         replace: function(el){
9098             el = Roo.get(el);
9099             this.insertBefore(el);
9100             el.remove();
9101             return this;
9102         },
9103
9104         /**
9105          * Inserts an html fragment into this element
9106          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9107          * @param {String} html The HTML fragment
9108          * @param {Boolean} returnEl True to return an Roo.Element
9109          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9110          */
9111         insertHtml : function(where, html, returnEl){
9112             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9113             return returnEl ? Roo.get(el) : el;
9114         },
9115
9116         /**
9117          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9118          * @param {Object} o The object with the attributes
9119          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9120          * @return {Roo.Element} this
9121          */
9122         set : function(o, useSet){
9123             var el = this.dom;
9124             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9125             for(var attr in o){
9126                 if(attr == "style" || typeof o[attr] == "function") continue;
9127                 if(attr=="cls"){
9128                     el.className = o["cls"];
9129                 }else{
9130                     if(useSet) el.setAttribute(attr, o[attr]);
9131                     else el[attr] = o[attr];
9132                 }
9133             }
9134             if(o.style){
9135                 Roo.DomHelper.applyStyles(el, o.style);
9136             }
9137             return this;
9138         },
9139
9140         /**
9141          * Convenience method for constructing a KeyMap
9142          * @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:
9143          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9144          * @param {Function} fn The function to call
9145          * @param {Object} scope (optional) The scope of the function
9146          * @return {Roo.KeyMap} The KeyMap created
9147          */
9148         addKeyListener : function(key, fn, scope){
9149             var config;
9150             if(typeof key != "object" || key instanceof Array){
9151                 config = {
9152                     key: key,
9153                     fn: fn,
9154                     scope: scope
9155                 };
9156             }else{
9157                 config = {
9158                     key : key.key,
9159                     shift : key.shift,
9160                     ctrl : key.ctrl,
9161                     alt : key.alt,
9162                     fn: fn,
9163                     scope: scope
9164                 };
9165             }
9166             return new Roo.KeyMap(this, config);
9167         },
9168
9169         /**
9170          * Creates a KeyMap for this element
9171          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9172          * @return {Roo.KeyMap} The KeyMap created
9173          */
9174         addKeyMap : function(config){
9175             return new Roo.KeyMap(this, config);
9176         },
9177
9178         /**
9179          * Returns true if this element is scrollable.
9180          * @return {Boolean}
9181          */
9182          isScrollable : function(){
9183             var dom = this.dom;
9184             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9185         },
9186
9187         /**
9188          * 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().
9189          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9190          * @param {Number} value The new scroll value
9191          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9192          * @return {Element} this
9193          */
9194
9195         scrollTo : function(side, value, animate){
9196             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9197             if(!animate || !A){
9198                 this.dom[prop] = value;
9199             }else{
9200                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9201                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9202             }
9203             return this;
9204         },
9205
9206         /**
9207          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9208          * within this element's scrollable range.
9209          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9210          * @param {Number} distance How far to scroll the element in pixels
9211          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9212          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9213          * was scrolled as far as it could go.
9214          */
9215          scroll : function(direction, distance, animate){
9216              if(!this.isScrollable()){
9217                  return;
9218              }
9219              var el = this.dom;
9220              var l = el.scrollLeft, t = el.scrollTop;
9221              var w = el.scrollWidth, h = el.scrollHeight;
9222              var cw = el.clientWidth, ch = el.clientHeight;
9223              direction = direction.toLowerCase();
9224              var scrolled = false;
9225              var a = this.preanim(arguments, 2);
9226              switch(direction){
9227                  case "l":
9228                  case "left":
9229                      if(w - l > cw){
9230                          var v = Math.min(l + distance, w-cw);
9231                          this.scrollTo("left", v, a);
9232                          scrolled = true;
9233                      }
9234                      break;
9235                 case "r":
9236                 case "right":
9237                      if(l > 0){
9238                          var v = Math.max(l - distance, 0);
9239                          this.scrollTo("left", v, a);
9240                          scrolled = true;
9241                      }
9242                      break;
9243                 case "t":
9244                 case "top":
9245                 case "up":
9246                      if(t > 0){
9247                          var v = Math.max(t - distance, 0);
9248                          this.scrollTo("top", v, a);
9249                          scrolled = true;
9250                      }
9251                      break;
9252                 case "b":
9253                 case "bottom":
9254                 case "down":
9255                      if(h - t > ch){
9256                          var v = Math.min(t + distance, h-ch);
9257                          this.scrollTo("top", v, a);
9258                          scrolled = true;
9259                      }
9260                      break;
9261              }
9262              return scrolled;
9263         },
9264
9265         /**
9266          * Translates the passed page coordinates into left/top css values for this element
9267          * @param {Number/Array} x The page x or an array containing [x, y]
9268          * @param {Number} y The page y
9269          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9270          */
9271         translatePoints : function(x, y){
9272             if(typeof x == 'object' || x instanceof Array){
9273                 y = x[1]; x = x[0];
9274             }
9275             var p = this.getStyle('position');
9276             var o = this.getXY();
9277
9278             var l = parseInt(this.getStyle('left'), 10);
9279             var t = parseInt(this.getStyle('top'), 10);
9280
9281             if(isNaN(l)){
9282                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9283             }
9284             if(isNaN(t)){
9285                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9286             }
9287
9288             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9289         },
9290
9291         /**
9292          * Returns the current scroll position of the element.
9293          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9294          */
9295         getScroll : function(){
9296             var d = this.dom, doc = document;
9297             if(d == doc || d == doc.body){
9298                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9299                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9300                 return {left: l, top: t};
9301             }else{
9302                 return {left: d.scrollLeft, top: d.scrollTop};
9303             }
9304         },
9305
9306         /**
9307          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9308          * are convert to standard 6 digit hex color.
9309          * @param {String} attr The css attribute
9310          * @param {String} defaultValue The default value to use when a valid color isn't found
9311          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9312          * YUI color anims.
9313          */
9314         getColor : function(attr, defaultValue, prefix){
9315             var v = this.getStyle(attr);
9316             if(!v || v == "transparent" || v == "inherit") {
9317                 return defaultValue;
9318             }
9319             var color = typeof prefix == "undefined" ? "#" : prefix;
9320             if(v.substr(0, 4) == "rgb("){
9321                 var rvs = v.slice(4, v.length -1).split(",");
9322                 for(var i = 0; i < 3; i++){
9323                     var h = parseInt(rvs[i]).toString(16);
9324                     if(h < 16){
9325                         h = "0" + h;
9326                     }
9327                     color += h;
9328                 }
9329             } else {
9330                 if(v.substr(0, 1) == "#"){
9331                     if(v.length == 4) {
9332                         for(var i = 1; i < 4; i++){
9333                             var c = v.charAt(i);
9334                             color +=  c + c;
9335                         }
9336                     }else if(v.length == 7){
9337                         color += v.substr(1);
9338                     }
9339                 }
9340             }
9341             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9342         },
9343
9344         /**
9345          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9346          * gradient background, rounded corners and a 4-way shadow.
9347          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9348          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9349          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9350          * @return {Roo.Element} this
9351          */
9352         boxWrap : function(cls){
9353             cls = cls || 'x-box';
9354             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9355             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9356             return el;
9357         },
9358
9359         /**
9360          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9361          * @param {String} namespace The namespace in which to look for the attribute
9362          * @param {String} name The attribute name
9363          * @return {String} The attribute value
9364          */
9365         getAttributeNS : Roo.isIE ? function(ns, name){
9366             var d = this.dom;
9367             var type = typeof d[ns+":"+name];
9368             if(type != 'undefined' && type != 'unknown'){
9369                 return d[ns+":"+name];
9370             }
9371             return d[name];
9372         } : function(ns, name){
9373             var d = this.dom;
9374             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9375         }
9376     };
9377
9378     var ep = El.prototype;
9379
9380     /**
9381      * Appends an event handler (Shorthand for addListener)
9382      * @param {String}   eventName     The type of event to append
9383      * @param {Function} fn        The method the event invokes
9384      * @param {Object} scope       (optional) The scope (this object) of the fn
9385      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9386      * @method
9387      */
9388     ep.on = ep.addListener;
9389         // backwards compat
9390     ep.mon = ep.addListener;
9391
9392     /**
9393      * Removes an event handler from this element (shorthand for removeListener)
9394      * @param {String} eventName the type of event to remove
9395      * @param {Function} fn the method the event invokes
9396      * @return {Roo.Element} this
9397      * @method
9398      */
9399     ep.un = ep.removeListener;
9400
9401     /**
9402      * true to automatically adjust width and height settings for box-model issues (default to true)
9403      */
9404     ep.autoBoxAdjust = true;
9405
9406     // private
9407     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9408
9409     // private
9410     El.addUnits = function(v, defaultUnit){
9411         if(v === "" || v == "auto"){
9412             return v;
9413         }
9414         if(v === undefined){
9415             return '';
9416         }
9417         if(typeof v == "number" || !El.unitPattern.test(v)){
9418             return v + (defaultUnit || 'px');
9419         }
9420         return v;
9421     };
9422
9423     // special markup used throughout Roo when box wrapping elements
9424     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>';
9425     /**
9426      * Visibility mode constant - Use visibility to hide element
9427      * @static
9428      * @type Number
9429      */
9430     El.VISIBILITY = 1;
9431     /**
9432      * Visibility mode constant - Use display to hide element
9433      * @static
9434      * @type Number
9435      */
9436     El.DISPLAY = 2;
9437
9438     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9439     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9440     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9441
9442
9443
9444     /**
9445      * @private
9446      */
9447     El.cache = {};
9448
9449     var docEl;
9450
9451     /**
9452      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9453      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9454      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9455      * @return {Element} The Element object
9456      * @static
9457      */
9458     El.get = function(el){
9459         var ex, elm, id;
9460         if(!el){ return null; }
9461         if(typeof el == "string"){ // element id
9462             if(!(elm = document.getElementById(el))){
9463                 return null;
9464             }
9465             if(ex = El.cache[el]){
9466                 ex.dom = elm;
9467             }else{
9468                 ex = El.cache[el] = new El(elm);
9469             }
9470             return ex;
9471         }else if(el.tagName){ // dom element
9472             if(!(id = el.id)){
9473                 id = Roo.id(el);
9474             }
9475             if(ex = El.cache[id]){
9476                 ex.dom = el;
9477             }else{
9478                 ex = El.cache[id] = new El(el);
9479             }
9480             return ex;
9481         }else if(el instanceof El){
9482             if(el != docEl){
9483                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9484                                                               // catch case where it hasn't been appended
9485                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9486             }
9487             return el;
9488         }else if(el.isComposite){
9489             return el;
9490         }else if(el instanceof Array){
9491             return El.select(el);
9492         }else if(el == document){
9493             // create a bogus element object representing the document object
9494             if(!docEl){
9495                 var f = function(){};
9496                 f.prototype = El.prototype;
9497                 docEl = new f();
9498                 docEl.dom = document;
9499             }
9500             return docEl;
9501         }
9502         return null;
9503     };
9504
9505     // private
9506     El.uncache = function(el){
9507         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9508             if(a[i]){
9509                 delete El.cache[a[i].id || a[i]];
9510             }
9511         }
9512     };
9513
9514     // private
9515     // Garbage collection - uncache elements/purge listeners on orphaned elements
9516     // so we don't hold a reference and cause the browser to retain them
9517     El.garbageCollect = function(){
9518         if(!Roo.enableGarbageCollector){
9519             clearInterval(El.collectorThread);
9520             return;
9521         }
9522         for(var eid in El.cache){
9523             var el = El.cache[eid], d = el.dom;
9524             // -------------------------------------------------------
9525             // Determining what is garbage:
9526             // -------------------------------------------------------
9527             // !d
9528             // dom node is null, definitely garbage
9529             // -------------------------------------------------------
9530             // !d.parentNode
9531             // no parentNode == direct orphan, definitely garbage
9532             // -------------------------------------------------------
9533             // !d.offsetParent && !document.getElementById(eid)
9534             // display none elements have no offsetParent so we will
9535             // also try to look it up by it's id. However, check
9536             // offsetParent first so we don't do unneeded lookups.
9537             // This enables collection of elements that are not orphans
9538             // directly, but somewhere up the line they have an orphan
9539             // parent.
9540             // -------------------------------------------------------
9541             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9542                 delete El.cache[eid];
9543                 if(d && Roo.enableListenerCollection){
9544                     E.purgeElement(d);
9545                 }
9546             }
9547         }
9548     }
9549     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9550
9551
9552     // dom is optional
9553     El.Flyweight = function(dom){
9554         this.dom = dom;
9555     };
9556     El.Flyweight.prototype = El.prototype;
9557
9558     El._flyweights = {};
9559     /**
9560      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9561      * the dom node can be overwritten by other code.
9562      * @param {String/HTMLElement} el The dom node or id
9563      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9564      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9565      * @static
9566      * @return {Element} The shared Element object
9567      */
9568     El.fly = function(el, named){
9569         named = named || '_global';
9570         el = Roo.getDom(el);
9571         if(!el){
9572             return null;
9573         }
9574         if(!El._flyweights[named]){
9575             El._flyweights[named] = new El.Flyweight();
9576         }
9577         El._flyweights[named].dom = el;
9578         return El._flyweights[named];
9579     };
9580
9581     /**
9582      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9583      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9584      * Shorthand of {@link Roo.Element#get}
9585      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9586      * @return {Element} The Element object
9587      * @member Roo
9588      * @method get
9589      */
9590     Roo.get = El.get;
9591     /**
9592      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9593      * the dom node can be overwritten by other code.
9594      * Shorthand of {@link Roo.Element#fly}
9595      * @param {String/HTMLElement} el The dom node or id
9596      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9597      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9598      * @static
9599      * @return {Element} The shared Element object
9600      * @member Roo
9601      * @method fly
9602      */
9603     Roo.fly = El.fly;
9604
9605     // speedy lookup for elements never to box adjust
9606     var noBoxAdjust = Roo.isStrict ? {
9607         select:1
9608     } : {
9609         input:1, select:1, textarea:1
9610     };
9611     if(Roo.isIE || Roo.isGecko){
9612         noBoxAdjust['button'] = 1;
9613     }
9614
9615
9616     Roo.EventManager.on(window, 'unload', function(){
9617         delete El.cache;
9618         delete El._flyweights;
9619     });
9620 })();
9621
9622
9623
9624
9625 if(Roo.DomQuery){
9626     Roo.Element.selectorFunction = Roo.DomQuery.select;
9627 }
9628
9629 Roo.Element.select = function(selector, unique, root){
9630     var els;
9631     if(typeof selector == "string"){
9632         els = Roo.Element.selectorFunction(selector, root);
9633     }else if(selector.length !== undefined){
9634         els = selector;
9635     }else{
9636         throw "Invalid selector";
9637     }
9638     if(unique === true){
9639         return new Roo.CompositeElement(els);
9640     }else{
9641         return new Roo.CompositeElementLite(els);
9642     }
9643 };
9644 /**
9645  * Selects elements based on the passed CSS selector to enable working on them as 1.
9646  * @param {String/Array} selector The CSS selector or an array of elements
9647  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9648  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9649  * @return {CompositeElementLite/CompositeElement}
9650  * @member Roo
9651  * @method select
9652  */
9653 Roo.select = Roo.Element.select;
9654
9655
9656
9657
9658
9659
9660
9661
9662
9663
9664
9665
9666
9667
9668 /*
9669  * Based on:
9670  * Ext JS Library 1.1.1
9671  * Copyright(c) 2006-2007, Ext JS, LLC.
9672  *
9673  * Originally Released Under LGPL - original licence link has changed is not relivant.
9674  *
9675  * Fork - LGPL
9676  * <script type="text/javascript">
9677  */
9678
9679
9680
9681 //Notifies Element that fx methods are available
9682 Roo.enableFx = true;
9683
9684 /**
9685  * @class Roo.Fx
9686  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9687  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9688  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9689  * Element effects to work.</p><br/>
9690  *
9691  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9692  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9693  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9694  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9695  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9696  * expected results and should be done with care.</p><br/>
9697  *
9698  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9699  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9700 <pre>
9701 Value  Description
9702 -----  -----------------------------
9703 tl     The top left corner
9704 t      The center of the top edge
9705 tr     The top right corner
9706 l      The center of the left edge
9707 r      The center of the right edge
9708 bl     The bottom left corner
9709 b      The center of the bottom edge
9710 br     The bottom right corner
9711 </pre>
9712  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9713  * below are common options that can be passed to any Fx method.</b>
9714  * @cfg {Function} callback A function called when the effect is finished
9715  * @cfg {Object} scope The scope of the effect function
9716  * @cfg {String} easing A valid Easing value for the effect
9717  * @cfg {String} afterCls A css class to apply after the effect
9718  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9719  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9720  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9721  * effects that end with the element being visually hidden, ignored otherwise)
9722  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9723  * a function which returns such a specification that will be applied to the Element after the effect finishes
9724  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9725  * @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
9726  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9727  */
9728 Roo.Fx = {
9729         /**
9730          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9731          * origin for the slide effect.  This function automatically handles wrapping the element with
9732          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9733          * Usage:
9734          *<pre><code>
9735 // default: slide the element in from the top
9736 el.slideIn();
9737
9738 // custom: slide the element in from the right with a 2-second duration
9739 el.slideIn('r', { duration: 2 });
9740
9741 // common config options shown with default values
9742 el.slideIn('t', {
9743     easing: 'easeOut',
9744     duration: .5
9745 });
9746 </code></pre>
9747          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9748          * @param {Object} options (optional) Object literal with any of the Fx config options
9749          * @return {Roo.Element} The Element
9750          */
9751     slideIn : function(anchor, o){
9752         var el = this.getFxEl();
9753         o = o || {};
9754
9755         el.queueFx(o, function(){
9756
9757             anchor = anchor || "t";
9758
9759             // fix display to visibility
9760             this.fixDisplay();
9761
9762             // restore values after effect
9763             var r = this.getFxRestore();
9764             var b = this.getBox();
9765             // fixed size for slide
9766             this.setSize(b);
9767
9768             // wrap if needed
9769             var wrap = this.fxWrap(r.pos, o, "hidden");
9770
9771             var st = this.dom.style;
9772             st.visibility = "visible";
9773             st.position = "absolute";
9774
9775             // clear out temp styles after slide and unwrap
9776             var after = function(){
9777                 el.fxUnwrap(wrap, r.pos, o);
9778                 st.width = r.width;
9779                 st.height = r.height;
9780                 el.afterFx(o);
9781             };
9782             // time to calc the positions
9783             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
9784
9785             switch(anchor.toLowerCase()){
9786                 case "t":
9787                     wrap.setSize(b.width, 0);
9788                     st.left = st.bottom = "0";
9789                     a = {height: bh};
9790                 break;
9791                 case "l":
9792                     wrap.setSize(0, b.height);
9793                     st.right = st.top = "0";
9794                     a = {width: bw};
9795                 break;
9796                 case "r":
9797                     wrap.setSize(0, b.height);
9798                     wrap.setX(b.right);
9799                     st.left = st.top = "0";
9800                     a = {width: bw, points: pt};
9801                 break;
9802                 case "b":
9803                     wrap.setSize(b.width, 0);
9804                     wrap.setY(b.bottom);
9805                     st.left = st.top = "0";
9806                     a = {height: bh, points: pt};
9807                 break;
9808                 case "tl":
9809                     wrap.setSize(0, 0);
9810                     st.right = st.bottom = "0";
9811                     a = {width: bw, height: bh};
9812                 break;
9813                 case "bl":
9814                     wrap.setSize(0, 0);
9815                     wrap.setY(b.y+b.height);
9816                     st.right = st.top = "0";
9817                     a = {width: bw, height: bh, points: pt};
9818                 break;
9819                 case "br":
9820                     wrap.setSize(0, 0);
9821                     wrap.setXY([b.right, b.bottom]);
9822                     st.left = st.top = "0";
9823                     a = {width: bw, height: bh, points: pt};
9824                 break;
9825                 case "tr":
9826                     wrap.setSize(0, 0);
9827                     wrap.setX(b.x+b.width);
9828                     st.left = st.bottom = "0";
9829                     a = {width: bw, height: bh, points: pt};
9830                 break;
9831             }
9832             this.dom.style.visibility = "visible";
9833             wrap.show();
9834
9835             arguments.callee.anim = wrap.fxanim(a,
9836                 o,
9837                 'motion',
9838                 .5,
9839                 'easeOut', after);
9840         });
9841         return this;
9842     },
9843     
9844         /**
9845          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
9846          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
9847          * 'hidden') but block elements will still take up space in the document.  The element must be removed
9848          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
9849          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9850          * Usage:
9851          *<pre><code>
9852 // default: slide the element out to the top
9853 el.slideOut();
9854
9855 // custom: slide the element out to the right with a 2-second duration
9856 el.slideOut('r', { duration: 2 });
9857
9858 // common config options shown with default values
9859 el.slideOut('t', {
9860     easing: 'easeOut',
9861     duration: .5,
9862     remove: false,
9863     useDisplay: false
9864 });
9865 </code></pre>
9866          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9867          * @param {Object} options (optional) Object literal with any of the Fx config options
9868          * @return {Roo.Element} The Element
9869          */
9870     slideOut : function(anchor, o){
9871         var el = this.getFxEl();
9872         o = o || {};
9873
9874         el.queueFx(o, function(){
9875
9876             anchor = anchor || "t";
9877
9878             // restore values after effect
9879             var r = this.getFxRestore();
9880             
9881             var b = this.getBox();
9882             // fixed size for slide
9883             this.setSize(b);
9884
9885             // wrap if needed
9886             var wrap = this.fxWrap(r.pos, o, "visible");
9887
9888             var st = this.dom.style;
9889             st.visibility = "visible";
9890             st.position = "absolute";
9891
9892             wrap.setSize(b);
9893
9894             var after = function(){
9895                 if(o.useDisplay){
9896                     el.setDisplayed(false);
9897                 }else{
9898                     el.hide();
9899                 }
9900
9901                 el.fxUnwrap(wrap, r.pos, o);
9902
9903                 st.width = r.width;
9904                 st.height = r.height;
9905
9906                 el.afterFx(o);
9907             };
9908
9909             var a, zero = {to: 0};
9910             switch(anchor.toLowerCase()){
9911                 case "t":
9912                     st.left = st.bottom = "0";
9913                     a = {height: zero};
9914                 break;
9915                 case "l":
9916                     st.right = st.top = "0";
9917                     a = {width: zero};
9918                 break;
9919                 case "r":
9920                     st.left = st.top = "0";
9921                     a = {width: zero, points: {to:[b.right, b.y]}};
9922                 break;
9923                 case "b":
9924                     st.left = st.top = "0";
9925                     a = {height: zero, points: {to:[b.x, b.bottom]}};
9926                 break;
9927                 case "tl":
9928                     st.right = st.bottom = "0";
9929                     a = {width: zero, height: zero};
9930                 break;
9931                 case "bl":
9932                     st.right = st.top = "0";
9933                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
9934                 break;
9935                 case "br":
9936                     st.left = st.top = "0";
9937                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
9938                 break;
9939                 case "tr":
9940                     st.left = st.bottom = "0";
9941                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
9942                 break;
9943             }
9944
9945             arguments.callee.anim = wrap.fxanim(a,
9946                 o,
9947                 'motion',
9948                 .5,
9949                 "easeOut", after);
9950         });
9951         return this;
9952     },
9953
9954         /**
9955          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
9956          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
9957          * The element must be removed from the DOM using the 'remove' config option if desired.
9958          * Usage:
9959          *<pre><code>
9960 // default
9961 el.puff();
9962
9963 // common config options shown with default values
9964 el.puff({
9965     easing: 'easeOut',
9966     duration: .5,
9967     remove: false,
9968     useDisplay: false
9969 });
9970 </code></pre>
9971          * @param {Object} options (optional) Object literal with any of the Fx config options
9972          * @return {Roo.Element} The Element
9973          */
9974     puff : function(o){
9975         var el = this.getFxEl();
9976         o = o || {};
9977
9978         el.queueFx(o, function(){
9979             this.clearOpacity();
9980             this.show();
9981
9982             // restore values after effect
9983             var r = this.getFxRestore();
9984             var st = this.dom.style;
9985
9986             var after = function(){
9987                 if(o.useDisplay){
9988                     el.setDisplayed(false);
9989                 }else{
9990                     el.hide();
9991                 }
9992
9993                 el.clearOpacity();
9994
9995                 el.setPositioning(r.pos);
9996                 st.width = r.width;
9997                 st.height = r.height;
9998                 st.fontSize = '';
9999                 el.afterFx(o);
10000             };
10001
10002             var width = this.getWidth();
10003             var height = this.getHeight();
10004
10005             arguments.callee.anim = this.fxanim({
10006                     width : {to: this.adjustWidth(width * 2)},
10007                     height : {to: this.adjustHeight(height * 2)},
10008                     points : {by: [-(width * .5), -(height * .5)]},
10009                     opacity : {to: 0},
10010                     fontSize: {to:200, unit: "%"}
10011                 },
10012                 o,
10013                 'motion',
10014                 .5,
10015                 "easeOut", after);
10016         });
10017         return this;
10018     },
10019
10020         /**
10021          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10022          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10023          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10024          * Usage:
10025          *<pre><code>
10026 // default
10027 el.switchOff();
10028
10029 // all config options shown with default values
10030 el.switchOff({
10031     easing: 'easeIn',
10032     duration: .3,
10033     remove: false,
10034     useDisplay: false
10035 });
10036 </code></pre>
10037          * @param {Object} options (optional) Object literal with any of the Fx config options
10038          * @return {Roo.Element} The Element
10039          */
10040     switchOff : function(o){
10041         var el = this.getFxEl();
10042         o = o || {};
10043
10044         el.queueFx(o, function(){
10045             this.clearOpacity();
10046             this.clip();
10047
10048             // restore values after effect
10049             var r = this.getFxRestore();
10050             var st = this.dom.style;
10051
10052             var after = function(){
10053                 if(o.useDisplay){
10054                     el.setDisplayed(false);
10055                 }else{
10056                     el.hide();
10057                 }
10058
10059                 el.clearOpacity();
10060                 el.setPositioning(r.pos);
10061                 st.width = r.width;
10062                 st.height = r.height;
10063
10064                 el.afterFx(o);
10065             };
10066
10067             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10068                 this.clearOpacity();
10069                 (function(){
10070                     this.fxanim({
10071                         height:{to:1},
10072                         points:{by:[0, this.getHeight() * .5]}
10073                     }, o, 'motion', 0.3, 'easeIn', after);
10074                 }).defer(100, this);
10075             });
10076         });
10077         return this;
10078     },
10079
10080     /**
10081      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10082      * changed using the "attr" config option) and then fading back to the original color. If no original
10083      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10084      * Usage:
10085 <pre><code>
10086 // default: highlight background to yellow
10087 el.highlight();
10088
10089 // custom: highlight foreground text to blue for 2 seconds
10090 el.highlight("0000ff", { attr: 'color', duration: 2 });
10091
10092 // common config options shown with default values
10093 el.highlight("ffff9c", {
10094     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10095     endColor: (current color) or "ffffff",
10096     easing: 'easeIn',
10097     duration: 1
10098 });
10099 </code></pre>
10100      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10101      * @param {Object} options (optional) Object literal with any of the Fx config options
10102      * @return {Roo.Element} The Element
10103      */ 
10104     highlight : function(color, o){
10105         var el = this.getFxEl();
10106         o = o || {};
10107
10108         el.queueFx(o, function(){
10109             color = color || "ffff9c";
10110             attr = o.attr || "backgroundColor";
10111
10112             this.clearOpacity();
10113             this.show();
10114
10115             var origColor = this.getColor(attr);
10116             var restoreColor = this.dom.style[attr];
10117             endColor = (o.endColor || origColor) || "ffffff";
10118
10119             var after = function(){
10120                 el.dom.style[attr] = restoreColor;
10121                 el.afterFx(o);
10122             };
10123
10124             var a = {};
10125             a[attr] = {from: color, to: endColor};
10126             arguments.callee.anim = this.fxanim(a,
10127                 o,
10128                 'color',
10129                 1,
10130                 'easeIn', after);
10131         });
10132         return this;
10133     },
10134
10135    /**
10136     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10137     * Usage:
10138 <pre><code>
10139 // default: a single light blue ripple
10140 el.frame();
10141
10142 // custom: 3 red ripples lasting 3 seconds total
10143 el.frame("ff0000", 3, { duration: 3 });
10144
10145 // common config options shown with default values
10146 el.frame("C3DAF9", 1, {
10147     duration: 1 //duration of entire animation (not each individual ripple)
10148     // Note: Easing is not configurable and will be ignored if included
10149 });
10150 </code></pre>
10151     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10152     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10153     * @param {Object} options (optional) Object literal with any of the Fx config options
10154     * @return {Roo.Element} The Element
10155     */
10156     frame : function(color, count, o){
10157         var el = this.getFxEl();
10158         o = o || {};
10159
10160         el.queueFx(o, function(){
10161             color = color || "#C3DAF9";
10162             if(color.length == 6){
10163                 color = "#" + color;
10164             }
10165             count = count || 1;
10166             duration = o.duration || 1;
10167             this.show();
10168
10169             var b = this.getBox();
10170             var animFn = function(){
10171                 var proxy = this.createProxy({
10172
10173                      style:{
10174                         visbility:"hidden",
10175                         position:"absolute",
10176                         "z-index":"35000", // yee haw
10177                         border:"0px solid " + color
10178                      }
10179                   });
10180                 var scale = Roo.isBorderBox ? 2 : 1;
10181                 proxy.animate({
10182                     top:{from:b.y, to:b.y - 20},
10183                     left:{from:b.x, to:b.x - 20},
10184                     borderWidth:{from:0, to:10},
10185                     opacity:{from:1, to:0},
10186                     height:{from:b.height, to:(b.height + (20*scale))},
10187                     width:{from:b.width, to:(b.width + (20*scale))}
10188                 }, duration, function(){
10189                     proxy.remove();
10190                 });
10191                 if(--count > 0){
10192                      animFn.defer((duration/2)*1000, this);
10193                 }else{
10194                     el.afterFx(o);
10195                 }
10196             };
10197             animFn.call(this);
10198         });
10199         return this;
10200     },
10201
10202    /**
10203     * Creates a pause before any subsequent queued effects begin.  If there are
10204     * no effects queued after the pause it will have no effect.
10205     * Usage:
10206 <pre><code>
10207 el.pause(1);
10208 </code></pre>
10209     * @param {Number} seconds The length of time to pause (in seconds)
10210     * @return {Roo.Element} The Element
10211     */
10212     pause : function(seconds){
10213         var el = this.getFxEl();
10214         var o = {};
10215
10216         el.queueFx(o, function(){
10217             setTimeout(function(){
10218                 el.afterFx(o);
10219             }, seconds * 1000);
10220         });
10221         return this;
10222     },
10223
10224    /**
10225     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10226     * using the "endOpacity" config option.
10227     * Usage:
10228 <pre><code>
10229 // default: fade in from opacity 0 to 100%
10230 el.fadeIn();
10231
10232 // custom: fade in from opacity 0 to 75% over 2 seconds
10233 el.fadeIn({ endOpacity: .75, duration: 2});
10234
10235 // common config options shown with default values
10236 el.fadeIn({
10237     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10238     easing: 'easeOut',
10239     duration: .5
10240 });
10241 </code></pre>
10242     * @param {Object} options (optional) Object literal with any of the Fx config options
10243     * @return {Roo.Element} The Element
10244     */
10245     fadeIn : function(o){
10246         var el = this.getFxEl();
10247         o = o || {};
10248         el.queueFx(o, function(){
10249             this.setOpacity(0);
10250             this.fixDisplay();
10251             this.dom.style.visibility = 'visible';
10252             var to = o.endOpacity || 1;
10253             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10254                 o, null, .5, "easeOut", function(){
10255                 if(to == 1){
10256                     this.clearOpacity();
10257                 }
10258                 el.afterFx(o);
10259             });
10260         });
10261         return this;
10262     },
10263
10264    /**
10265     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10266     * using the "endOpacity" config option.
10267     * Usage:
10268 <pre><code>
10269 // default: fade out from the element's current opacity to 0
10270 el.fadeOut();
10271
10272 // custom: fade out from the element's current opacity to 25% over 2 seconds
10273 el.fadeOut({ endOpacity: .25, duration: 2});
10274
10275 // common config options shown with default values
10276 el.fadeOut({
10277     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10278     easing: 'easeOut',
10279     duration: .5
10280     remove: false,
10281     useDisplay: false
10282 });
10283 </code></pre>
10284     * @param {Object} options (optional) Object literal with any of the Fx config options
10285     * @return {Roo.Element} The Element
10286     */
10287     fadeOut : function(o){
10288         var el = this.getFxEl();
10289         o = o || {};
10290         el.queueFx(o, function(){
10291             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10292                 o, null, .5, "easeOut", function(){
10293                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10294                      this.dom.style.display = "none";
10295                 }else{
10296                      this.dom.style.visibility = "hidden";
10297                 }
10298                 this.clearOpacity();
10299                 el.afterFx(o);
10300             });
10301         });
10302         return this;
10303     },
10304
10305    /**
10306     * Animates the transition of an element's dimensions from a starting height/width
10307     * to an ending height/width.
10308     * Usage:
10309 <pre><code>
10310 // change height and width to 100x100 pixels
10311 el.scale(100, 100);
10312
10313 // common config options shown with default values.  The height and width will default to
10314 // the element's existing values if passed as null.
10315 el.scale(
10316     [element's width],
10317     [element's height], {
10318     easing: 'easeOut',
10319     duration: .35
10320 });
10321 </code></pre>
10322     * @param {Number} width  The new width (pass undefined to keep the original width)
10323     * @param {Number} height  The new height (pass undefined to keep the original height)
10324     * @param {Object} options (optional) Object literal with any of the Fx config options
10325     * @return {Roo.Element} The Element
10326     */
10327     scale : function(w, h, o){
10328         this.shift(Roo.apply({}, o, {
10329             width: w,
10330             height: h
10331         }));
10332         return this;
10333     },
10334
10335    /**
10336     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10337     * Any of these properties not specified in the config object will not be changed.  This effect 
10338     * requires that at least one new dimension, position or opacity setting must be passed in on
10339     * the config object in order for the function to have any effect.
10340     * Usage:
10341 <pre><code>
10342 // slide the element horizontally to x position 200 while changing the height and opacity
10343 el.shift({ x: 200, height: 50, opacity: .8 });
10344
10345 // common config options shown with default values.
10346 el.shift({
10347     width: [element's width],
10348     height: [element's height],
10349     x: [element's x position],
10350     y: [element's y position],
10351     opacity: [element's opacity],
10352     easing: 'easeOut',
10353     duration: .35
10354 });
10355 </code></pre>
10356     * @param {Object} options  Object literal with any of the Fx config options
10357     * @return {Roo.Element} The Element
10358     */
10359     shift : function(o){
10360         var el = this.getFxEl();
10361         o = o || {};
10362         el.queueFx(o, function(){
10363             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10364             if(w !== undefined){
10365                 a.width = {to: this.adjustWidth(w)};
10366             }
10367             if(h !== undefined){
10368                 a.height = {to: this.adjustHeight(h)};
10369             }
10370             if(x !== undefined || y !== undefined){
10371                 a.points = {to: [
10372                     x !== undefined ? x : this.getX(),
10373                     y !== undefined ? y : this.getY()
10374                 ]};
10375             }
10376             if(op !== undefined){
10377                 a.opacity = {to: op};
10378             }
10379             if(o.xy !== undefined){
10380                 a.points = {to: o.xy};
10381             }
10382             arguments.callee.anim = this.fxanim(a,
10383                 o, 'motion', .35, "easeOut", function(){
10384                 el.afterFx(o);
10385             });
10386         });
10387         return this;
10388     },
10389
10390         /**
10391          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10392          * ending point of the effect.
10393          * Usage:
10394          *<pre><code>
10395 // default: slide the element downward while fading out
10396 el.ghost();
10397
10398 // custom: slide the element out to the right with a 2-second duration
10399 el.ghost('r', { duration: 2 });
10400
10401 // common config options shown with default values
10402 el.ghost('b', {
10403     easing: 'easeOut',
10404     duration: .5
10405     remove: false,
10406     useDisplay: false
10407 });
10408 </code></pre>
10409          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10410          * @param {Object} options (optional) Object literal with any of the Fx config options
10411          * @return {Roo.Element} The Element
10412          */
10413     ghost : function(anchor, o){
10414         var el = this.getFxEl();
10415         o = o || {};
10416
10417         el.queueFx(o, function(){
10418             anchor = anchor || "b";
10419
10420             // restore values after effect
10421             var r = this.getFxRestore();
10422             var w = this.getWidth(),
10423                 h = this.getHeight();
10424
10425             var st = this.dom.style;
10426
10427             var after = function(){
10428                 if(o.useDisplay){
10429                     el.setDisplayed(false);
10430                 }else{
10431                     el.hide();
10432                 }
10433
10434                 el.clearOpacity();
10435                 el.setPositioning(r.pos);
10436                 st.width = r.width;
10437                 st.height = r.height;
10438
10439                 el.afterFx(o);
10440             };
10441
10442             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10443             switch(anchor.toLowerCase()){
10444                 case "t":
10445                     pt.by = [0, -h];
10446                 break;
10447                 case "l":
10448                     pt.by = [-w, 0];
10449                 break;
10450                 case "r":
10451                     pt.by = [w, 0];
10452                 break;
10453                 case "b":
10454                     pt.by = [0, h];
10455                 break;
10456                 case "tl":
10457                     pt.by = [-w, -h];
10458                 break;
10459                 case "bl":
10460                     pt.by = [-w, h];
10461                 break;
10462                 case "br":
10463                     pt.by = [w, h];
10464                 break;
10465                 case "tr":
10466                     pt.by = [w, -h];
10467                 break;
10468             }
10469
10470             arguments.callee.anim = this.fxanim(a,
10471                 o,
10472                 'motion',
10473                 .5,
10474                 "easeOut", after);
10475         });
10476         return this;
10477     },
10478
10479         /**
10480          * Ensures that all effects queued after syncFx is called on the element are
10481          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10482          * @return {Roo.Element} The Element
10483          */
10484     syncFx : function(){
10485         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10486             block : false,
10487             concurrent : true,
10488             stopFx : false
10489         });
10490         return this;
10491     },
10492
10493         /**
10494          * Ensures that all effects queued after sequenceFx is called on the element are
10495          * run in sequence.  This is the opposite of {@link #syncFx}.
10496          * @return {Roo.Element} The Element
10497          */
10498     sequenceFx : function(){
10499         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10500             block : false,
10501             concurrent : false,
10502             stopFx : false
10503         });
10504         return this;
10505     },
10506
10507         /* @private */
10508     nextFx : function(){
10509         var ef = this.fxQueue[0];
10510         if(ef){
10511             ef.call(this);
10512         }
10513     },
10514
10515         /**
10516          * Returns true if the element has any effects actively running or queued, else returns false.
10517          * @return {Boolean} True if element has active effects, else false
10518          */
10519     hasActiveFx : function(){
10520         return this.fxQueue && this.fxQueue[0];
10521     },
10522
10523         /**
10524          * Stops any running effects and clears the element's internal effects queue if it contains
10525          * any additional effects that haven't started yet.
10526          * @return {Roo.Element} The Element
10527          */
10528     stopFx : function(){
10529         if(this.hasActiveFx()){
10530             var cur = this.fxQueue[0];
10531             if(cur && cur.anim && cur.anim.isAnimated()){
10532                 this.fxQueue = [cur]; // clear out others
10533                 cur.anim.stop(true);
10534             }
10535         }
10536         return this;
10537     },
10538
10539         /* @private */
10540     beforeFx : function(o){
10541         if(this.hasActiveFx() && !o.concurrent){
10542            if(o.stopFx){
10543                this.stopFx();
10544                return true;
10545            }
10546            return false;
10547         }
10548         return true;
10549     },
10550
10551         /**
10552          * Returns true if the element is currently blocking so that no other effect can be queued
10553          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10554          * used to ensure that an effect initiated by a user action runs to completion prior to the
10555          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10556          * @return {Boolean} True if blocking, else false
10557          */
10558     hasFxBlock : function(){
10559         var q = this.fxQueue;
10560         return q && q[0] && q[0].block;
10561     },
10562
10563         /* @private */
10564     queueFx : function(o, fn){
10565         if(!this.fxQueue){
10566             this.fxQueue = [];
10567         }
10568         if(!this.hasFxBlock()){
10569             Roo.applyIf(o, this.fxDefaults);
10570             if(!o.concurrent){
10571                 var run = this.beforeFx(o);
10572                 fn.block = o.block;
10573                 this.fxQueue.push(fn);
10574                 if(run){
10575                     this.nextFx();
10576                 }
10577             }else{
10578                 fn.call(this);
10579             }
10580         }
10581         return this;
10582     },
10583
10584         /* @private */
10585     fxWrap : function(pos, o, vis){
10586         var wrap;
10587         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10588             var wrapXY;
10589             if(o.fixPosition){
10590                 wrapXY = this.getXY();
10591             }
10592             var div = document.createElement("div");
10593             div.style.visibility = vis;
10594             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10595             wrap.setPositioning(pos);
10596             if(wrap.getStyle("position") == "static"){
10597                 wrap.position("relative");
10598             }
10599             this.clearPositioning('auto');
10600             wrap.clip();
10601             wrap.dom.appendChild(this.dom);
10602             if(wrapXY){
10603                 wrap.setXY(wrapXY);
10604             }
10605         }
10606         return wrap;
10607     },
10608
10609         /* @private */
10610     fxUnwrap : function(wrap, pos, o){
10611         this.clearPositioning();
10612         this.setPositioning(pos);
10613         if(!o.wrap){
10614             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10615             wrap.remove();
10616         }
10617     },
10618
10619         /* @private */
10620     getFxRestore : function(){
10621         var st = this.dom.style;
10622         return {pos: this.getPositioning(), width: st.width, height : st.height};
10623     },
10624
10625         /* @private */
10626     afterFx : function(o){
10627         if(o.afterStyle){
10628             this.applyStyles(o.afterStyle);
10629         }
10630         if(o.afterCls){
10631             this.addClass(o.afterCls);
10632         }
10633         if(o.remove === true){
10634             this.remove();
10635         }
10636         Roo.callback(o.callback, o.scope, [this]);
10637         if(!o.concurrent){
10638             this.fxQueue.shift();
10639             this.nextFx();
10640         }
10641     },
10642
10643         /* @private */
10644     getFxEl : function(){ // support for composite element fx
10645         return Roo.get(this.dom);
10646     },
10647
10648         /* @private */
10649     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10650         animType = animType || 'run';
10651         opt = opt || {};
10652         var anim = Roo.lib.Anim[animType](
10653             this.dom, args,
10654             (opt.duration || defaultDur) || .35,
10655             (opt.easing || defaultEase) || 'easeOut',
10656             function(){
10657                 Roo.callback(cb, this);
10658             },
10659             this
10660         );
10661         opt.anim = anim;
10662         return anim;
10663     }
10664 };
10665
10666 // backwords compat
10667 Roo.Fx.resize = Roo.Fx.scale;
10668
10669 //When included, Roo.Fx is automatically applied to Element so that all basic
10670 //effects are available directly via the Element API
10671 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10672  * Based on:
10673  * Ext JS Library 1.1.1
10674  * Copyright(c) 2006-2007, Ext JS, LLC.
10675  *
10676  * Originally Released Under LGPL - original licence link has changed is not relivant.
10677  *
10678  * Fork - LGPL
10679  * <script type="text/javascript">
10680  */
10681
10682
10683 /**
10684  * @class Roo.CompositeElement
10685  * Standard composite class. Creates a Roo.Element for every element in the collection.
10686  * <br><br>
10687  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10688  * actions will be performed on all the elements in this collection.</b>
10689  * <br><br>
10690  * All methods return <i>this</i> and can be chained.
10691  <pre><code>
10692  var els = Roo.select("#some-el div.some-class", true);
10693  // or select directly from an existing element
10694  var el = Roo.get('some-el');
10695  el.select('div.some-class', true);
10696
10697  els.setWidth(100); // all elements become 100 width
10698  els.hide(true); // all elements fade out and hide
10699  // or
10700  els.setWidth(100).hide(true);
10701  </code></pre>
10702  */
10703 Roo.CompositeElement = function(els){
10704     this.elements = [];
10705     this.addElements(els);
10706 };
10707 Roo.CompositeElement.prototype = {
10708     isComposite: true,
10709     addElements : function(els){
10710         if(!els) return this;
10711         if(typeof els == "string"){
10712             els = Roo.Element.selectorFunction(els);
10713         }
10714         var yels = this.elements;
10715         var index = yels.length-1;
10716         for(var i = 0, len = els.length; i < len; i++) {
10717                 yels[++index] = Roo.get(els[i]);
10718         }
10719         return this;
10720     },
10721
10722     /**
10723     * Clears this composite and adds the elements returned by the passed selector.
10724     * @param {String/Array} els A string CSS selector, an array of elements or an element
10725     * @return {CompositeElement} this
10726     */
10727     fill : function(els){
10728         this.elements = [];
10729         this.add(els);
10730         return this;
10731     },
10732
10733     /**
10734     * Filters this composite to only elements that match the passed selector.
10735     * @param {String} selector A string CSS selector
10736     * @return {CompositeElement} this
10737     */
10738     filter : function(selector){
10739         var els = [];
10740         this.each(function(el){
10741             if(el.is(selector)){
10742                 els[els.length] = el.dom;
10743             }
10744         });
10745         this.fill(els);
10746         return this;
10747     },
10748
10749     invoke : function(fn, args){
10750         var els = this.elements;
10751         for(var i = 0, len = els.length; i < len; i++) {
10752                 Roo.Element.prototype[fn].apply(els[i], args);
10753         }
10754         return this;
10755     },
10756     /**
10757     * Adds elements to this composite.
10758     * @param {String/Array} els A string CSS selector, an array of elements or an element
10759     * @return {CompositeElement} this
10760     */
10761     add : function(els){
10762         if(typeof els == "string"){
10763             this.addElements(Roo.Element.selectorFunction(els));
10764         }else if(els.length !== undefined){
10765             this.addElements(els);
10766         }else{
10767             this.addElements([els]);
10768         }
10769         return this;
10770     },
10771     /**
10772     * Calls the passed function passing (el, this, index) for each element in this composite.
10773     * @param {Function} fn The function to call
10774     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10775     * @return {CompositeElement} this
10776     */
10777     each : function(fn, scope){
10778         var els = this.elements;
10779         for(var i = 0, len = els.length; i < len; i++){
10780             if(fn.call(scope || els[i], els[i], this, i) === false) {
10781                 break;
10782             }
10783         }
10784         return this;
10785     },
10786
10787     /**
10788      * Returns the Element object at the specified index
10789      * @param {Number} index
10790      * @return {Roo.Element}
10791      */
10792     item : function(index){
10793         return this.elements[index] || null;
10794     },
10795
10796     /**
10797      * Returns the first Element
10798      * @return {Roo.Element}
10799      */
10800     first : function(){
10801         return this.item(0);
10802     },
10803
10804     /**
10805      * Returns the last Element
10806      * @return {Roo.Element}
10807      */
10808     last : function(){
10809         return this.item(this.elements.length-1);
10810     },
10811
10812     /**
10813      * Returns the number of elements in this composite
10814      * @return Number
10815      */
10816     getCount : function(){
10817         return this.elements.length;
10818     },
10819
10820     /**
10821      * Returns true if this composite contains the passed element
10822      * @return Boolean
10823      */
10824     contains : function(el){
10825         return this.indexOf(el) !== -1;
10826     },
10827
10828     /**
10829      * Returns true if this composite contains the passed element
10830      * @return Boolean
10831      */
10832     indexOf : function(el){
10833         return this.elements.indexOf(Roo.get(el));
10834     },
10835
10836
10837     /**
10838     * Removes the specified element(s).
10839     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
10840     * or an array of any of those.
10841     * @param {Boolean} removeDom (optional) True to also remove the element from the document
10842     * @return {CompositeElement} this
10843     */
10844     removeElement : function(el, removeDom){
10845         if(el instanceof Array){
10846             for(var i = 0, len = el.length; i < len; i++){
10847                 this.removeElement(el[i]);
10848             }
10849             return this;
10850         }
10851         var index = typeof el == 'number' ? el : this.indexOf(el);
10852         if(index !== -1){
10853             if(removeDom){
10854                 var d = this.elements[index];
10855                 if(d.dom){
10856                     d.remove();
10857                 }else{
10858                     d.parentNode.removeChild(d);
10859                 }
10860             }
10861             this.elements.splice(index, 1);
10862         }
10863         return this;
10864     },
10865
10866     /**
10867     * Replaces the specified element with the passed element.
10868     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
10869     * to replace.
10870     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
10871     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
10872     * @return {CompositeElement} this
10873     */
10874     replaceElement : function(el, replacement, domReplace){
10875         var index = typeof el == 'number' ? el : this.indexOf(el);
10876         if(index !== -1){
10877             if(domReplace){
10878                 this.elements[index].replaceWith(replacement);
10879             }else{
10880                 this.elements.splice(index, 1, Roo.get(replacement))
10881             }
10882         }
10883         return this;
10884     },
10885
10886     /**
10887      * Removes all elements.
10888      */
10889     clear : function(){
10890         this.elements = [];
10891     }
10892 };
10893 (function(){
10894     Roo.CompositeElement.createCall = function(proto, fnName){
10895         if(!proto[fnName]){
10896             proto[fnName] = function(){
10897                 return this.invoke(fnName, arguments);
10898             };
10899         }
10900     };
10901     for(var fnName in Roo.Element.prototype){
10902         if(typeof Roo.Element.prototype[fnName] == "function"){
10903             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
10904         }
10905     };
10906 })();
10907 /*
10908  * Based on:
10909  * Ext JS Library 1.1.1
10910  * Copyright(c) 2006-2007, Ext JS, LLC.
10911  *
10912  * Originally Released Under LGPL - original licence link has changed is not relivant.
10913  *
10914  * Fork - LGPL
10915  * <script type="text/javascript">
10916  */
10917
10918 /**
10919  * @class Roo.CompositeElementLite
10920  * @extends Roo.CompositeElement
10921  * Flyweight composite class. Reuses the same Roo.Element for element operations.
10922  <pre><code>
10923  var els = Roo.select("#some-el div.some-class");
10924  // or select directly from an existing element
10925  var el = Roo.get('some-el');
10926  el.select('div.some-class');
10927
10928  els.setWidth(100); // all elements become 100 width
10929  els.hide(true); // all elements fade out and hide
10930  // or
10931  els.setWidth(100).hide(true);
10932  </code></pre><br><br>
10933  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10934  * actions will be performed on all the elements in this collection.</b>
10935  */
10936 Roo.CompositeElementLite = function(els){
10937     Roo.CompositeElementLite.superclass.constructor.call(this, els);
10938     this.el = new Roo.Element.Flyweight();
10939 };
10940 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
10941     addElements : function(els){
10942         if(els){
10943             if(els instanceof Array){
10944                 this.elements = this.elements.concat(els);
10945             }else{
10946                 var yels = this.elements;
10947                 var index = yels.length-1;
10948                 for(var i = 0, len = els.length; i < len; i++) {
10949                     yels[++index] = els[i];
10950                 }
10951             }
10952         }
10953         return this;
10954     },
10955     invoke : function(fn, args){
10956         var els = this.elements;
10957         var el = this.el;
10958         for(var i = 0, len = els.length; i < len; i++) {
10959             el.dom = els[i];
10960                 Roo.Element.prototype[fn].apply(el, args);
10961         }
10962         return this;
10963     },
10964     /**
10965      * Returns a flyweight Element of the dom element object at the specified index
10966      * @param {Number} index
10967      * @return {Roo.Element}
10968      */
10969     item : function(index){
10970         if(!this.elements[index]){
10971             return null;
10972         }
10973         this.el.dom = this.elements[index];
10974         return this.el;
10975     },
10976
10977     // fixes scope with flyweight
10978     addListener : function(eventName, handler, scope, opt){
10979         var els = this.elements;
10980         for(var i = 0, len = els.length; i < len; i++) {
10981             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
10982         }
10983         return this;
10984     },
10985
10986     /**
10987     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
10988     * passed is the flyweight (shared) Roo.Element instance, so if you require a
10989     * a reference to the dom node, use el.dom.</b>
10990     * @param {Function} fn The function to call
10991     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10992     * @return {CompositeElement} this
10993     */
10994     each : function(fn, scope){
10995         var els = this.elements;
10996         var el = this.el;
10997         for(var i = 0, len = els.length; i < len; i++){
10998             el.dom = els[i];
10999                 if(fn.call(scope || el, el, this, i) === false){
11000                 break;
11001             }
11002         }
11003         return this;
11004     },
11005
11006     indexOf : function(el){
11007         return this.elements.indexOf(Roo.getDom(el));
11008     },
11009
11010     replaceElement : function(el, replacement, domReplace){
11011         var index = typeof el == 'number' ? el : this.indexOf(el);
11012         if(index !== -1){
11013             replacement = Roo.getDom(replacement);
11014             if(domReplace){
11015                 var d = this.elements[index];
11016                 d.parentNode.insertBefore(replacement, d);
11017                 d.parentNode.removeChild(d);
11018             }
11019             this.elements.splice(index, 1, replacement);
11020         }
11021         return this;
11022     }
11023 });
11024 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11025
11026 /*
11027  * Based on:
11028  * Ext JS Library 1.1.1
11029  * Copyright(c) 2006-2007, Ext JS, LLC.
11030  *
11031  * Originally Released Under LGPL - original licence link has changed is not relivant.
11032  *
11033  * Fork - LGPL
11034  * <script type="text/javascript">
11035  */
11036
11037  
11038
11039 /**
11040  * @class Roo.data.Connection
11041  * @extends Roo.util.Observable
11042  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11043  * either to a configured URL, or to a URL specified at request time.<br><br>
11044  * <p>
11045  * Requests made by this class are asynchronous, and will return immediately. No data from
11046  * the server will be available to the statement immediately following the {@link #request} call.
11047  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11048  * <p>
11049  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11050  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11051  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11052  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11053  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11054  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11055  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11056  * standard DOM methods.
11057  * @constructor
11058  * @param {Object} config a configuration object.
11059  */
11060 Roo.data.Connection = function(config){
11061     Roo.apply(this, config);
11062     this.addEvents({
11063         /**
11064          * @event beforerequest
11065          * Fires before a network request is made to retrieve a data object.
11066          * @param {Connection} conn This Connection object.
11067          * @param {Object} options The options config object passed to the {@link #request} method.
11068          */
11069         "beforerequest" : true,
11070         /**
11071          * @event requestcomplete
11072          * Fires if the request was successfully completed.
11073          * @param {Connection} conn This Connection object.
11074          * @param {Object} response The XHR object containing the response data.
11075          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11076          * @param {Object} options The options config object passed to the {@link #request} method.
11077          */
11078         "requestcomplete" : true,
11079         /**
11080          * @event requestexception
11081          * Fires if an error HTTP status was returned from the server.
11082          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11083          * @param {Connection} conn This Connection object.
11084          * @param {Object} response The XHR object containing the response data.
11085          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11086          * @param {Object} options The options config object passed to the {@link #request} method.
11087          */
11088         "requestexception" : true
11089     });
11090     Roo.data.Connection.superclass.constructor.call(this);
11091 };
11092
11093 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11094     /**
11095      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11096      */
11097     /**
11098      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11099      * extra parameters to each request made by this object. (defaults to undefined)
11100      */
11101     /**
11102      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11103      *  to each request made by this object. (defaults to undefined)
11104      */
11105     /**
11106      * @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)
11107      */
11108     /**
11109      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11110      */
11111     timeout : 30000,
11112     /**
11113      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11114      * @type Boolean
11115      */
11116     autoAbort:false,
11117
11118     /**
11119      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11120      * @type Boolean
11121      */
11122     disableCaching: true,
11123
11124     /**
11125      * Sends an HTTP request to a remote server.
11126      * @param {Object} options An object which may contain the following properties:<ul>
11127      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11128      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11129      * request, a url encoded string or a function to call to get either.</li>
11130      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11131      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11132      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11133      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11134      * <li>options {Object} The parameter to the request call.</li>
11135      * <li>success {Boolean} True if the request succeeded.</li>
11136      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11137      * </ul></li>
11138      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11139      * The callback is passed the following parameters:<ul>
11140      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11141      * <li>options {Object} The parameter to the request call.</li>
11142      * </ul></li>
11143      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11144      * The callback is passed the following parameters:<ul>
11145      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11146      * <li>options {Object} The parameter to the request call.</li>
11147      * </ul></li>
11148      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11149      * for the callback function. Defaults to the browser window.</li>
11150      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11151      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11152      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11153      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11154      * params for the post data. Any params will be appended to the URL.</li>
11155      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11156      * </ul>
11157      * @return {Number} transactionId
11158      */
11159     request : function(o){
11160         if(this.fireEvent("beforerequest", this, o) !== false){
11161             var p = o.params;
11162
11163             if(typeof p == "function"){
11164                 p = p.call(o.scope||window, o);
11165             }
11166             if(typeof p == "object"){
11167                 p = Roo.urlEncode(o.params);
11168             }
11169             if(this.extraParams){
11170                 var extras = Roo.urlEncode(this.extraParams);
11171                 p = p ? (p + '&' + extras) : extras;
11172             }
11173
11174             var url = o.url || this.url;
11175             if(typeof url == 'function'){
11176                 url = url.call(o.scope||window, o);
11177             }
11178
11179             if(o.form){
11180                 var form = Roo.getDom(o.form);
11181                 url = url || form.action;
11182
11183                 var enctype = form.getAttribute("enctype");
11184                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11185                     return this.doFormUpload(o, p, url);
11186                 }
11187                 var f = Roo.lib.Ajax.serializeForm(form);
11188                 p = p ? (p + '&' + f) : f;
11189             }
11190
11191             var hs = o.headers;
11192             if(this.defaultHeaders){
11193                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11194                 if(!o.headers){
11195                     o.headers = hs;
11196                 }
11197             }
11198
11199             var cb = {
11200                 success: this.handleResponse,
11201                 failure: this.handleFailure,
11202                 scope: this,
11203                 argument: {options: o},
11204                 timeout : this.timeout
11205             };
11206
11207             var method = o.method||this.method||(p ? "POST" : "GET");
11208
11209             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11210                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11211             }
11212
11213             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11214                 if(o.autoAbort){
11215                     this.abort();
11216                 }
11217             }else if(this.autoAbort !== false){
11218                 this.abort();
11219             }
11220
11221             if((method == 'GET' && p) || o.xmlData){
11222                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11223                 p = '';
11224             }
11225             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11226             return this.transId;
11227         }else{
11228             Roo.callback(o.callback, o.scope, [o, null, null]);
11229             return null;
11230         }
11231     },
11232
11233     /**
11234      * Determine whether this object has a request outstanding.
11235      * @param {Number} transactionId (Optional) defaults to the last transaction
11236      * @return {Boolean} True if there is an outstanding request.
11237      */
11238     isLoading : function(transId){
11239         if(transId){
11240             return Roo.lib.Ajax.isCallInProgress(transId);
11241         }else{
11242             return this.transId ? true : false;
11243         }
11244     },
11245
11246     /**
11247      * Aborts any outstanding request.
11248      * @param {Number} transactionId (Optional) defaults to the last transaction
11249      */
11250     abort : function(transId){
11251         if(transId || this.isLoading()){
11252             Roo.lib.Ajax.abort(transId || this.transId);
11253         }
11254     },
11255
11256     // private
11257     handleResponse : function(response){
11258         this.transId = false;
11259         var options = response.argument.options;
11260         response.argument = options ? options.argument : null;
11261         this.fireEvent("requestcomplete", this, response, options);
11262         Roo.callback(options.success, options.scope, [response, options]);
11263         Roo.callback(options.callback, options.scope, [options, true, response]);
11264     },
11265
11266     // private
11267     handleFailure : function(response, e){
11268         this.transId = false;
11269         var options = response.argument.options;
11270         response.argument = options ? options.argument : null;
11271         this.fireEvent("requestexception", this, response, options, e);
11272         Roo.callback(options.failure, options.scope, [response, options]);
11273         Roo.callback(options.callback, options.scope, [options, false, response]);
11274     },
11275
11276     // private
11277     doFormUpload : function(o, ps, url){
11278         var id = Roo.id();
11279         var frame = document.createElement('iframe');
11280         frame.id = id;
11281         frame.name = id;
11282         frame.className = 'x-hidden';
11283         if(Roo.isIE){
11284             frame.src = Roo.SSL_SECURE_URL;
11285         }
11286         document.body.appendChild(frame);
11287
11288         if(Roo.isIE){
11289            document.frames[id].name = id;
11290         }
11291
11292         var form = Roo.getDom(o.form);
11293         form.target = id;
11294         form.method = 'POST';
11295         form.enctype = form.encoding = 'multipart/form-data';
11296         if(url){
11297             form.action = url;
11298         }
11299
11300         var hiddens, hd;
11301         if(ps){ // add dynamic params
11302             hiddens = [];
11303             ps = Roo.urlDecode(ps, false);
11304             for(var k in ps){
11305                 if(ps.hasOwnProperty(k)){
11306                     hd = document.createElement('input');
11307                     hd.type = 'hidden';
11308                     hd.name = k;
11309                     hd.value = ps[k];
11310                     form.appendChild(hd);
11311                     hiddens.push(hd);
11312                 }
11313             }
11314         }
11315
11316         function cb(){
11317             var r = {  // bogus response object
11318                 responseText : '',
11319                 responseXML : null
11320             };
11321
11322             r.argument = o ? o.argument : null;
11323
11324             try { //
11325                 var doc;
11326                 if(Roo.isIE){
11327                     doc = frame.contentWindow.document;
11328                 }else {
11329                     doc = (frame.contentDocument || window.frames[id].document);
11330                 }
11331                 if(doc && doc.body){
11332                     r.responseText = doc.body.innerHTML;
11333                 }
11334                 if(doc && doc.XMLDocument){
11335                     r.responseXML = doc.XMLDocument;
11336                 }else {
11337                     r.responseXML = doc;
11338                 }
11339             }
11340             catch(e) {
11341                 // ignore
11342             }
11343
11344             Roo.EventManager.removeListener(frame, 'load', cb, this);
11345
11346             this.fireEvent("requestcomplete", this, r, o);
11347             Roo.callback(o.success, o.scope, [r, o]);
11348             Roo.callback(o.callback, o.scope, [o, true, r]);
11349
11350             setTimeout(function(){document.body.removeChild(frame);}, 100);
11351         }
11352
11353         Roo.EventManager.on(frame, 'load', cb, this);
11354         form.submit();
11355
11356         if(hiddens){ // remove dynamic params
11357             for(var i = 0, len = hiddens.length; i < len; i++){
11358                 form.removeChild(hiddens[i]);
11359             }
11360         }
11361     }
11362 });
11363
11364 /**
11365  * @class Roo.Ajax
11366  * @extends Roo.data.Connection
11367  * Global Ajax request class.
11368  *
11369  * @singleton
11370  */
11371 Roo.Ajax = new Roo.data.Connection({
11372     // fix up the docs
11373    /**
11374      * @cfg {String} url @hide
11375      */
11376     /**
11377      * @cfg {Object} extraParams @hide
11378      */
11379     /**
11380      * @cfg {Object} defaultHeaders @hide
11381      */
11382     /**
11383      * @cfg {String} method (Optional) @hide
11384      */
11385     /**
11386      * @cfg {Number} timeout (Optional) @hide
11387      */
11388     /**
11389      * @cfg {Boolean} autoAbort (Optional) @hide
11390      */
11391
11392     /**
11393      * @cfg {Boolean} disableCaching (Optional) @hide
11394      */
11395
11396     /**
11397      * @property  disableCaching
11398      * True to add a unique cache-buster param to GET requests. (defaults to true)
11399      * @type Boolean
11400      */
11401     /**
11402      * @property  url
11403      * The default URL to be used for requests to the server. (defaults to undefined)
11404      * @type String
11405      */
11406     /**
11407      * @property  extraParams
11408      * An object containing properties which are used as
11409      * extra parameters to each request made by this object. (defaults to undefined)
11410      * @type Object
11411      */
11412     /**
11413      * @property  defaultHeaders
11414      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11415      * @type Object
11416      */
11417     /**
11418      * @property  method
11419      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11420      * @type String
11421      */
11422     /**
11423      * @property  timeout
11424      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11425      * @type Number
11426      */
11427
11428     /**
11429      * @property  autoAbort
11430      * Whether a new request should abort any pending requests. (defaults to false)
11431      * @type Boolean
11432      */
11433     autoAbort : false,
11434
11435     /**
11436      * Serialize the passed form into a url encoded string
11437      * @param {String/HTMLElement} form
11438      * @return {String}
11439      */
11440     serializeForm : function(form){
11441         return Roo.lib.Ajax.serializeForm(form);
11442     }
11443 });/*
11444  * Based on:
11445  * Ext JS Library 1.1.1
11446  * Copyright(c) 2006-2007, Ext JS, LLC.
11447  *
11448  * Originally Released Under LGPL - original licence link has changed is not relivant.
11449  *
11450  * Fork - LGPL
11451  * <script type="text/javascript">
11452  */
11453  
11454 /**
11455  * @class Roo.Ajax
11456  * @extends Roo.data.Connection
11457  * Global Ajax request class.
11458  *
11459  * @instanceOf  Roo.data.Connection
11460  */
11461 Roo.Ajax = new Roo.data.Connection({
11462     // fix up the docs
11463     
11464     /**
11465      * fix up scoping
11466      * @scope Roo.Ajax
11467      */
11468     
11469    /**
11470      * @cfg {String} url @hide
11471      */
11472     /**
11473      * @cfg {Object} extraParams @hide
11474      */
11475     /**
11476      * @cfg {Object} defaultHeaders @hide
11477      */
11478     /**
11479      * @cfg {String} method (Optional) @hide
11480      */
11481     /**
11482      * @cfg {Number} timeout (Optional) @hide
11483      */
11484     /**
11485      * @cfg {Boolean} autoAbort (Optional) @hide
11486      */
11487
11488     /**
11489      * @cfg {Boolean} disableCaching (Optional) @hide
11490      */
11491
11492     /**
11493      * @property  disableCaching
11494      * True to add a unique cache-buster param to GET requests. (defaults to true)
11495      * @type Boolean
11496      */
11497     /**
11498      * @property  url
11499      * The default URL to be used for requests to the server. (defaults to undefined)
11500      * @type String
11501      */
11502     /**
11503      * @property  extraParams
11504      * An object containing properties which are used as
11505      * extra parameters to each request made by this object. (defaults to undefined)
11506      * @type Object
11507      */
11508     /**
11509      * @property  defaultHeaders
11510      * An object containing request headers which are added to each request made by this object. (defaults to undefined)
11511      * @type Object
11512      */
11513     /**
11514      * @property  method
11515      * The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11516      * @type String
11517      */
11518     /**
11519      * @property  timeout
11520      * The timeout in milliseconds to be used for requests. (defaults to 30000)
11521      * @type Number
11522      */
11523
11524     /**
11525      * @property  autoAbort
11526      * Whether a new request should abort any pending requests. (defaults to false)
11527      * @type Boolean
11528      */
11529     autoAbort : false,
11530
11531     /**
11532      * Serialize the passed form into a url encoded string
11533      * @param {String/HTMLElement} form
11534      * @return {String}
11535      */
11536     serializeForm : function(form){
11537         return Roo.lib.Ajax.serializeForm(form);
11538     }
11539 });/*
11540  * Based on:
11541  * Ext JS Library 1.1.1
11542  * Copyright(c) 2006-2007, Ext JS, LLC.
11543  *
11544  * Originally Released Under LGPL - original licence link has changed is not relivant.
11545  *
11546  * Fork - LGPL
11547  * <script type="text/javascript">
11548  */
11549
11550  
11551 /**
11552  * @class Roo.UpdateManager
11553  * @extends Roo.util.Observable
11554  * Provides AJAX-style update for Element object.<br><br>
11555  * Usage:<br>
11556  * <pre><code>
11557  * // Get it from a Roo.Element object
11558  * var el = Roo.get("foo");
11559  * var mgr = el.getUpdateManager();
11560  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11561  * ...
11562  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11563  * <br>
11564  * // or directly (returns the same UpdateManager instance)
11565  * var mgr = new Roo.UpdateManager("myElementId");
11566  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11567  * mgr.on("update", myFcnNeedsToKnow);
11568  * <br>
11569    // short handed call directly from the element object
11570    Roo.get("foo").load({
11571         url: "bar.php",
11572         scripts:true,
11573         params: "for=bar",
11574         text: "Loading Foo..."
11575    });
11576  * </code></pre>
11577  * @constructor
11578  * Create new UpdateManager directly.
11579  * @param {String/HTMLElement/Roo.Element} el The element to update
11580  * @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).
11581  */
11582 Roo.UpdateManager = function(el, forceNew){
11583     el = Roo.get(el);
11584     if(!forceNew && el.updateManager){
11585         return el.updateManager;
11586     }
11587     /**
11588      * The Element object
11589      * @type Roo.Element
11590      */
11591     this.el = el;
11592     /**
11593      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11594      * @type String
11595      */
11596     this.defaultUrl = null;
11597
11598     this.addEvents({
11599         /**
11600          * @event beforeupdate
11601          * Fired before an update is made, return false from your handler and the update is cancelled.
11602          * @param {Roo.Element} el
11603          * @param {String/Object/Function} url
11604          * @param {String/Object} params
11605          */
11606         "beforeupdate": true,
11607         /**
11608          * @event update
11609          * Fired after successful update is made.
11610          * @param {Roo.Element} el
11611          * @param {Object} oResponseObject The response Object
11612          */
11613         "update": true,
11614         /**
11615          * @event failure
11616          * Fired on update failure.
11617          * @param {Roo.Element} el
11618          * @param {Object} oResponseObject The response Object
11619          */
11620         "failure": true
11621     });
11622     var d = Roo.UpdateManager.defaults;
11623     /**
11624      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11625      * @type String
11626      */
11627     this.sslBlankUrl = d.sslBlankUrl;
11628     /**
11629      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11630      * @type Boolean
11631      */
11632     this.disableCaching = d.disableCaching;
11633     /**
11634      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11635      * @type String
11636      */
11637     this.indicatorText = d.indicatorText;
11638     /**
11639      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11640      * @type String
11641      */
11642     this.showLoadIndicator = d.showLoadIndicator;
11643     /**
11644      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11645      * @type Number
11646      */
11647     this.timeout = d.timeout;
11648
11649     /**
11650      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11651      * @type Boolean
11652      */
11653     this.loadScripts = d.loadScripts;
11654
11655     /**
11656      * Transaction object of current executing transaction
11657      */
11658     this.transaction = null;
11659
11660     /**
11661      * @private
11662      */
11663     this.autoRefreshProcId = null;
11664     /**
11665      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11666      * @type Function
11667      */
11668     this.refreshDelegate = this.refresh.createDelegate(this);
11669     /**
11670      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11671      * @type Function
11672      */
11673     this.updateDelegate = this.update.createDelegate(this);
11674     /**
11675      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11676      * @type Function
11677      */
11678     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11679     /**
11680      * @private
11681      */
11682     this.successDelegate = this.processSuccess.createDelegate(this);
11683     /**
11684      * @private
11685      */
11686     this.failureDelegate = this.processFailure.createDelegate(this);
11687
11688     if(!this.renderer){
11689      /**
11690       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11691       */
11692     this.renderer = new Roo.UpdateManager.BasicRenderer();
11693     }
11694     
11695     Roo.UpdateManager.superclass.constructor.call(this);
11696 };
11697
11698 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11699     /**
11700      * Get the Element this UpdateManager is bound to
11701      * @return {Roo.Element} The element
11702      */
11703     getEl : function(){
11704         return this.el;
11705     },
11706     /**
11707      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11708      * @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:
11709 <pre><code>
11710 um.update({<br/>
11711     url: "your-url.php",<br/>
11712     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11713     callback: yourFunction,<br/>
11714     scope: yourObject, //(optional scope)  <br/>
11715     discardUrl: false, <br/>
11716     nocache: false,<br/>
11717     text: "Loading...",<br/>
11718     timeout: 30,<br/>
11719     scripts: false<br/>
11720 });
11721 </code></pre>
11722      * The only required property is url. The optional properties nocache, text and scripts
11723      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11724      * @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}
11725      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11726      * @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.
11727      */
11728     update : function(url, params, callback, discardUrl){
11729         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11730             var method = this.method, cfg;
11731             if(typeof url == "object"){ // must be config object
11732                 cfg = url;
11733                 url = cfg.url;
11734                 params = params || cfg.params;
11735                 callback = callback || cfg.callback;
11736                 discardUrl = discardUrl || cfg.discardUrl;
11737                 if(callback && cfg.scope){
11738                     callback = callback.createDelegate(cfg.scope);
11739                 }
11740                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11741                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11742                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11743                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11744                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11745             }
11746             this.showLoading();
11747             if(!discardUrl){
11748                 this.defaultUrl = url;
11749             }
11750             if(typeof url == "function"){
11751                 url = url.call(this);
11752             }
11753
11754             method = method || (params ? "POST" : "GET");
11755             if(method == "GET"){
11756                 url = this.prepareUrl(url);
11757             }
11758
11759             var o = Roo.apply(cfg ||{}, {
11760                 url : url,
11761                 params: params,
11762                 success: this.successDelegate,
11763                 failure: this.failureDelegate,
11764                 callback: undefined,
11765                 timeout: (this.timeout*1000),
11766                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11767             });
11768
11769             this.transaction = Roo.Ajax.request(o);
11770         }
11771     },
11772
11773     /**
11774      * 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.
11775      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11776      * @param {String/HTMLElement} form The form Id or form element
11777      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11778      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11779      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11780      */
11781     formUpdate : function(form, url, reset, callback){
11782         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11783             if(typeof url == "function"){
11784                 url = url.call(this);
11785             }
11786             form = Roo.getDom(form);
11787             this.transaction = Roo.Ajax.request({
11788                 form: form,
11789                 url:url,
11790                 success: this.successDelegate,
11791                 failure: this.failureDelegate,
11792                 timeout: (this.timeout*1000),
11793                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11794             });
11795             this.showLoading.defer(1, this);
11796         }
11797     },
11798
11799     /**
11800      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11801      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11802      */
11803     refresh : function(callback){
11804         if(this.defaultUrl == null){
11805             return;
11806         }
11807         this.update(this.defaultUrl, null, callback, true);
11808     },
11809
11810     /**
11811      * Set this element to auto refresh.
11812      * @param {Number} interval How often to update (in seconds).
11813      * @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)
11814      * @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}
11815      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11816      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11817      */
11818     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11819         if(refreshNow){
11820             this.update(url || this.defaultUrl, params, callback, true);
11821         }
11822         if(this.autoRefreshProcId){
11823             clearInterval(this.autoRefreshProcId);
11824         }
11825         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11826     },
11827
11828     /**
11829      * Stop auto refresh on this element.
11830      */
11831      stopAutoRefresh : function(){
11832         if(this.autoRefreshProcId){
11833             clearInterval(this.autoRefreshProcId);
11834             delete this.autoRefreshProcId;
11835         }
11836     },
11837
11838     isAutoRefreshing : function(){
11839        return this.autoRefreshProcId ? true : false;
11840     },
11841     /**
11842      * Called to update the element to "Loading" state. Override to perform custom action.
11843      */
11844     showLoading : function(){
11845         if(this.showLoadIndicator){
11846             this.el.update(this.indicatorText);
11847         }
11848     },
11849
11850     /**
11851      * Adds unique parameter to query string if disableCaching = true
11852      * @private
11853      */
11854     prepareUrl : function(url){
11855         if(this.disableCaching){
11856             var append = "_dc=" + (new Date().getTime());
11857             if(url.indexOf("?") !== -1){
11858                 url += "&" + append;
11859             }else{
11860                 url += "?" + append;
11861             }
11862         }
11863         return url;
11864     },
11865
11866     /**
11867      * @private
11868      */
11869     processSuccess : function(response){
11870         this.transaction = null;
11871         if(response.argument.form && response.argument.reset){
11872             try{ // put in try/catch since some older FF releases had problems with this
11873                 response.argument.form.reset();
11874             }catch(e){}
11875         }
11876         if(this.loadScripts){
11877             this.renderer.render(this.el, response, this,
11878                 this.updateComplete.createDelegate(this, [response]));
11879         }else{
11880             this.renderer.render(this.el, response, this);
11881             this.updateComplete(response);
11882         }
11883     },
11884
11885     updateComplete : function(response){
11886         this.fireEvent("update", this.el, response);
11887         if(typeof response.argument.callback == "function"){
11888             response.argument.callback(this.el, true, response);
11889         }
11890     },
11891
11892     /**
11893      * @private
11894      */
11895     processFailure : function(response){
11896         this.transaction = null;
11897         this.fireEvent("failure", this.el, response);
11898         if(typeof response.argument.callback == "function"){
11899             response.argument.callback(this.el, false, response);
11900         }
11901     },
11902
11903     /**
11904      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11905      * @param {Object} renderer The object implementing the render() method
11906      */
11907     setRenderer : function(renderer){
11908         this.renderer = renderer;
11909     },
11910
11911     getRenderer : function(){
11912        return this.renderer;
11913     },
11914
11915     /**
11916      * Set the defaultUrl used for updates
11917      * @param {String/Function} defaultUrl The url or a function to call to get the url
11918      */
11919     setDefaultUrl : function(defaultUrl){
11920         this.defaultUrl = defaultUrl;
11921     },
11922
11923     /**
11924      * Aborts the executing transaction
11925      */
11926     abort : function(){
11927         if(this.transaction){
11928             Roo.Ajax.abort(this.transaction);
11929         }
11930     },
11931
11932     /**
11933      * Returns true if an update is in progress
11934      * @return {Boolean}
11935      */
11936     isUpdating : function(){
11937         if(this.transaction){
11938             return Roo.Ajax.isLoading(this.transaction);
11939         }
11940         return false;
11941     }
11942 });
11943
11944 /**
11945  * @class Roo.UpdateManager.defaults
11946  * @static (not really - but it helps the doc tool)
11947  * The defaults collection enables customizing the default properties of UpdateManager
11948  */
11949    Roo.UpdateManager.defaults = {
11950        /**
11951          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
11952          * @type Number
11953          */
11954          timeout : 30,
11955
11956          /**
11957          * True to process scripts by default (Defaults to false).
11958          * @type Boolean
11959          */
11960         loadScripts : false,
11961
11962         /**
11963         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
11964         * @type String
11965         */
11966         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
11967         /**
11968          * Whether to append unique parameter on get request to disable caching (Defaults to false).
11969          * @type Boolean
11970          */
11971         disableCaching : false,
11972         /**
11973          * Whether to show indicatorText when loading (Defaults to true).
11974          * @type Boolean
11975          */
11976         showLoadIndicator : true,
11977         /**
11978          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11979          * @type String
11980          */
11981         indicatorText : '<div class="loading-indicator">Loading...</div>'
11982    };
11983
11984 /**
11985  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
11986  *Usage:
11987  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
11988  * @param {String/HTMLElement/Roo.Element} el The element to update
11989  * @param {String} url The url
11990  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
11991  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
11992  * @static
11993  * @deprecated
11994  * @member Roo.UpdateManager
11995  */
11996 Roo.UpdateManager.updateElement = function(el, url, params, options){
11997     var um = Roo.get(el, true).getUpdateManager();
11998     Roo.apply(um, options);
11999     um.update(url, params, options ? options.callback : null);
12000 };
12001 // alias for backwards compat
12002 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12003 /**
12004  * @class Roo.UpdateManager.BasicRenderer
12005  * Default Content renderer. Updates the elements innerHTML with the responseText.
12006  */
12007 Roo.UpdateManager.BasicRenderer = function(){};
12008
12009 Roo.UpdateManager.BasicRenderer.prototype = {
12010     /**
12011      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12012      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12013      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12014      * @param {Roo.Element} el The element being rendered
12015      * @param {Object} response The YUI Connect response object
12016      * @param {UpdateManager} updateManager The calling update manager
12017      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12018      */
12019      render : function(el, response, updateManager, callback){
12020         el.update(response.responseText, updateManager.loadScripts, callback);
12021     }
12022 };
12023 /*
12024  * Based on:
12025  * Ext JS Library 1.1.1
12026  * Copyright(c) 2006-2007, Ext JS, LLC.
12027  *
12028  * Originally Released Under LGPL - original licence link has changed is not relivant.
12029  *
12030  * Fork - LGPL
12031  * <script type="text/javascript">
12032  */
12033
12034 /**
12035  * @class Roo.util.DelayedTask
12036  * Provides a convenient method of performing setTimeout where a new
12037  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12038  * You can use this class to buffer
12039  * the keypress events for a certain number of milliseconds, and perform only if they stop
12040  * for that amount of time.
12041  * @constructor The parameters to this constructor serve as defaults and are not required.
12042  * @param {Function} fn (optional) The default function to timeout
12043  * @param {Object} scope (optional) The default scope of that timeout
12044  * @param {Array} args (optional) The default Array of arguments
12045  */
12046 Roo.util.DelayedTask = function(fn, scope, args){
12047     var id = null, d, t;
12048
12049     var call = function(){
12050         var now = new Date().getTime();
12051         if(now - t >= d){
12052             clearInterval(id);
12053             id = null;
12054             fn.apply(scope, args || []);
12055         }
12056     };
12057     /**
12058      * Cancels any pending timeout and queues a new one
12059      * @param {Number} delay The milliseconds to delay
12060      * @param {Function} newFn (optional) Overrides function passed to constructor
12061      * @param {Object} newScope (optional) Overrides scope passed to constructor
12062      * @param {Array} newArgs (optional) Overrides args passed to constructor
12063      */
12064     this.delay = function(delay, newFn, newScope, newArgs){
12065         if(id && delay != d){
12066             this.cancel();
12067         }
12068         d = delay;
12069         t = new Date().getTime();
12070         fn = newFn || fn;
12071         scope = newScope || scope;
12072         args = newArgs || args;
12073         if(!id){
12074             id = setInterval(call, d);
12075         }
12076     };
12077
12078     /**
12079      * Cancel the last queued timeout
12080      */
12081     this.cancel = function(){
12082         if(id){
12083             clearInterval(id);
12084             id = null;
12085         }
12086     };
12087 };/*
12088  * Based on:
12089  * Ext JS Library 1.1.1
12090  * Copyright(c) 2006-2007, Ext JS, LLC.
12091  *
12092  * Originally Released Under LGPL - original licence link has changed is not relivant.
12093  *
12094  * Fork - LGPL
12095  * <script type="text/javascript">
12096  */
12097  
12098  
12099 Roo.util.TaskRunner = function(interval){
12100     interval = interval || 10;
12101     var tasks = [], removeQueue = [];
12102     var id = 0;
12103     var running = false;
12104
12105     var stopThread = function(){
12106         running = false;
12107         clearInterval(id);
12108         id = 0;
12109     };
12110
12111     var startThread = function(){
12112         if(!running){
12113             running = true;
12114             id = setInterval(runTasks, interval);
12115         }
12116     };
12117
12118     var removeTask = function(task){
12119         removeQueue.push(task);
12120         if(task.onStop){
12121             task.onStop();
12122         }
12123     };
12124
12125     var runTasks = function(){
12126         if(removeQueue.length > 0){
12127             for(var i = 0, len = removeQueue.length; i < len; i++){
12128                 tasks.remove(removeQueue[i]);
12129             }
12130             removeQueue = [];
12131             if(tasks.length < 1){
12132                 stopThread();
12133                 return;
12134             }
12135         }
12136         var now = new Date().getTime();
12137         for(var i = 0, len = tasks.length; i < len; ++i){
12138             var t = tasks[i];
12139             var itime = now - t.taskRunTime;
12140             if(t.interval <= itime){
12141                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12142                 t.taskRunTime = now;
12143                 if(rt === false || t.taskRunCount === t.repeat){
12144                     removeTask(t);
12145                     return;
12146                 }
12147             }
12148             if(t.duration && t.duration <= (now - t.taskStartTime)){
12149                 removeTask(t);
12150             }
12151         }
12152     };
12153
12154     /**
12155      * Queues a new task.
12156      * @param {Object} task
12157      */
12158     this.start = function(task){
12159         tasks.push(task);
12160         task.taskStartTime = new Date().getTime();
12161         task.taskRunTime = 0;
12162         task.taskRunCount = 0;
12163         startThread();
12164         return task;
12165     };
12166
12167     this.stop = function(task){
12168         removeTask(task);
12169         return task;
12170     };
12171
12172     this.stopAll = function(){
12173         stopThread();
12174         for(var i = 0, len = tasks.length; i < len; i++){
12175             if(tasks[i].onStop){
12176                 tasks[i].onStop();
12177             }
12178         }
12179         tasks = [];
12180         removeQueue = [];
12181     };
12182 };
12183
12184 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12185  * Based on:
12186  * Ext JS Library 1.1.1
12187  * Copyright(c) 2006-2007, Ext JS, LLC.
12188  *
12189  * Originally Released Under LGPL - original licence link has changed is not relivant.
12190  *
12191  * Fork - LGPL
12192  * <script type="text/javascript">
12193  */
12194
12195  
12196 /**
12197  * @class Roo.util.MixedCollection
12198  * @extends Roo.util.Observable
12199  * A Collection class that maintains both numeric indexes and keys and exposes events.
12200  * @constructor
12201  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12202  * collection (defaults to false)
12203  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12204  * and return the key value for that item.  This is used when available to look up the key on items that
12205  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12206  * equivalent to providing an implementation for the {@link #getKey} method.
12207  */
12208 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12209     this.items = [];
12210     this.map = {};
12211     this.keys = [];
12212     this.length = 0;
12213     this.addEvents({
12214         /**
12215          * @event clear
12216          * Fires when the collection is cleared.
12217          */
12218         "clear" : true,
12219         /**
12220          * @event add
12221          * Fires when an item is added to the collection.
12222          * @param {Number} index The index at which the item was added.
12223          * @param {Object} o The item added.
12224          * @param {String} key The key associated with the added item.
12225          */
12226         "add" : true,
12227         /**
12228          * @event replace
12229          * Fires when an item is replaced in the collection.
12230          * @param {String} key he key associated with the new added.
12231          * @param {Object} old The item being replaced.
12232          * @param {Object} new The new item.
12233          */
12234         "replace" : true,
12235         /**
12236          * @event remove
12237          * Fires when an item is removed from the collection.
12238          * @param {Object} o The item being removed.
12239          * @param {String} key (optional) The key associated with the removed item.
12240          */
12241         "remove" : true,
12242         "sort" : true
12243     });
12244     this.allowFunctions = allowFunctions === true;
12245     if(keyFn){
12246         this.getKey = keyFn;
12247     }
12248     Roo.util.MixedCollection.superclass.constructor.call(this);
12249 };
12250
12251 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12252     allowFunctions : false,
12253     
12254 /**
12255  * Adds an item to the collection.
12256  * @param {String} key The key to associate with the item
12257  * @param {Object} o The item to add.
12258  * @return {Object} The item added.
12259  */
12260     add : function(key, o){
12261         if(arguments.length == 1){
12262             o = arguments[0];
12263             key = this.getKey(o);
12264         }
12265         if(typeof key == "undefined" || key === null){
12266             this.length++;
12267             this.items.push(o);
12268             this.keys.push(null);
12269         }else{
12270             var old = this.map[key];
12271             if(old){
12272                 return this.replace(key, o);
12273             }
12274             this.length++;
12275             this.items.push(o);
12276             this.map[key] = o;
12277             this.keys.push(key);
12278         }
12279         this.fireEvent("add", this.length-1, o, key);
12280         return o;
12281     },
12282        
12283 /**
12284   * MixedCollection has a generic way to fetch keys if you implement getKey.
12285 <pre><code>
12286 // normal way
12287 var mc = new Roo.util.MixedCollection();
12288 mc.add(someEl.dom.id, someEl);
12289 mc.add(otherEl.dom.id, otherEl);
12290 //and so on
12291
12292 // using getKey
12293 var mc = new Roo.util.MixedCollection();
12294 mc.getKey = function(el){
12295    return el.dom.id;
12296 };
12297 mc.add(someEl);
12298 mc.add(otherEl);
12299
12300 // or via the constructor
12301 var mc = new Roo.util.MixedCollection(false, function(el){
12302    return el.dom.id;
12303 });
12304 mc.add(someEl);
12305 mc.add(otherEl);
12306 </code></pre>
12307  * @param o {Object} The item for which to find the key.
12308  * @return {Object} The key for the passed item.
12309  */
12310     getKey : function(o){
12311          return o.id; 
12312     },
12313    
12314 /**
12315  * Replaces an item in the collection.
12316  * @param {String} key The key associated with the item to replace, or the item to replace.
12317  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12318  * @return {Object}  The new item.
12319  */
12320     replace : function(key, o){
12321         if(arguments.length == 1){
12322             o = arguments[0];
12323             key = this.getKey(o);
12324         }
12325         var old = this.item(key);
12326         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12327              return this.add(key, o);
12328         }
12329         var index = this.indexOfKey(key);
12330         this.items[index] = o;
12331         this.map[key] = o;
12332         this.fireEvent("replace", key, old, o);
12333         return o;
12334     },
12335    
12336 /**
12337  * Adds all elements of an Array or an Object to the collection.
12338  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12339  * an Array of values, each of which are added to the collection.
12340  */
12341     addAll : function(objs){
12342         if(arguments.length > 1 || objs instanceof Array){
12343             var args = arguments.length > 1 ? arguments : objs;
12344             for(var i = 0, len = args.length; i < len; i++){
12345                 this.add(args[i]);
12346             }
12347         }else{
12348             for(var key in objs){
12349                 if(this.allowFunctions || typeof objs[key] != "function"){
12350                     this.add(key, objs[key]);
12351                 }
12352             }
12353         }
12354     },
12355    
12356 /**
12357  * Executes the specified function once for every item in the collection, passing each
12358  * item as the first and only parameter. returning false from the function will stop the iteration.
12359  * @param {Function} fn The function to execute for each item.
12360  * @param {Object} scope (optional) The scope in which to execute the function.
12361  */
12362     each : function(fn, scope){
12363         var items = [].concat(this.items); // each safe for removal
12364         for(var i = 0, len = items.length; i < len; i++){
12365             if(fn.call(scope || items[i], items[i], i, len) === false){
12366                 break;
12367             }
12368         }
12369     },
12370    
12371 /**
12372  * Executes the specified function once for every key in the collection, passing each
12373  * key, and its associated item as the first two parameters.
12374  * @param {Function} fn The function to execute for each item.
12375  * @param {Object} scope (optional) The scope in which to execute the function.
12376  */
12377     eachKey : function(fn, scope){
12378         for(var i = 0, len = this.keys.length; i < len; i++){
12379             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12380         }
12381     },
12382    
12383 /**
12384  * Returns the first item in the collection which elicits a true return value from the
12385  * passed selection function.
12386  * @param {Function} fn The selection function to execute for each item.
12387  * @param {Object} scope (optional) The scope in which to execute the function.
12388  * @return {Object} The first item in the collection which returned true from the selection function.
12389  */
12390     find : function(fn, scope){
12391         for(var i = 0, len = this.items.length; i < len; i++){
12392             if(fn.call(scope || window, this.items[i], this.keys[i])){
12393                 return this.items[i];
12394             }
12395         }
12396         return null;
12397     },
12398    
12399 /**
12400  * Inserts an item at the specified index in the collection.
12401  * @param {Number} index The index to insert the item at.
12402  * @param {String} key The key to associate with the new item, or the item itself.
12403  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12404  * @return {Object} The item inserted.
12405  */
12406     insert : function(index, key, o){
12407         if(arguments.length == 2){
12408             o = arguments[1];
12409             key = this.getKey(o);
12410         }
12411         if(index >= this.length){
12412             return this.add(key, o);
12413         }
12414         this.length++;
12415         this.items.splice(index, 0, o);
12416         if(typeof key != "undefined" && key != null){
12417             this.map[key] = o;
12418         }
12419         this.keys.splice(index, 0, key);
12420         this.fireEvent("add", index, o, key);
12421         return o;
12422     },
12423    
12424 /**
12425  * Removed an item from the collection.
12426  * @param {Object} o The item to remove.
12427  * @return {Object} The item removed.
12428  */
12429     remove : function(o){
12430         return this.removeAt(this.indexOf(o));
12431     },
12432    
12433 /**
12434  * Remove an item from a specified index in the collection.
12435  * @param {Number} index The index within the collection of the item to remove.
12436  */
12437     removeAt : function(index){
12438         if(index < this.length && index >= 0){
12439             this.length--;
12440             var o = this.items[index];
12441             this.items.splice(index, 1);
12442             var key = this.keys[index];
12443             if(typeof key != "undefined"){
12444                 delete this.map[key];
12445             }
12446             this.keys.splice(index, 1);
12447             this.fireEvent("remove", o, key);
12448         }
12449     },
12450    
12451 /**
12452  * Removed an item associated with the passed key fom the collection.
12453  * @param {String} key The key of the item to remove.
12454  */
12455     removeKey : function(key){
12456         return this.removeAt(this.indexOfKey(key));
12457     },
12458    
12459 /**
12460  * Returns the number of items in the collection.
12461  * @return {Number} the number of items in the collection.
12462  */
12463     getCount : function(){
12464         return this.length; 
12465     },
12466    
12467 /**
12468  * Returns index within the collection of the passed Object.
12469  * @param {Object} o The item to find the index of.
12470  * @return {Number} index of the item.
12471  */
12472     indexOf : function(o){
12473         if(!this.items.indexOf){
12474             for(var i = 0, len = this.items.length; i < len; i++){
12475                 if(this.items[i] == o) return i;
12476             }
12477             return -1;
12478         }else{
12479             return this.items.indexOf(o);
12480         }
12481     },
12482    
12483 /**
12484  * Returns index within the collection of the passed key.
12485  * @param {String} key The key to find the index of.
12486  * @return {Number} index of the key.
12487  */
12488     indexOfKey : function(key){
12489         if(!this.keys.indexOf){
12490             for(var i = 0, len = this.keys.length; i < len; i++){
12491                 if(this.keys[i] == key) return i;
12492             }
12493             return -1;
12494         }else{
12495             return this.keys.indexOf(key);
12496         }
12497     },
12498    
12499 /**
12500  * Returns the item associated with the passed key OR index. Key has priority over index.
12501  * @param {String/Number} key The key or index of the item.
12502  * @return {Object} The item associated with the passed key.
12503  */
12504     item : function(key){
12505         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
12506         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
12507     },
12508     
12509 /**
12510  * Returns the item at the specified index.
12511  * @param {Number} index The index of the item.
12512  * @return {Object}
12513  */
12514     itemAt : function(index){
12515         return this.items[index];
12516     },
12517     
12518 /**
12519  * Returns the item associated with the passed key.
12520  * @param {String/Number} key The key of the item.
12521  * @return {Object} The item associated with the passed key.
12522  */
12523     key : function(key){
12524         return this.map[key];
12525     },
12526    
12527 /**
12528  * Returns true if the collection contains the passed Object as an item.
12529  * @param {Object} o  The Object to look for in the collection.
12530  * @return {Boolean} True if the collection contains the Object as an item.
12531  */
12532     contains : function(o){
12533         return this.indexOf(o) != -1;
12534     },
12535    
12536 /**
12537  * Returns true if the collection contains the passed Object as a key.
12538  * @param {String} key The key to look for in the collection.
12539  * @return {Boolean} True if the collection contains the Object as a key.
12540  */
12541     containsKey : function(key){
12542         return typeof this.map[key] != "undefined";
12543     },
12544    
12545 /**
12546  * Removes all items from the collection.
12547  */
12548     clear : function(){
12549         this.length = 0;
12550         this.items = [];
12551         this.keys = [];
12552         this.map = {};
12553         this.fireEvent("clear");
12554     },
12555    
12556 /**
12557  * Returns the first item in the collection.
12558  * @return {Object} the first item in the collection..
12559  */
12560     first : function(){
12561         return this.items[0]; 
12562     },
12563    
12564 /**
12565  * Returns the last item in the collection.
12566  * @return {Object} the last item in the collection..
12567  */
12568     last : function(){
12569         return this.items[this.length-1];   
12570     },
12571     
12572     _sort : function(property, dir, fn){
12573         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
12574         fn = fn || function(a, b){
12575             return a-b;
12576         };
12577         var c = [], k = this.keys, items = this.items;
12578         for(var i = 0, len = items.length; i < len; i++){
12579             c[c.length] = {key: k[i], value: items[i], index: i};
12580         }
12581         c.sort(function(a, b){
12582             var v = fn(a[property], b[property]) * dsc;
12583             if(v == 0){
12584                 v = (a.index < b.index ? -1 : 1);
12585             }
12586             return v;
12587         });
12588         for(var i = 0, len = c.length; i < len; i++){
12589             items[i] = c[i].value;
12590             k[i] = c[i].key;
12591         }
12592         this.fireEvent("sort", this);
12593     },
12594     
12595     /**
12596      * Sorts this collection with the passed comparison function
12597      * @param {String} direction (optional) "ASC" or "DESC"
12598      * @param {Function} fn (optional) comparison function
12599      */
12600     sort : function(dir, fn){
12601         this._sort("value", dir, fn);
12602     },
12603     
12604     /**
12605      * Sorts this collection by keys
12606      * @param {String} direction (optional) "ASC" or "DESC"
12607      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
12608      */
12609     keySort : function(dir, fn){
12610         this._sort("key", dir, fn || function(a, b){
12611             return String(a).toUpperCase()-String(b).toUpperCase();
12612         });
12613     },
12614     
12615     /**
12616      * Returns a range of items in this collection
12617      * @param {Number} startIndex (optional) defaults to 0
12618      * @param {Number} endIndex (optional) default to the last item
12619      * @return {Array} An array of items
12620      */
12621     getRange : function(start, end){
12622         var items = this.items;
12623         if(items.length < 1){
12624             return [];
12625         }
12626         start = start || 0;
12627         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
12628         var r = [];
12629         if(start <= end){
12630             for(var i = start; i <= end; i++) {
12631                     r[r.length] = items[i];
12632             }
12633         }else{
12634             for(var i = start; i >= end; i--) {
12635                     r[r.length] = items[i];
12636             }
12637         }
12638         return r;
12639     },
12640         
12641     /**
12642      * Filter the <i>objects</i> in this collection by a specific property. 
12643      * Returns a new collection that has been filtered.
12644      * @param {String} property A property on your objects
12645      * @param {String/RegExp} value Either string that the property values 
12646      * should start with or a RegExp to test against the property
12647      * @return {MixedCollection} The new filtered collection
12648      */
12649     filter : function(property, value){
12650         if(!value.exec){ // not a regex
12651             value = String(value);
12652             if(value.length == 0){
12653                 return this.clone();
12654             }
12655             value = new RegExp("^" + Roo.escapeRe(value), "i");
12656         }
12657         return this.filterBy(function(o){
12658             return o && value.test(o[property]);
12659         });
12660         },
12661     
12662     /**
12663      * Filter by a function. * Returns a new collection that has been filtered.
12664      * The passed function will be called with each 
12665      * object in the collection. If the function returns true, the value is included 
12666      * otherwise it is filtered.
12667      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
12668      * @param {Object} scope (optional) The scope of the function (defaults to this) 
12669      * @return {MixedCollection} The new filtered collection
12670      */
12671     filterBy : function(fn, scope){
12672         var r = new Roo.util.MixedCollection();
12673         r.getKey = this.getKey;
12674         var k = this.keys, it = this.items;
12675         for(var i = 0, len = it.length; i < len; i++){
12676             if(fn.call(scope||this, it[i], k[i])){
12677                                 r.add(k[i], it[i]);
12678                         }
12679         }
12680         return r;
12681     },
12682     
12683     /**
12684      * Creates a duplicate of this collection
12685      * @return {MixedCollection}
12686      */
12687     clone : function(){
12688         var r = new Roo.util.MixedCollection();
12689         var k = this.keys, it = this.items;
12690         for(var i = 0, len = it.length; i < len; i++){
12691             r.add(k[i], it[i]);
12692         }
12693         r.getKey = this.getKey;
12694         return r;
12695     }
12696 });
12697 /**
12698  * Returns the item associated with the passed key or index.
12699  * @method
12700  * @param {String/Number} key The key or index of the item.
12701  * @return {Object} The item associated with the passed key.
12702  */
12703 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
12704  * Based on:
12705  * Ext JS Library 1.1.1
12706  * Copyright(c) 2006-2007, Ext JS, LLC.
12707  *
12708  * Originally Released Under LGPL - original licence link has changed is not relivant.
12709  *
12710  * Fork - LGPL
12711  * <script type="text/javascript">
12712  */
12713 /**
12714  * @class Roo.util.JSON
12715  * Modified version of Douglas Crockford"s json.js that doesn"t
12716  * mess with the Object prototype 
12717  * http://www.json.org/js.html
12718  * @singleton
12719  */
12720 Roo.util.JSON = new (function(){
12721     var useHasOwn = {}.hasOwnProperty ? true : false;
12722     
12723     // crashes Safari in some instances
12724     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
12725     
12726     var pad = function(n) {
12727         return n < 10 ? "0" + n : n;
12728     };
12729     
12730     var m = {
12731         "\b": '\\b',
12732         "\t": '\\t',
12733         "\n": '\\n',
12734         "\f": '\\f',
12735         "\r": '\\r',
12736         '"' : '\\"',
12737         "\\": '\\\\'
12738     };
12739
12740     var encodeString = function(s){
12741         if (/["\\\x00-\x1f]/.test(s)) {
12742             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
12743                 var c = m[b];
12744                 if(c){
12745                     return c;
12746                 }
12747                 c = b.charCodeAt();
12748                 return "\\u00" +
12749                     Math.floor(c / 16).toString(16) +
12750                     (c % 16).toString(16);
12751             }) + '"';
12752         }
12753         return '"' + s + '"';
12754     };
12755     
12756     var encodeArray = function(o){
12757         var a = ["["], b, i, l = o.length, v;
12758             for (i = 0; i < l; i += 1) {
12759                 v = o[i];
12760                 switch (typeof v) {
12761                     case "undefined":
12762                     case "function":
12763                     case "unknown":
12764                         break;
12765                     default:
12766                         if (b) {
12767                             a.push(',');
12768                         }
12769                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
12770                         b = true;
12771                 }
12772             }
12773             a.push("]");
12774             return a.join("");
12775     };
12776     
12777     var encodeDate = function(o){
12778         return '"' + o.getFullYear() + "-" +
12779                 pad(o.getMonth() + 1) + "-" +
12780                 pad(o.getDate()) + "T" +
12781                 pad(o.getHours()) + ":" +
12782                 pad(o.getMinutes()) + ":" +
12783                 pad(o.getSeconds()) + '"';
12784     };
12785     
12786     /**
12787      * Encodes an Object, Array or other value
12788      * @param {Mixed} o The variable to encode
12789      * @return {String} The JSON string
12790      */
12791     this.encode = function(o)
12792     {
12793         // should this be extended to fully wrap stringify..
12794         
12795         if(typeof o == "undefined" || o === null){
12796             return "null";
12797         }else if(o instanceof Array){
12798             return encodeArray(o);
12799         }else if(o instanceof Date){
12800             return encodeDate(o);
12801         }else if(typeof o == "string"){
12802             return encodeString(o);
12803         }else if(typeof o == "number"){
12804             return isFinite(o) ? String(o) : "null";
12805         }else if(typeof o == "boolean"){
12806             return String(o);
12807         }else {
12808             var a = ["{"], b, i, v;
12809             for (i in o) {
12810                 if(!useHasOwn || o.hasOwnProperty(i)) {
12811                     v = o[i];
12812                     switch (typeof v) {
12813                     case "undefined":
12814                     case "function":
12815                     case "unknown":
12816                         break;
12817                     default:
12818                         if(b){
12819                             a.push(',');
12820                         }
12821                         a.push(this.encode(i), ":",
12822                                 v === null ? "null" : this.encode(v));
12823                         b = true;
12824                     }
12825                 }
12826             }
12827             a.push("}");
12828             return a.join("");
12829         }
12830     };
12831     
12832     /**
12833      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
12834      * @param {String} json The JSON string
12835      * @return {Object} The resulting object
12836      */
12837     this.decode = function(json){
12838         
12839         return  /** eval:var:json */ eval("(" + json + ')');
12840     };
12841 })();
12842 /** 
12843  * Shorthand for {@link Roo.util.JSON#encode}
12844  * @member Roo encode 
12845  * @method */
12846 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
12847 /** 
12848  * Shorthand for {@link Roo.util.JSON#decode}
12849  * @member Roo decode 
12850  * @method */
12851 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
12852 /*
12853  * Based on:
12854  * Ext JS Library 1.1.1
12855  * Copyright(c) 2006-2007, Ext JS, LLC.
12856  *
12857  * Originally Released Under LGPL - original licence link has changed is not relivant.
12858  *
12859  * Fork - LGPL
12860  * <script type="text/javascript">
12861  */
12862  
12863 /**
12864  * @class Roo.util.Format
12865  * Reusable data formatting functions
12866  * @singleton
12867  */
12868 Roo.util.Format = function(){
12869     var trimRe = /^\s+|\s+$/g;
12870     return {
12871         /**
12872          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
12873          * @param {String} value The string to truncate
12874          * @param {Number} length The maximum length to allow before truncating
12875          * @return {String} The converted text
12876          */
12877         ellipsis : function(value, len){
12878             if(value && value.length > len){
12879                 return value.substr(0, len-3)+"...";
12880             }
12881             return value;
12882         },
12883
12884         /**
12885          * Checks a reference and converts it to empty string if it is undefined
12886          * @param {Mixed} value Reference to check
12887          * @return {Mixed} Empty string if converted, otherwise the original value
12888          */
12889         undef : function(value){
12890             return typeof value != "undefined" ? value : "";
12891         },
12892
12893         /**
12894          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
12895          * @param {String} value The string to encode
12896          * @return {String} The encoded text
12897          */
12898         htmlEncode : function(value){
12899             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
12900         },
12901
12902         /**
12903          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
12904          * @param {String} value The string to decode
12905          * @return {String} The decoded text
12906          */
12907         htmlDecode : function(value){
12908             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
12909         },
12910
12911         /**
12912          * Trims any whitespace from either side of a string
12913          * @param {String} value The text to trim
12914          * @return {String} The trimmed text
12915          */
12916         trim : function(value){
12917             return String(value).replace(trimRe, "");
12918         },
12919
12920         /**
12921          * Returns a substring from within an original string
12922          * @param {String} value The original text
12923          * @param {Number} start The start index of the substring
12924          * @param {Number} length The length of the substring
12925          * @return {String} The substring
12926          */
12927         substr : function(value, start, length){
12928             return String(value).substr(start, length);
12929         },
12930
12931         /**
12932          * Converts a string to all lower case letters
12933          * @param {String} value The text to convert
12934          * @return {String} The converted text
12935          */
12936         lowercase : function(value){
12937             return String(value).toLowerCase();
12938         },
12939
12940         /**
12941          * Converts a string to all upper case letters
12942          * @param {String} value The text to convert
12943          * @return {String} The converted text
12944          */
12945         uppercase : function(value){
12946             return String(value).toUpperCase();
12947         },
12948
12949         /**
12950          * Converts the first character only of a string to upper case
12951          * @param {String} value The text to convert
12952          * @return {String} The converted text
12953          */
12954         capitalize : function(value){
12955             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
12956         },
12957
12958         // private
12959         call : function(value, fn){
12960             if(arguments.length > 2){
12961                 var args = Array.prototype.slice.call(arguments, 2);
12962                 args.unshift(value);
12963                  
12964                 return /** eval:var:value */  eval(fn).apply(window, args);
12965             }else{
12966                 /** eval:var:value */
12967                 return /** eval:var:value */ eval(fn).call(window, value);
12968             }
12969         },
12970
12971        
12972         /**
12973          * safer version of Math.toFixed..??/
12974          * @param {Number/String} value The numeric value to format
12975          * @param {Number/String} value Decimal places 
12976          * @return {String} The formatted currency string
12977          */
12978         toFixed : function(v, n)
12979         {
12980             // why not use to fixed - precision is buggered???
12981             if (!n) {
12982                 return Math.round(v-0);
12983             }
12984             var fact = Math.pow(10,n+1);
12985             v = (Math.round((v-0)*fact))/fact;
12986             var z = (''+fact).substring(2);
12987             if (v == Math.floor(v)) {
12988                 return Math.floor(v) + '.' + z;
12989             }
12990             
12991             // now just padd decimals..
12992             var ps = String(v).split('.');
12993             var fd = (ps[1] + z);
12994             var r = fd.substring(0,n); 
12995             var rm = fd.substring(n); 
12996             if (rm < 5) {
12997                 return ps[0] + '.' + r;
12998             }
12999             r*=1; // turn it into a number;
13000             r++;
13001             if (String(r).length != n) {
13002                 ps[0]*=1;
13003                 ps[0]++;
13004                 r = String(r).substring(1); // chop the end off.
13005             }
13006             
13007             return ps[0] + '.' + r;
13008              
13009         },
13010         
13011         /**
13012          * Format a number as US currency
13013          * @param {Number/String} value The numeric value to format
13014          * @return {String} The formatted currency string
13015          */
13016         usMoney : function(v){
13017             v = (Math.round((v-0)*100))/100;
13018             v = (v == Math.floor(v)) ? v + ".00" : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13019             v = String(v);
13020             var ps = v.split('.');
13021             var whole = ps[0];
13022             var sub = ps[1] ? '.'+ ps[1] : '.00';
13023             var r = /(\d+)(\d{3})/;
13024             while (r.test(whole)) {
13025                 whole = whole.replace(r, '$1' + ',' + '$2');
13026             }
13027             return "$" + whole + sub ;
13028         },
13029         
13030         /**
13031          * Parse a value into a formatted date using the specified format pattern.
13032          * @param {Mixed} value The value to format
13033          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13034          * @return {String} The formatted date string
13035          */
13036         date : function(v, format){
13037             if(!v){
13038                 return "";
13039             }
13040             if(!(v instanceof Date)){
13041                 v = new Date(Date.parse(v));
13042             }
13043             return v.dateFormat(format || "m/d/Y");
13044         },
13045
13046         /**
13047          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13048          * @param {String} format Any valid date format string
13049          * @return {Function} The date formatting function
13050          */
13051         dateRenderer : function(format){
13052             return function(v){
13053                 return Roo.util.Format.date(v, format);  
13054             };
13055         },
13056
13057         // private
13058         stripTagsRE : /<\/?[^>]+>/gi,
13059         
13060         /**
13061          * Strips all HTML tags
13062          * @param {Mixed} value The text from which to strip tags
13063          * @return {String} The stripped text
13064          */
13065         stripTags : function(v){
13066             return !v ? v : String(v).replace(this.stripTagsRE, "");
13067         }
13068     };
13069 }();/*
13070  * Based on:
13071  * Ext JS Library 1.1.1
13072  * Copyright(c) 2006-2007, Ext JS, LLC.
13073  *
13074  * Originally Released Under LGPL - original licence link has changed is not relivant.
13075  *
13076  * Fork - LGPL
13077  * <script type="text/javascript">
13078  */
13079
13080
13081  
13082
13083 /**
13084  * @class Roo.MasterTemplate
13085  * @extends Roo.Template
13086  * Provides a template that can have child templates. The syntax is:
13087 <pre><code>
13088 var t = new Roo.MasterTemplate(
13089         '&lt;select name="{name}"&gt;',
13090                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13091         '&lt;/select&gt;'
13092 );
13093 t.add('options', {value: 'foo', text: 'bar'});
13094 // or you can add multiple child elements in one shot
13095 t.addAll('options', [
13096     {value: 'foo', text: 'bar'},
13097     {value: 'foo2', text: 'bar2'},
13098     {value: 'foo3', text: 'bar3'}
13099 ]);
13100 // then append, applying the master template values
13101 t.append('my-form', {name: 'my-select'});
13102 </code></pre>
13103 * A name attribute for the child template is not required if you have only one child
13104 * template or you want to refer to them by index.
13105  */
13106 Roo.MasterTemplate = function(){
13107     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13108     this.originalHtml = this.html;
13109     var st = {};
13110     var m, re = this.subTemplateRe;
13111     re.lastIndex = 0;
13112     var subIndex = 0;
13113     while(m = re.exec(this.html)){
13114         var name = m[1], content = m[2];
13115         st[subIndex] = {
13116             name: name,
13117             index: subIndex,
13118             buffer: [],
13119             tpl : new Roo.Template(content)
13120         };
13121         if(name){
13122             st[name] = st[subIndex];
13123         }
13124         st[subIndex].tpl.compile();
13125         st[subIndex].tpl.call = this.call.createDelegate(this);
13126         subIndex++;
13127     }
13128     this.subCount = subIndex;
13129     this.subs = st;
13130 };
13131 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13132     /**
13133     * The regular expression used to match sub templates
13134     * @type RegExp
13135     * @property
13136     */
13137     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13138
13139     /**
13140      * Applies the passed values to a child template.
13141      * @param {String/Number} name (optional) The name or index of the child template
13142      * @param {Array/Object} values The values to be applied to the template
13143      * @return {MasterTemplate} this
13144      */
13145      add : function(name, values){
13146         if(arguments.length == 1){
13147             values = arguments[0];
13148             name = 0;
13149         }
13150         var s = this.subs[name];
13151         s.buffer[s.buffer.length] = s.tpl.apply(values);
13152         return this;
13153     },
13154
13155     /**
13156      * Applies all the passed values to a child template.
13157      * @param {String/Number} name (optional) The name or index of the child template
13158      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13159      * @param {Boolean} reset (optional) True to reset the template first
13160      * @return {MasterTemplate} this
13161      */
13162     fill : function(name, values, reset){
13163         var a = arguments;
13164         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13165             values = a[0];
13166             name = 0;
13167             reset = a[1];
13168         }
13169         if(reset){
13170             this.reset();
13171         }
13172         for(var i = 0, len = values.length; i < len; i++){
13173             this.add(name, values[i]);
13174         }
13175         return this;
13176     },
13177
13178     /**
13179      * Resets the template for reuse
13180      * @return {MasterTemplate} this
13181      */
13182      reset : function(){
13183         var s = this.subs;
13184         for(var i = 0; i < this.subCount; i++){
13185             s[i].buffer = [];
13186         }
13187         return this;
13188     },
13189
13190     applyTemplate : function(values){
13191         var s = this.subs;
13192         var replaceIndex = -1;
13193         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13194             return s[++replaceIndex].buffer.join("");
13195         });
13196         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13197     },
13198
13199     apply : function(){
13200         return this.applyTemplate.apply(this, arguments);
13201     },
13202
13203     compile : function(){return this;}
13204 });
13205
13206 /**
13207  * Alias for fill().
13208  * @method
13209  */
13210 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13211  /**
13212  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13213  * var tpl = Roo.MasterTemplate.from('element-id');
13214  * @param {String/HTMLElement} el
13215  * @param {Object} config
13216  * @static
13217  */
13218 Roo.MasterTemplate.from = function(el, config){
13219     el = Roo.getDom(el);
13220     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13221 };/*
13222  * Based on:
13223  * Ext JS Library 1.1.1
13224  * Copyright(c) 2006-2007, Ext JS, LLC.
13225  *
13226  * Originally Released Under LGPL - original licence link has changed is not relivant.
13227  *
13228  * Fork - LGPL
13229  * <script type="text/javascript">
13230  */
13231
13232  
13233 /**
13234  * @class Roo.util.CSS
13235  * Utility class for manipulating CSS rules
13236  * @singleton
13237  */
13238 Roo.util.CSS = function(){
13239         var rules = null;
13240         var doc = document;
13241
13242     var camelRe = /(-[a-z])/gi;
13243     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13244
13245    return {
13246    /**
13247     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13248     * tag and appended to the HEAD of the document.
13249     * @param {String|Object} cssText The text containing the css rules
13250     * @param {String} id An id to add to the stylesheet for later removal
13251     * @return {StyleSheet}
13252     */
13253     createStyleSheet : function(cssText, id){
13254         var ss;
13255         var head = doc.getElementsByTagName("head")[0];
13256         var nrules = doc.createElement("style");
13257         nrules.setAttribute("type", "text/css");
13258         if(id){
13259             nrules.setAttribute("id", id);
13260         }
13261         if (typeof(cssText) != 'string') {
13262             // support object maps..
13263             // not sure if this a good idea.. 
13264             // perhaps it should be merged with the general css handling
13265             // and handle js style props.
13266             var cssTextNew = [];
13267             for(var n in cssText) {
13268                 var citems = [];
13269                 for(var k in cssText[n]) {
13270                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13271                 }
13272                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13273                 
13274             }
13275             cssText = cssTextNew.join("\n");
13276             
13277         }
13278        
13279        
13280        if(Roo.isIE){
13281            head.appendChild(nrules);
13282            ss = nrules.styleSheet;
13283            ss.cssText = cssText;
13284        }else{
13285            try{
13286                 nrules.appendChild(doc.createTextNode(cssText));
13287            }catch(e){
13288                nrules.cssText = cssText; 
13289            }
13290            head.appendChild(nrules);
13291            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13292        }
13293        this.cacheStyleSheet(ss);
13294        return ss;
13295    },
13296
13297    /**
13298     * Removes a style or link tag by id
13299     * @param {String} id The id of the tag
13300     */
13301    removeStyleSheet : function(id){
13302        var existing = doc.getElementById(id);
13303        if(existing){
13304            existing.parentNode.removeChild(existing);
13305        }
13306    },
13307
13308    /**
13309     * Dynamically swaps an existing stylesheet reference for a new one
13310     * @param {String} id The id of an existing link tag to remove
13311     * @param {String} url The href of the new stylesheet to include
13312     */
13313    swapStyleSheet : function(id, url){
13314        this.removeStyleSheet(id);
13315        var ss = doc.createElement("link");
13316        ss.setAttribute("rel", "stylesheet");
13317        ss.setAttribute("type", "text/css");
13318        ss.setAttribute("id", id);
13319        ss.setAttribute("href", url);
13320        doc.getElementsByTagName("head")[0].appendChild(ss);
13321    },
13322    
13323    /**
13324     * Refresh the rule cache if you have dynamically added stylesheets
13325     * @return {Object} An object (hash) of rules indexed by selector
13326     */
13327    refreshCache : function(){
13328        return this.getRules(true);
13329    },
13330
13331    // private
13332    cacheStyleSheet : function(stylesheet){
13333        if(!rules){
13334            rules = {};
13335        }
13336        try{// try catch for cross domain access issue
13337            var ssRules = stylesheet.cssRules || stylesheet.rules;
13338            for(var j = ssRules.length-1; j >= 0; --j){
13339                rules[ssRules[j].selectorText] = ssRules[j];
13340            }
13341        }catch(e){}
13342    },
13343    
13344    /**
13345     * Gets all css rules for the document
13346     * @param {Boolean} refreshCache true to refresh the internal cache
13347     * @return {Object} An object (hash) of rules indexed by selector
13348     */
13349    getRules : function(refreshCache){
13350                 if(rules == null || refreshCache){
13351                         rules = {};
13352                         var ds = doc.styleSheets;
13353                         for(var i =0, len = ds.length; i < len; i++){
13354                             try{
13355                         this.cacheStyleSheet(ds[i]);
13356                     }catch(e){} 
13357                 }
13358                 }
13359                 return rules;
13360         },
13361         
13362         /**
13363     * Gets an an individual CSS rule by selector(s)
13364     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13365     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13366     * @return {CSSRule} The CSS rule or null if one is not found
13367     */
13368    getRule : function(selector, refreshCache){
13369                 var rs = this.getRules(refreshCache);
13370                 if(!(selector instanceof Array)){
13371                     return rs[selector];
13372                 }
13373                 for(var i = 0; i < selector.length; i++){
13374                         if(rs[selector[i]]){
13375                                 return rs[selector[i]];
13376                         }
13377                 }
13378                 return null;
13379         },
13380         
13381         
13382         /**
13383     * Updates a rule property
13384     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13385     * @param {String} property The css property
13386     * @param {String} value The new value for the property
13387     * @return {Boolean} true If a rule was found and updated
13388     */
13389    updateRule : function(selector, property, value){
13390                 if(!(selector instanceof Array)){
13391                         var rule = this.getRule(selector);
13392                         if(rule){
13393                                 rule.style[property.replace(camelRe, camelFn)] = value;
13394                                 return true;
13395                         }
13396                 }else{
13397                         for(var i = 0; i < selector.length; i++){
13398                                 if(this.updateRule(selector[i], property, value)){
13399                                         return true;
13400                                 }
13401                         }
13402                 }
13403                 return false;
13404         }
13405    };   
13406 }();/*
13407  * Based on:
13408  * Ext JS Library 1.1.1
13409  * Copyright(c) 2006-2007, Ext JS, LLC.
13410  *
13411  * Originally Released Under LGPL - original licence link has changed is not relivant.
13412  *
13413  * Fork - LGPL
13414  * <script type="text/javascript">
13415  */
13416
13417  
13418
13419 /**
13420  * @class Roo.util.ClickRepeater
13421  * @extends Roo.util.Observable
13422  * 
13423  * A wrapper class which can be applied to any element. Fires a "click" event while the
13424  * mouse is pressed. The interval between firings may be specified in the config but
13425  * defaults to 10 milliseconds.
13426  * 
13427  * Optionally, a CSS class may be applied to the element during the time it is pressed.
13428  * 
13429  * @cfg {String/HTMLElement/Element} el The element to act as a button.
13430  * @cfg {Number} delay The initial delay before the repeating event begins firing.
13431  * Similar to an autorepeat key delay.
13432  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
13433  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
13434  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
13435  *           "interval" and "delay" are ignored. "immediate" is honored.
13436  * @cfg {Boolean} preventDefault True to prevent the default click event
13437  * @cfg {Boolean} stopDefault True to stop the default click event
13438  * 
13439  * @history
13440  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
13441  *     2007-02-02 jvs Renamed to ClickRepeater
13442  *   2007-02-03 jvs Modifications for FF Mac and Safari 
13443  *
13444  *  @constructor
13445  * @param {String/HTMLElement/Element} el The element to listen on
13446  * @param {Object} config
13447  **/
13448 Roo.util.ClickRepeater = function(el, config)
13449 {
13450     this.el = Roo.get(el);
13451     this.el.unselectable();
13452
13453     Roo.apply(this, config);
13454
13455     this.addEvents({
13456     /**
13457      * @event mousedown
13458      * Fires when the mouse button is depressed.
13459      * @param {Roo.util.ClickRepeater} this
13460      */
13461         "mousedown" : true,
13462     /**
13463      * @event click
13464      * Fires on a specified interval during the time the element is pressed.
13465      * @param {Roo.util.ClickRepeater} this
13466      */
13467         "click" : true,
13468     /**
13469      * @event mouseup
13470      * Fires when the mouse key is released.
13471      * @param {Roo.util.ClickRepeater} this
13472      */
13473         "mouseup" : true
13474     });
13475
13476     this.el.on("mousedown", this.handleMouseDown, this);
13477     if(this.preventDefault || this.stopDefault){
13478         this.el.on("click", function(e){
13479             if(this.preventDefault){
13480                 e.preventDefault();
13481             }
13482             if(this.stopDefault){
13483                 e.stopEvent();
13484             }
13485         }, this);
13486     }
13487
13488     // allow inline handler
13489     if(this.handler){
13490         this.on("click", this.handler,  this.scope || this);
13491     }
13492
13493     Roo.util.ClickRepeater.superclass.constructor.call(this);
13494 };
13495
13496 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
13497     interval : 20,
13498     delay: 250,
13499     preventDefault : true,
13500     stopDefault : false,
13501     timer : 0,
13502
13503     // private
13504     handleMouseDown : function(){
13505         clearTimeout(this.timer);
13506         this.el.blur();
13507         if(this.pressClass){
13508             this.el.addClass(this.pressClass);
13509         }
13510         this.mousedownTime = new Date();
13511
13512         Roo.get(document).on("mouseup", this.handleMouseUp, this);
13513         this.el.on("mouseout", this.handleMouseOut, this);
13514
13515         this.fireEvent("mousedown", this);
13516         this.fireEvent("click", this);
13517         
13518         this.timer = this.click.defer(this.delay || this.interval, this);
13519     },
13520
13521     // private
13522     click : function(){
13523         this.fireEvent("click", this);
13524         this.timer = this.click.defer(this.getInterval(), this);
13525     },
13526
13527     // private
13528     getInterval: function(){
13529         if(!this.accelerate){
13530             return this.interval;
13531         }
13532         var pressTime = this.mousedownTime.getElapsed();
13533         if(pressTime < 500){
13534             return 400;
13535         }else if(pressTime < 1700){
13536             return 320;
13537         }else if(pressTime < 2600){
13538             return 250;
13539         }else if(pressTime < 3500){
13540             return 180;
13541         }else if(pressTime < 4400){
13542             return 140;
13543         }else if(pressTime < 5300){
13544             return 80;
13545         }else if(pressTime < 6200){
13546             return 50;
13547         }else{
13548             return 10;
13549         }
13550     },
13551
13552     // private
13553     handleMouseOut : function(){
13554         clearTimeout(this.timer);
13555         if(this.pressClass){
13556             this.el.removeClass(this.pressClass);
13557         }
13558         this.el.on("mouseover", this.handleMouseReturn, this);
13559     },
13560
13561     // private
13562     handleMouseReturn : function(){
13563         this.el.un("mouseover", this.handleMouseReturn);
13564         if(this.pressClass){
13565             this.el.addClass(this.pressClass);
13566         }
13567         this.click();
13568     },
13569
13570     // private
13571     handleMouseUp : function(){
13572         clearTimeout(this.timer);
13573         this.el.un("mouseover", this.handleMouseReturn);
13574         this.el.un("mouseout", this.handleMouseOut);
13575         Roo.get(document).un("mouseup", this.handleMouseUp);
13576         this.el.removeClass(this.pressClass);
13577         this.fireEvent("mouseup", this);
13578     }
13579 });/*
13580  * Based on:
13581  * Ext JS Library 1.1.1
13582  * Copyright(c) 2006-2007, Ext JS, LLC.
13583  *
13584  * Originally Released Under LGPL - original licence link has changed is not relivant.
13585  *
13586  * Fork - LGPL
13587  * <script type="text/javascript">
13588  */
13589
13590  
13591 /**
13592  * @class Roo.KeyNav
13593  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
13594  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
13595  * way to implement custom navigation schemes for any UI component.</p>
13596  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
13597  * pageUp, pageDown, del, home, end.  Usage:</p>
13598  <pre><code>
13599 var nav = new Roo.KeyNav("my-element", {
13600     "left" : function(e){
13601         this.moveLeft(e.ctrlKey);
13602     },
13603     "right" : function(e){
13604         this.moveRight(e.ctrlKey);
13605     },
13606     "enter" : function(e){
13607         this.save();
13608     },
13609     scope : this
13610 });
13611 </code></pre>
13612  * @constructor
13613  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13614  * @param {Object} config The config
13615  */
13616 Roo.KeyNav = function(el, config){
13617     this.el = Roo.get(el);
13618     Roo.apply(this, config);
13619     if(!this.disabled){
13620         this.disabled = true;
13621         this.enable();
13622     }
13623 };
13624
13625 Roo.KeyNav.prototype = {
13626     /**
13627      * @cfg {Boolean} disabled
13628      * True to disable this KeyNav instance (defaults to false)
13629      */
13630     disabled : false,
13631     /**
13632      * @cfg {String} defaultEventAction
13633      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
13634      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
13635      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
13636      */
13637     defaultEventAction: "stopEvent",
13638     /**
13639      * @cfg {Boolean} forceKeyDown
13640      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
13641      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
13642      * handle keydown instead of keypress.
13643      */
13644     forceKeyDown : false,
13645
13646     // private
13647     prepareEvent : function(e){
13648         var k = e.getKey();
13649         var h = this.keyToHandler[k];
13650         //if(h && this[h]){
13651         //    e.stopPropagation();
13652         //}
13653         if(Roo.isSafari && h && k >= 37 && k <= 40){
13654             e.stopEvent();
13655         }
13656     },
13657
13658     // private
13659     relay : function(e){
13660         var k = e.getKey();
13661         var h = this.keyToHandler[k];
13662         if(h && this[h]){
13663             if(this.doRelay(e, this[h], h) !== true){
13664                 e[this.defaultEventAction]();
13665             }
13666         }
13667     },
13668
13669     // private
13670     doRelay : function(e, h, hname){
13671         return h.call(this.scope || this, e);
13672     },
13673
13674     // possible handlers
13675     enter : false,
13676     left : false,
13677     right : false,
13678     up : false,
13679     down : false,
13680     tab : false,
13681     esc : false,
13682     pageUp : false,
13683     pageDown : false,
13684     del : false,
13685     home : false,
13686     end : false,
13687
13688     // quick lookup hash
13689     keyToHandler : {
13690         37 : "left",
13691         39 : "right",
13692         38 : "up",
13693         40 : "down",
13694         33 : "pageUp",
13695         34 : "pageDown",
13696         46 : "del",
13697         36 : "home",
13698         35 : "end",
13699         13 : "enter",
13700         27 : "esc",
13701         9  : "tab"
13702     },
13703
13704         /**
13705          * Enable this KeyNav
13706          */
13707         enable: function(){
13708                 if(this.disabled){
13709             // ie won't do special keys on keypress, no one else will repeat keys with keydown
13710             // the EventObject will normalize Safari automatically
13711             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13712                 this.el.on("keydown", this.relay,  this);
13713             }else{
13714                 this.el.on("keydown", this.prepareEvent,  this);
13715                 this.el.on("keypress", this.relay,  this);
13716             }
13717                     this.disabled = false;
13718                 }
13719         },
13720
13721         /**
13722          * Disable this KeyNav
13723          */
13724         disable: function(){
13725                 if(!this.disabled){
13726                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
13727                 this.el.un("keydown", this.relay);
13728             }else{
13729                 this.el.un("keydown", this.prepareEvent);
13730                 this.el.un("keypress", this.relay);
13731             }
13732                     this.disabled = true;
13733                 }
13734         }
13735 };/*
13736  * Based on:
13737  * Ext JS Library 1.1.1
13738  * Copyright(c) 2006-2007, Ext JS, LLC.
13739  *
13740  * Originally Released Under LGPL - original licence link has changed is not relivant.
13741  *
13742  * Fork - LGPL
13743  * <script type="text/javascript">
13744  */
13745
13746  
13747 /**
13748  * @class Roo.KeyMap
13749  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
13750  * The constructor accepts the same config object as defined by {@link #addBinding}.
13751  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
13752  * combination it will call the function with this signature (if the match is a multi-key
13753  * combination the callback will still be called only once): (String key, Roo.EventObject e)
13754  * A KeyMap can also handle a string representation of keys.<br />
13755  * Usage:
13756  <pre><code>
13757 // map one key by key code
13758 var map = new Roo.KeyMap("my-element", {
13759     key: 13, // or Roo.EventObject.ENTER
13760     fn: myHandler,
13761     scope: myObject
13762 });
13763
13764 // map multiple keys to one action by string
13765 var map = new Roo.KeyMap("my-element", {
13766     key: "a\r\n\t",
13767     fn: myHandler,
13768     scope: myObject
13769 });
13770
13771 // map multiple keys to multiple actions by strings and array of codes
13772 var map = new Roo.KeyMap("my-element", [
13773     {
13774         key: [10,13],
13775         fn: function(){ alert("Return was pressed"); }
13776     }, {
13777         key: "abc",
13778         fn: function(){ alert('a, b or c was pressed'); }
13779     }, {
13780         key: "\t",
13781         ctrl:true,
13782         shift:true,
13783         fn: function(){ alert('Control + shift + tab was pressed.'); }
13784     }
13785 ]);
13786 </code></pre>
13787  * <b>Note: A KeyMap starts enabled</b>
13788  * @constructor
13789  * @param {String/HTMLElement/Roo.Element} el The element to bind to
13790  * @param {Object} config The config (see {@link #addBinding})
13791  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
13792  */
13793 Roo.KeyMap = function(el, config, eventName){
13794     this.el  = Roo.get(el);
13795     this.eventName = eventName || "keydown";
13796     this.bindings = [];
13797     if(config){
13798         this.addBinding(config);
13799     }
13800     this.enable();
13801 };
13802
13803 Roo.KeyMap.prototype = {
13804     /**
13805      * True to stop the event from bubbling and prevent the default browser action if the
13806      * key was handled by the KeyMap (defaults to false)
13807      * @type Boolean
13808      */
13809     stopEvent : false,
13810
13811     /**
13812      * Add a new binding to this KeyMap. The following config object properties are supported:
13813      * <pre>
13814 Property    Type             Description
13815 ----------  ---------------  ----------------------------------------------------------------------
13816 key         String/Array     A single keycode or an array of keycodes to handle
13817 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
13818 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
13819 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
13820 fn          Function         The function to call when KeyMap finds the expected key combination
13821 scope       Object           The scope of the callback function
13822 </pre>
13823      *
13824      * Usage:
13825      * <pre><code>
13826 // Create a KeyMap
13827 var map = new Roo.KeyMap(document, {
13828     key: Roo.EventObject.ENTER,
13829     fn: handleKey,
13830     scope: this
13831 });
13832
13833 //Add a new binding to the existing KeyMap later
13834 map.addBinding({
13835     key: 'abc',
13836     shift: true,
13837     fn: handleKey,
13838     scope: this
13839 });
13840 </code></pre>
13841      * @param {Object/Array} config A single KeyMap config or an array of configs
13842      */
13843         addBinding : function(config){
13844         if(config instanceof Array){
13845             for(var i = 0, len = config.length; i < len; i++){
13846                 this.addBinding(config[i]);
13847             }
13848             return;
13849         }
13850         var keyCode = config.key,
13851             shift = config.shift, 
13852             ctrl = config.ctrl, 
13853             alt = config.alt,
13854             fn = config.fn,
13855             scope = config.scope;
13856         if(typeof keyCode == "string"){
13857             var ks = [];
13858             var keyString = keyCode.toUpperCase();
13859             for(var j = 0, len = keyString.length; j < len; j++){
13860                 ks.push(keyString.charCodeAt(j));
13861             }
13862             keyCode = ks;
13863         }
13864         var keyArray = keyCode instanceof Array;
13865         var handler = function(e){
13866             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
13867                 var k = e.getKey();
13868                 if(keyArray){
13869                     for(var i = 0, len = keyCode.length; i < len; i++){
13870                         if(keyCode[i] == k){
13871                           if(this.stopEvent){
13872                               e.stopEvent();
13873                           }
13874                           fn.call(scope || window, k, e);
13875                           return;
13876                         }
13877                     }
13878                 }else{
13879                     if(k == keyCode){
13880                         if(this.stopEvent){
13881                            e.stopEvent();
13882                         }
13883                         fn.call(scope || window, k, e);
13884                     }
13885                 }
13886             }
13887         };
13888         this.bindings.push(handler);  
13889         },
13890
13891     /**
13892      * Shorthand for adding a single key listener
13893      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
13894      * following options:
13895      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
13896      * @param {Function} fn The function to call
13897      * @param {Object} scope (optional) The scope of the function
13898      */
13899     on : function(key, fn, scope){
13900         var keyCode, shift, ctrl, alt;
13901         if(typeof key == "object" && !(key instanceof Array)){
13902             keyCode = key.key;
13903             shift = key.shift;
13904             ctrl = key.ctrl;
13905             alt = key.alt;
13906         }else{
13907             keyCode = key;
13908         }
13909         this.addBinding({
13910             key: keyCode,
13911             shift: shift,
13912             ctrl: ctrl,
13913             alt: alt,
13914             fn: fn,
13915             scope: scope
13916         })
13917     },
13918
13919     // private
13920     handleKeyDown : function(e){
13921             if(this.enabled){ //just in case
13922             var b = this.bindings;
13923             for(var i = 0, len = b.length; i < len; i++){
13924                 b[i].call(this, e);
13925             }
13926             }
13927         },
13928         
13929         /**
13930          * Returns true if this KeyMap is enabled
13931          * @return {Boolean} 
13932          */
13933         isEnabled : function(){
13934             return this.enabled;  
13935         },
13936         
13937         /**
13938          * Enables this KeyMap
13939          */
13940         enable: function(){
13941                 if(!this.enabled){
13942                     this.el.on(this.eventName, this.handleKeyDown, this);
13943                     this.enabled = true;
13944                 }
13945         },
13946
13947         /**
13948          * Disable this KeyMap
13949          */
13950         disable: function(){
13951                 if(this.enabled){
13952                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
13953                     this.enabled = false;
13954                 }
13955         }
13956 };/*
13957  * Based on:
13958  * Ext JS Library 1.1.1
13959  * Copyright(c) 2006-2007, Ext JS, LLC.
13960  *
13961  * Originally Released Under LGPL - original licence link has changed is not relivant.
13962  *
13963  * Fork - LGPL
13964  * <script type="text/javascript">
13965  */
13966
13967  
13968 /**
13969  * @class Roo.util.TextMetrics
13970  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
13971  * wide, in pixels, a given block of text will be.
13972  * @singleton
13973  */
13974 Roo.util.TextMetrics = function(){
13975     var shared;
13976     return {
13977         /**
13978          * Measures the size of the specified text
13979          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
13980          * that can affect the size of the rendered text
13981          * @param {String} text The text to measure
13982          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
13983          * in order to accurately measure the text height
13984          * @return {Object} An object containing the text's size {width: (width), height: (height)}
13985          */
13986         measure : function(el, text, fixedWidth){
13987             if(!shared){
13988                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
13989             }
13990             shared.bind(el);
13991             shared.setFixedWidth(fixedWidth || 'auto');
13992             return shared.getSize(text);
13993         },
13994
13995         /**
13996          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
13997          * the overhead of multiple calls to initialize the style properties on each measurement.
13998          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
13999          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14000          * in order to accurately measure the text height
14001          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14002          */
14003         createInstance : function(el, fixedWidth){
14004             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14005         }
14006     };
14007 }();
14008
14009  
14010
14011 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14012     var ml = new Roo.Element(document.createElement('div'));
14013     document.body.appendChild(ml.dom);
14014     ml.position('absolute');
14015     ml.setLeftTop(-1000, -1000);
14016     ml.hide();
14017
14018     if(fixedWidth){
14019         ml.setWidth(fixedWidth);
14020     }
14021      
14022     var instance = {
14023         /**
14024          * Returns the size of the specified text based on the internal element's style and width properties
14025          * @memberOf Roo.util.TextMetrics.Instance#
14026          * @param {String} text The text to measure
14027          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14028          */
14029         getSize : function(text){
14030             ml.update(text);
14031             var s = ml.getSize();
14032             ml.update('');
14033             return s;
14034         },
14035
14036         /**
14037          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14038          * that can affect the size of the rendered text
14039          * @memberOf Roo.util.TextMetrics.Instance#
14040          * @param {String/HTMLElement} el The element, dom node or id
14041          */
14042         bind : function(el){
14043             ml.setStyle(
14044                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14045             );
14046         },
14047
14048         /**
14049          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14050          * to set a fixed width in order to accurately measure the text height.
14051          * @memberOf Roo.util.TextMetrics.Instance#
14052          * @param {Number} width The width to set on the element
14053          */
14054         setFixedWidth : function(width){
14055             ml.setWidth(width);
14056         },
14057
14058         /**
14059          * Returns the measured width of the specified text
14060          * @memberOf Roo.util.TextMetrics.Instance#
14061          * @param {String} text The text to measure
14062          * @return {Number} width The width in pixels
14063          */
14064         getWidth : function(text){
14065             ml.dom.style.width = 'auto';
14066             return this.getSize(text).width;
14067         },
14068
14069         /**
14070          * Returns the measured height of the specified text.  For multiline text, be sure to call
14071          * {@link #setFixedWidth} if necessary.
14072          * @memberOf Roo.util.TextMetrics.Instance#
14073          * @param {String} text The text to measure
14074          * @return {Number} height The height in pixels
14075          */
14076         getHeight : function(text){
14077             return this.getSize(text).height;
14078         }
14079     };
14080
14081     instance.bind(bindTo);
14082
14083     return instance;
14084 };
14085
14086 // backwards compat
14087 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14088  * Based on:
14089  * Ext JS Library 1.1.1
14090  * Copyright(c) 2006-2007, Ext JS, LLC.
14091  *
14092  * Originally Released Under LGPL - original licence link has changed is not relivant.
14093  *
14094  * Fork - LGPL
14095  * <script type="text/javascript">
14096  */
14097
14098 /**
14099  * @class Roo.state.Provider
14100  * Abstract base class for state provider implementations. This class provides methods
14101  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14102  * Provider interface.
14103  */
14104 Roo.state.Provider = function(){
14105     /**
14106      * @event statechange
14107      * Fires when a state change occurs.
14108      * @param {Provider} this This state provider
14109      * @param {String} key The state key which was changed
14110      * @param {String} value The encoded value for the state
14111      */
14112     this.addEvents({
14113         "statechange": true
14114     });
14115     this.state = {};
14116     Roo.state.Provider.superclass.constructor.call(this);
14117 };
14118 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14119     /**
14120      * Returns the current value for a key
14121      * @param {String} name The key name
14122      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14123      * @return {Mixed} The state data
14124      */
14125     get : function(name, defaultValue){
14126         return typeof this.state[name] == "undefined" ?
14127             defaultValue : this.state[name];
14128     },
14129     
14130     /**
14131      * Clears a value from the state
14132      * @param {String} name The key name
14133      */
14134     clear : function(name){
14135         delete this.state[name];
14136         this.fireEvent("statechange", this, name, null);
14137     },
14138     
14139     /**
14140      * Sets the value for a key
14141      * @param {String} name The key name
14142      * @param {Mixed} value The value to set
14143      */
14144     set : function(name, value){
14145         this.state[name] = value;
14146         this.fireEvent("statechange", this, name, value);
14147     },
14148     
14149     /**
14150      * Decodes a string previously encoded with {@link #encodeValue}.
14151      * @param {String} value The value to decode
14152      * @return {Mixed} The decoded value
14153      */
14154     decodeValue : function(cookie){
14155         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14156         var matches = re.exec(unescape(cookie));
14157         if(!matches || !matches[1]) return; // non state cookie
14158         var type = matches[1];
14159         var v = matches[2];
14160         switch(type){
14161             case "n":
14162                 return parseFloat(v);
14163             case "d":
14164                 return new Date(Date.parse(v));
14165             case "b":
14166                 return (v == "1");
14167             case "a":
14168                 var all = [];
14169                 var values = v.split("^");
14170                 for(var i = 0, len = values.length; i < len; i++){
14171                     all.push(this.decodeValue(values[i]));
14172                 }
14173                 return all;
14174            case "o":
14175                 var all = {};
14176                 var values = v.split("^");
14177                 for(var i = 0, len = values.length; i < len; i++){
14178                     var kv = values[i].split("=");
14179                     all[kv[0]] = this.decodeValue(kv[1]);
14180                 }
14181                 return all;
14182            default:
14183                 return v;
14184         }
14185     },
14186     
14187     /**
14188      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14189      * @param {Mixed} value The value to encode
14190      * @return {String} The encoded value
14191      */
14192     encodeValue : function(v){
14193         var enc;
14194         if(typeof v == "number"){
14195             enc = "n:" + v;
14196         }else if(typeof v == "boolean"){
14197             enc = "b:" + (v ? "1" : "0");
14198         }else if(v instanceof Date){
14199             enc = "d:" + v.toGMTString();
14200         }else if(v instanceof Array){
14201             var flat = "";
14202             for(var i = 0, len = v.length; i < len; i++){
14203                 flat += this.encodeValue(v[i]);
14204                 if(i != len-1) flat += "^";
14205             }
14206             enc = "a:" + flat;
14207         }else if(typeof v == "object"){
14208             var flat = "";
14209             for(var key in v){
14210                 if(typeof v[key] != "function"){
14211                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14212                 }
14213             }
14214             enc = "o:" + flat.substring(0, flat.length-1);
14215         }else{
14216             enc = "s:" + v;
14217         }
14218         return escape(enc);        
14219     }
14220 });
14221
14222 /*
14223  * Based on:
14224  * Ext JS Library 1.1.1
14225  * Copyright(c) 2006-2007, Ext JS, LLC.
14226  *
14227  * Originally Released Under LGPL - original licence link has changed is not relivant.
14228  *
14229  * Fork - LGPL
14230  * <script type="text/javascript">
14231  */
14232 /**
14233  * @class Roo.state.Manager
14234  * This is the global state manager. By default all components that are "state aware" check this class
14235  * for state information if you don't pass them a custom state provider. In order for this class
14236  * to be useful, it must be initialized with a provider when your application initializes.
14237  <pre><code>
14238 // in your initialization function
14239 init : function(){
14240    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14241    ...
14242    // supposed you have a {@link Roo.BorderLayout}
14243    var layout = new Roo.BorderLayout(...);
14244    layout.restoreState();
14245    // or a {Roo.BasicDialog}
14246    var dialog = new Roo.BasicDialog(...);
14247    dialog.restoreState();
14248  </code></pre>
14249  * @singleton
14250  */
14251 Roo.state.Manager = function(){
14252     var provider = new Roo.state.Provider();
14253     
14254     return {
14255         /**
14256          * Configures the default state provider for your application
14257          * @param {Provider} stateProvider The state provider to set
14258          */
14259         setProvider : function(stateProvider){
14260             provider = stateProvider;
14261         },
14262         
14263         /**
14264          * Returns the current value for a key
14265          * @param {String} name The key name
14266          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14267          * @return {Mixed} The state data
14268          */
14269         get : function(key, defaultValue){
14270             return provider.get(key, defaultValue);
14271         },
14272         
14273         /**
14274          * Sets the value for a key
14275          * @param {String} name The key name
14276          * @param {Mixed} value The state data
14277          */
14278          set : function(key, value){
14279             provider.set(key, value);
14280         },
14281         
14282         /**
14283          * Clears a value from the state
14284          * @param {String} name The key name
14285          */
14286         clear : function(key){
14287             provider.clear(key);
14288         },
14289         
14290         /**
14291          * Gets the currently configured state provider
14292          * @return {Provider} The state provider
14293          */
14294         getProvider : function(){
14295             return provider;
14296         }
14297     };
14298 }();
14299 /*
14300  * Based on:
14301  * Ext JS Library 1.1.1
14302  * Copyright(c) 2006-2007, Ext JS, LLC.
14303  *
14304  * Originally Released Under LGPL - original licence link has changed is not relivant.
14305  *
14306  * Fork - LGPL
14307  * <script type="text/javascript">
14308  */
14309 /**
14310  * @class Roo.state.CookieProvider
14311  * @extends Roo.state.Provider
14312  * The default Provider implementation which saves state via cookies.
14313  * <br />Usage:
14314  <pre><code>
14315    var cp = new Roo.state.CookieProvider({
14316        path: "/cgi-bin/",
14317        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14318        domain: "roojs.com"
14319    })
14320    Roo.state.Manager.setProvider(cp);
14321  </code></pre>
14322  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14323  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14324  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14325  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14326  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14327  * domain the page is running on including the 'www' like 'www.roojs.com')
14328  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14329  * @constructor
14330  * Create a new CookieProvider
14331  * @param {Object} config The configuration object
14332  */
14333 Roo.state.CookieProvider = function(config){
14334     Roo.state.CookieProvider.superclass.constructor.call(this);
14335     this.path = "/";
14336     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14337     this.domain = null;
14338     this.secure = false;
14339     Roo.apply(this, config);
14340     this.state = this.readCookies();
14341 };
14342
14343 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14344     // private
14345     set : function(name, value){
14346         if(typeof value == "undefined" || value === null){
14347             this.clear(name);
14348             return;
14349         }
14350         this.setCookie(name, value);
14351         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14352     },
14353
14354     // private
14355     clear : function(name){
14356         this.clearCookie(name);
14357         Roo.state.CookieProvider.superclass.clear.call(this, name);
14358     },
14359
14360     // private
14361     readCookies : function(){
14362         var cookies = {};
14363         var c = document.cookie + ";";
14364         var re = /\s?(.*?)=(.*?);/g;
14365         var matches;
14366         while((matches = re.exec(c)) != null){
14367             var name = matches[1];
14368             var value = matches[2];
14369             if(name && name.substring(0,3) == "ys-"){
14370                 cookies[name.substr(3)] = this.decodeValue(value);
14371             }
14372         }
14373         return cookies;
14374     },
14375
14376     // private
14377     setCookie : function(name, value){
14378         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14379            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14380            ((this.path == null) ? "" : ("; path=" + this.path)) +
14381            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14382            ((this.secure == true) ? "; secure" : "");
14383     },
14384
14385     // private
14386     clearCookie : function(name){
14387         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14388            ((this.path == null) ? "" : ("; path=" + this.path)) +
14389            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14390            ((this.secure == true) ? "; secure" : "");
14391     }
14392 });/*
14393  * Based on:
14394  * Ext JS Library 1.1.1
14395  * Copyright(c) 2006-2007, Ext JS, LLC.
14396  *
14397  * Originally Released Under LGPL - original licence link has changed is not relivant.
14398  *
14399  * Fork - LGPL
14400  * <script type="text/javascript">
14401  */
14402
14403
14404
14405 /*
14406  * These classes are derivatives of the similarly named classes in the YUI Library.
14407  * The original license:
14408  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
14409  * Code licensed under the BSD License:
14410  * http://developer.yahoo.net/yui/license.txt
14411  */
14412
14413 (function() {
14414
14415 var Event=Roo.EventManager;
14416 var Dom=Roo.lib.Dom;
14417
14418 /**
14419  * @class Roo.dd.DragDrop
14420  * @extends Roo.util.Observable
14421  * Defines the interface and base operation of items that that can be
14422  * dragged or can be drop targets.  It was designed to be extended, overriding
14423  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
14424  * Up to three html elements can be associated with a DragDrop instance:
14425  * <ul>
14426  * <li>linked element: the element that is passed into the constructor.
14427  * This is the element which defines the boundaries for interaction with
14428  * other DragDrop objects.</li>
14429  * <li>handle element(s): The drag operation only occurs if the element that
14430  * was clicked matches a handle element.  By default this is the linked
14431  * element, but there are times that you will want only a portion of the
14432  * linked element to initiate the drag operation, and the setHandleElId()
14433  * method provides a way to define this.</li>
14434  * <li>drag element: this represents the element that would be moved along
14435  * with the cursor during a drag operation.  By default, this is the linked
14436  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
14437  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
14438  * </li>
14439  * </ul>
14440  * This class should not be instantiated until the onload event to ensure that
14441  * the associated elements are available.
14442  * The following would define a DragDrop obj that would interact with any
14443  * other DragDrop obj in the "group1" group:
14444  * <pre>
14445  *  dd = new Roo.dd.DragDrop("div1", "group1");
14446  * </pre>
14447  * Since none of the event handlers have been implemented, nothing would
14448  * actually happen if you were to run the code above.  Normally you would
14449  * override this class or one of the default implementations, but you can
14450  * also override the methods you want on an instance of the class...
14451  * <pre>
14452  *  dd.onDragDrop = function(e, id) {
14453  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
14454  *  }
14455  * </pre>
14456  * @constructor
14457  * @param {String} id of the element that is linked to this instance
14458  * @param {String} sGroup the group of related DragDrop objects
14459  * @param {object} config an object containing configurable attributes
14460  *                Valid properties for DragDrop:
14461  *                    padding, isTarget, maintainOffset, primaryButtonOnly
14462  */
14463 Roo.dd.DragDrop = function(id, sGroup, config) {
14464     if (id) {
14465         this.init(id, sGroup, config);
14466     }
14467     
14468 };
14469
14470 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
14471
14472     /**
14473      * The id of the element associated with this object.  This is what we
14474      * refer to as the "linked element" because the size and position of
14475      * this element is used to determine when the drag and drop objects have
14476      * interacted.
14477      * @property id
14478      * @type String
14479      */
14480     id: null,
14481
14482     /**
14483      * Configuration attributes passed into the constructor
14484      * @property config
14485      * @type object
14486      */
14487     config: null,
14488
14489     /**
14490      * The id of the element that will be dragged.  By default this is same
14491      * as the linked element , but could be changed to another element. Ex:
14492      * Roo.dd.DDProxy
14493      * @property dragElId
14494      * @type String
14495      * @private
14496      */
14497     dragElId: null,
14498
14499     /**
14500      * the id of the element that initiates the drag operation.  By default
14501      * this is the linked element, but could be changed to be a child of this
14502      * element.  This lets us do things like only starting the drag when the
14503      * header element within the linked html element is clicked.
14504      * @property handleElId
14505      * @type String
14506      * @private
14507      */
14508     handleElId: null,
14509
14510     /**
14511      * An associative array of HTML tags that will be ignored if clicked.
14512      * @property invalidHandleTypes
14513      * @type {string: string}
14514      */
14515     invalidHandleTypes: null,
14516
14517     /**
14518      * An associative array of ids for elements that will be ignored if clicked
14519      * @property invalidHandleIds
14520      * @type {string: string}
14521      */
14522     invalidHandleIds: null,
14523
14524     /**
14525      * An indexted array of css class names for elements that will be ignored
14526      * if clicked.
14527      * @property invalidHandleClasses
14528      * @type string[]
14529      */
14530     invalidHandleClasses: null,
14531
14532     /**
14533      * The linked element's absolute X position at the time the drag was
14534      * started
14535      * @property startPageX
14536      * @type int
14537      * @private
14538      */
14539     startPageX: 0,
14540
14541     /**
14542      * The linked element's absolute X position at the time the drag was
14543      * started
14544      * @property startPageY
14545      * @type int
14546      * @private
14547      */
14548     startPageY: 0,
14549
14550     /**
14551      * The group defines a logical collection of DragDrop objects that are
14552      * related.  Instances only get events when interacting with other
14553      * DragDrop object in the same group.  This lets us define multiple
14554      * groups using a single DragDrop subclass if we want.
14555      * @property groups
14556      * @type {string: string}
14557      */
14558     groups: null,
14559
14560     /**
14561      * Individual drag/drop instances can be locked.  This will prevent
14562      * onmousedown start drag.
14563      * @property locked
14564      * @type boolean
14565      * @private
14566      */
14567     locked: false,
14568
14569     /**
14570      * Lock this instance
14571      * @method lock
14572      */
14573     lock: function() { this.locked = true; },
14574
14575     /**
14576      * Unlock this instace
14577      * @method unlock
14578      */
14579     unlock: function() { this.locked = false; },
14580
14581     /**
14582      * By default, all insances can be a drop target.  This can be disabled by
14583      * setting isTarget to false.
14584      * @method isTarget
14585      * @type boolean
14586      */
14587     isTarget: true,
14588
14589     /**
14590      * The padding configured for this drag and drop object for calculating
14591      * the drop zone intersection with this object.
14592      * @method padding
14593      * @type int[]
14594      */
14595     padding: null,
14596
14597     /**
14598      * Cached reference to the linked element
14599      * @property _domRef
14600      * @private
14601      */
14602     _domRef: null,
14603
14604     /**
14605      * Internal typeof flag
14606      * @property __ygDragDrop
14607      * @private
14608      */
14609     __ygDragDrop: true,
14610
14611     /**
14612      * Set to true when horizontal contraints are applied
14613      * @property constrainX
14614      * @type boolean
14615      * @private
14616      */
14617     constrainX: false,
14618
14619     /**
14620      * Set to true when vertical contraints are applied
14621      * @property constrainY
14622      * @type boolean
14623      * @private
14624      */
14625     constrainY: false,
14626
14627     /**
14628      * The left constraint
14629      * @property minX
14630      * @type int
14631      * @private
14632      */
14633     minX: 0,
14634
14635     /**
14636      * The right constraint
14637      * @property maxX
14638      * @type int
14639      * @private
14640      */
14641     maxX: 0,
14642
14643     /**
14644      * The up constraint
14645      * @property minY
14646      * @type int
14647      * @type int
14648      * @private
14649      */
14650     minY: 0,
14651
14652     /**
14653      * The down constraint
14654      * @property maxY
14655      * @type int
14656      * @private
14657      */
14658     maxY: 0,
14659
14660     /**
14661      * Maintain offsets when we resetconstraints.  Set to true when you want
14662      * the position of the element relative to its parent to stay the same
14663      * when the page changes
14664      *
14665      * @property maintainOffset
14666      * @type boolean
14667      */
14668     maintainOffset: false,
14669
14670     /**
14671      * Array of pixel locations the element will snap to if we specified a
14672      * horizontal graduation/interval.  This array is generated automatically
14673      * when you define a tick interval.
14674      * @property xTicks
14675      * @type int[]
14676      */
14677     xTicks: null,
14678
14679     /**
14680      * Array of pixel locations the element will snap to if we specified a
14681      * vertical graduation/interval.  This array is generated automatically
14682      * when you define a tick interval.
14683      * @property yTicks
14684      * @type int[]
14685      */
14686     yTicks: null,
14687
14688     /**
14689      * By default the drag and drop instance will only respond to the primary
14690      * button click (left button for a right-handed mouse).  Set to true to
14691      * allow drag and drop to start with any mouse click that is propogated
14692      * by the browser
14693      * @property primaryButtonOnly
14694      * @type boolean
14695      */
14696     primaryButtonOnly: true,
14697
14698     /**
14699      * The availabe property is false until the linked dom element is accessible.
14700      * @property available
14701      * @type boolean
14702      */
14703     available: false,
14704
14705     /**
14706      * By default, drags can only be initiated if the mousedown occurs in the
14707      * region the linked element is.  This is done in part to work around a
14708      * bug in some browsers that mis-report the mousedown if the previous
14709      * mouseup happened outside of the window.  This property is set to true
14710      * if outer handles are defined.
14711      *
14712      * @property hasOuterHandles
14713      * @type boolean
14714      * @default false
14715      */
14716     hasOuterHandles: false,
14717
14718     /**
14719      * Code that executes immediately before the startDrag event
14720      * @method b4StartDrag
14721      * @private
14722      */
14723     b4StartDrag: function(x, y) { },
14724
14725     /**
14726      * Abstract method called after a drag/drop object is clicked
14727      * and the drag or mousedown time thresholds have beeen met.
14728      * @method startDrag
14729      * @param {int} X click location
14730      * @param {int} Y click location
14731      */
14732     startDrag: function(x, y) { /* override this */ },
14733
14734     /**
14735      * Code that executes immediately before the onDrag event
14736      * @method b4Drag
14737      * @private
14738      */
14739     b4Drag: function(e) { },
14740
14741     /**
14742      * Abstract method called during the onMouseMove event while dragging an
14743      * object.
14744      * @method onDrag
14745      * @param {Event} e the mousemove event
14746      */
14747     onDrag: function(e) { /* override this */ },
14748
14749     /**
14750      * Abstract method called when this element fist begins hovering over
14751      * another DragDrop obj
14752      * @method onDragEnter
14753      * @param {Event} e the mousemove event
14754      * @param {String|DragDrop[]} id In POINT mode, the element
14755      * id this is hovering over.  In INTERSECT mode, an array of one or more
14756      * dragdrop items being hovered over.
14757      */
14758     onDragEnter: function(e, id) { /* override this */ },
14759
14760     /**
14761      * Code that executes immediately before the onDragOver event
14762      * @method b4DragOver
14763      * @private
14764      */
14765     b4DragOver: function(e) { },
14766
14767     /**
14768      * Abstract method called when this element is hovering over another
14769      * DragDrop obj
14770      * @method onDragOver
14771      * @param {Event} e the mousemove event
14772      * @param {String|DragDrop[]} id In POINT mode, the element
14773      * id this is hovering over.  In INTERSECT mode, an array of dd items
14774      * being hovered over.
14775      */
14776     onDragOver: function(e, id) { /* override this */ },
14777
14778     /**
14779      * Code that executes immediately before the onDragOut event
14780      * @method b4DragOut
14781      * @private
14782      */
14783     b4DragOut: function(e) { },
14784
14785     /**
14786      * Abstract method called when we are no longer hovering over an element
14787      * @method onDragOut
14788      * @param {Event} e the mousemove event
14789      * @param {String|DragDrop[]} id In POINT mode, the element
14790      * id this was hovering over.  In INTERSECT mode, an array of dd items
14791      * that the mouse is no longer over.
14792      */
14793     onDragOut: function(e, id) { /* override this */ },
14794
14795     /**
14796      * Code that executes immediately before the onDragDrop event
14797      * @method b4DragDrop
14798      * @private
14799      */
14800     b4DragDrop: function(e) { },
14801
14802     /**
14803      * Abstract method called when this item is dropped on another DragDrop
14804      * obj
14805      * @method onDragDrop
14806      * @param {Event} e the mouseup event
14807      * @param {String|DragDrop[]} id In POINT mode, the element
14808      * id this was dropped on.  In INTERSECT mode, an array of dd items this
14809      * was dropped on.
14810      */
14811     onDragDrop: function(e, id) { /* override this */ },
14812
14813     /**
14814      * Abstract method called when this item is dropped on an area with no
14815      * drop target
14816      * @method onInvalidDrop
14817      * @param {Event} e the mouseup event
14818      */
14819     onInvalidDrop: function(e) { /* override this */ },
14820
14821     /**
14822      * Code that executes immediately before the endDrag event
14823      * @method b4EndDrag
14824      * @private
14825      */
14826     b4EndDrag: function(e) { },
14827
14828     /**
14829      * Fired when we are done dragging the object
14830      * @method endDrag
14831      * @param {Event} e the mouseup event
14832      */
14833     endDrag: function(e) { /* override this */ },
14834
14835     /**
14836      * Code executed immediately before the onMouseDown event
14837      * @method b4MouseDown
14838      * @param {Event} e the mousedown event
14839      * @private
14840      */
14841     b4MouseDown: function(e) {  },
14842
14843     /**
14844      * Event handler that fires when a drag/drop obj gets a mousedown
14845      * @method onMouseDown
14846      * @param {Event} e the mousedown event
14847      */
14848     onMouseDown: function(e) { /* override this */ },
14849
14850     /**
14851      * Event handler that fires when a drag/drop obj gets a mouseup
14852      * @method onMouseUp
14853      * @param {Event} e the mouseup event
14854      */
14855     onMouseUp: function(e) { /* override this */ },
14856
14857     /**
14858      * Override the onAvailable method to do what is needed after the initial
14859      * position was determined.
14860      * @method onAvailable
14861      */
14862     onAvailable: function () {
14863     },
14864
14865     /*
14866      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
14867      * @type Object
14868      */
14869     defaultPadding : {left:0, right:0, top:0, bottom:0},
14870
14871     /*
14872      * Initializes the drag drop object's constraints to restrict movement to a certain element.
14873  *
14874  * Usage:
14875  <pre><code>
14876  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
14877                 { dragElId: "existingProxyDiv" });
14878  dd.startDrag = function(){
14879      this.constrainTo("parent-id");
14880  };
14881  </code></pre>
14882  * Or you can initalize it using the {@link Roo.Element} object:
14883  <pre><code>
14884  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
14885      startDrag : function(){
14886          this.constrainTo("parent-id");
14887      }
14888  });
14889  </code></pre>
14890      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
14891      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
14892      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
14893      * an object containing the sides to pad. For example: {right:10, bottom:10}
14894      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
14895      */
14896     constrainTo : function(constrainTo, pad, inContent){
14897         if(typeof pad == "number"){
14898             pad = {left: pad, right:pad, top:pad, bottom:pad};
14899         }
14900         pad = pad || this.defaultPadding;
14901         var b = Roo.get(this.getEl()).getBox();
14902         var ce = Roo.get(constrainTo);
14903         var s = ce.getScroll();
14904         var c, cd = ce.dom;
14905         if(cd == document.body){
14906             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
14907         }else{
14908             xy = ce.getXY();
14909             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
14910         }
14911
14912
14913         var topSpace = b.y - c.y;
14914         var leftSpace = b.x - c.x;
14915
14916         this.resetConstraints();
14917         this.setXConstraint(leftSpace - (pad.left||0), // left
14918                 c.width - leftSpace - b.width - (pad.right||0) //right
14919         );
14920         this.setYConstraint(topSpace - (pad.top||0), //top
14921                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
14922         );
14923     },
14924
14925     /**
14926      * Returns a reference to the linked element
14927      * @method getEl
14928      * @return {HTMLElement} the html element
14929      */
14930     getEl: function() {
14931         if (!this._domRef) {
14932             this._domRef = Roo.getDom(this.id);
14933         }
14934
14935         return this._domRef;
14936     },
14937
14938     /**
14939      * Returns a reference to the actual element to drag.  By default this is
14940      * the same as the html element, but it can be assigned to another
14941      * element. An example of this can be found in Roo.dd.DDProxy
14942      * @method getDragEl
14943      * @return {HTMLElement} the html element
14944      */
14945     getDragEl: function() {
14946         return Roo.getDom(this.dragElId);
14947     },
14948
14949     /**
14950      * Sets up the DragDrop object.  Must be called in the constructor of any
14951      * Roo.dd.DragDrop subclass
14952      * @method init
14953      * @param id the id of the linked element
14954      * @param {String} sGroup the group of related items
14955      * @param {object} config configuration attributes
14956      */
14957     init: function(id, sGroup, config) {
14958         this.initTarget(id, sGroup, config);
14959         Event.on(this.id, "mousedown", this.handleMouseDown, this);
14960         // Event.on(this.id, "selectstart", Event.preventDefault);
14961     },
14962
14963     /**
14964      * Initializes Targeting functionality only... the object does not
14965      * get a mousedown handler.
14966      * @method initTarget
14967      * @param id the id of the linked element
14968      * @param {String} sGroup the group of related items
14969      * @param {object} config configuration attributes
14970      */
14971     initTarget: function(id, sGroup, config) {
14972
14973         // configuration attributes
14974         this.config = config || {};
14975
14976         // create a local reference to the drag and drop manager
14977         this.DDM = Roo.dd.DDM;
14978         // initialize the groups array
14979         this.groups = {};
14980
14981         // assume that we have an element reference instead of an id if the
14982         // parameter is not a string
14983         if (typeof id !== "string") {
14984             id = Roo.id(id);
14985         }
14986
14987         // set the id
14988         this.id = id;
14989
14990         // add to an interaction group
14991         this.addToGroup((sGroup) ? sGroup : "default");
14992
14993         // We don't want to register this as the handle with the manager
14994         // so we just set the id rather than calling the setter.
14995         this.handleElId = id;
14996
14997         // the linked element is the element that gets dragged by default
14998         this.setDragElId(id);
14999
15000         // by default, clicked anchors will not start drag operations.
15001         this.invalidHandleTypes = { A: "A" };
15002         this.invalidHandleIds = {};
15003         this.invalidHandleClasses = [];
15004
15005         this.applyConfig();
15006
15007         this.handleOnAvailable();
15008     },
15009
15010     /**
15011      * Applies the configuration parameters that were passed into the constructor.
15012      * This is supposed to happen at each level through the inheritance chain.  So
15013      * a DDProxy implentation will execute apply config on DDProxy, DD, and
15014      * DragDrop in order to get all of the parameters that are available in
15015      * each object.
15016      * @method applyConfig
15017      */
15018     applyConfig: function() {
15019
15020         // configurable properties:
15021         //    padding, isTarget, maintainOffset, primaryButtonOnly
15022         this.padding           = this.config.padding || [0, 0, 0, 0];
15023         this.isTarget          = (this.config.isTarget !== false);
15024         this.maintainOffset    = (this.config.maintainOffset);
15025         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
15026
15027     },
15028
15029     /**
15030      * Executed when the linked element is available
15031      * @method handleOnAvailable
15032      * @private
15033      */
15034     handleOnAvailable: function() {
15035         this.available = true;
15036         this.resetConstraints();
15037         this.onAvailable();
15038     },
15039
15040      /**
15041      * Configures the padding for the target zone in px.  Effectively expands
15042      * (or reduces) the virtual object size for targeting calculations.
15043      * Supports css-style shorthand; if only one parameter is passed, all sides
15044      * will have that padding, and if only two are passed, the top and bottom
15045      * will have the first param, the left and right the second.
15046      * @method setPadding
15047      * @param {int} iTop    Top pad
15048      * @param {int} iRight  Right pad
15049      * @param {int} iBot    Bot pad
15050      * @param {int} iLeft   Left pad
15051      */
15052     setPadding: function(iTop, iRight, iBot, iLeft) {
15053         // this.padding = [iLeft, iRight, iTop, iBot];
15054         if (!iRight && 0 !== iRight) {
15055             this.padding = [iTop, iTop, iTop, iTop];
15056         } else if (!iBot && 0 !== iBot) {
15057             this.padding = [iTop, iRight, iTop, iRight];
15058         } else {
15059             this.padding = [iTop, iRight, iBot, iLeft];
15060         }
15061     },
15062
15063     /**
15064      * Stores the initial placement of the linked element.
15065      * @method setInitialPosition
15066      * @param {int} diffX   the X offset, default 0
15067      * @param {int} diffY   the Y offset, default 0
15068      */
15069     setInitPosition: function(diffX, diffY) {
15070         var el = this.getEl();
15071
15072         if (!this.DDM.verifyEl(el)) {
15073             return;
15074         }
15075
15076         var dx = diffX || 0;
15077         var dy = diffY || 0;
15078
15079         var p = Dom.getXY( el );
15080
15081         this.initPageX = p[0] - dx;
15082         this.initPageY = p[1] - dy;
15083
15084         this.lastPageX = p[0];
15085         this.lastPageY = p[1];
15086
15087
15088         this.setStartPosition(p);
15089     },
15090
15091     /**
15092      * Sets the start position of the element.  This is set when the obj
15093      * is initialized, the reset when a drag is started.
15094      * @method setStartPosition
15095      * @param pos current position (from previous lookup)
15096      * @private
15097      */
15098     setStartPosition: function(pos) {
15099         var p = pos || Dom.getXY( this.getEl() );
15100         this.deltaSetXY = null;
15101
15102         this.startPageX = p[0];
15103         this.startPageY = p[1];
15104     },
15105
15106     /**
15107      * Add this instance to a group of related drag/drop objects.  All
15108      * instances belong to at least one group, and can belong to as many
15109      * groups as needed.
15110      * @method addToGroup
15111      * @param sGroup {string} the name of the group
15112      */
15113     addToGroup: function(sGroup) {
15114         this.groups[sGroup] = true;
15115         this.DDM.regDragDrop(this, sGroup);
15116     },
15117
15118     /**
15119      * Remove's this instance from the supplied interaction group
15120      * @method removeFromGroup
15121      * @param {string}  sGroup  The group to drop
15122      */
15123     removeFromGroup: function(sGroup) {
15124         if (this.groups[sGroup]) {
15125             delete this.groups[sGroup];
15126         }
15127
15128         this.DDM.removeDDFromGroup(this, sGroup);
15129     },
15130
15131     /**
15132      * Allows you to specify that an element other than the linked element
15133      * will be moved with the cursor during a drag
15134      * @method setDragElId
15135      * @param id {string} the id of the element that will be used to initiate the drag
15136      */
15137     setDragElId: function(id) {
15138         this.dragElId = id;
15139     },
15140
15141     /**
15142      * Allows you to specify a child of the linked element that should be
15143      * used to initiate the drag operation.  An example of this would be if
15144      * you have a content div with text and links.  Clicking anywhere in the
15145      * content area would normally start the drag operation.  Use this method
15146      * to specify that an element inside of the content div is the element
15147      * that starts the drag operation.
15148      * @method setHandleElId
15149      * @param id {string} the id of the element that will be used to
15150      * initiate the drag.
15151      */
15152     setHandleElId: function(id) {
15153         if (typeof id !== "string") {
15154             id = Roo.id(id);
15155         }
15156         this.handleElId = id;
15157         this.DDM.regHandle(this.id, id);
15158     },
15159
15160     /**
15161      * Allows you to set an element outside of the linked element as a drag
15162      * handle
15163      * @method setOuterHandleElId
15164      * @param id the id of the element that will be used to initiate the drag
15165      */
15166     setOuterHandleElId: function(id) {
15167         if (typeof id !== "string") {
15168             id = Roo.id(id);
15169         }
15170         Event.on(id, "mousedown",
15171                 this.handleMouseDown, this);
15172         this.setHandleElId(id);
15173
15174         this.hasOuterHandles = true;
15175     },
15176
15177     /**
15178      * Remove all drag and drop hooks for this element
15179      * @method unreg
15180      */
15181     unreg: function() {
15182         Event.un(this.id, "mousedown",
15183                 this.handleMouseDown);
15184         this._domRef = null;
15185         this.DDM._remove(this);
15186     },
15187
15188     destroy : function(){
15189         this.unreg();
15190     },
15191
15192     /**
15193      * Returns true if this instance is locked, or the drag drop mgr is locked
15194      * (meaning that all drag/drop is disabled on the page.)
15195      * @method isLocked
15196      * @return {boolean} true if this obj or all drag/drop is locked, else
15197      * false
15198      */
15199     isLocked: function() {
15200         return (this.DDM.isLocked() || this.locked);
15201     },
15202
15203     /**
15204      * Fired when this object is clicked
15205      * @method handleMouseDown
15206      * @param {Event} e
15207      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
15208      * @private
15209      */
15210     handleMouseDown: function(e, oDD){
15211         if (this.primaryButtonOnly && e.button != 0) {
15212             return;
15213         }
15214
15215         if (this.isLocked()) {
15216             return;
15217         }
15218
15219         this.DDM.refreshCache(this.groups);
15220
15221         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
15222         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
15223         } else {
15224             if (this.clickValidator(e)) {
15225
15226                 // set the initial element position
15227                 this.setStartPosition();
15228
15229
15230                 this.b4MouseDown(e);
15231                 this.onMouseDown(e);
15232
15233                 this.DDM.handleMouseDown(e, this);
15234
15235                 this.DDM.stopEvent(e);
15236             } else {
15237
15238
15239             }
15240         }
15241     },
15242
15243     clickValidator: function(e) {
15244         var target = e.getTarget();
15245         return ( this.isValidHandleChild(target) &&
15246                     (this.id == this.handleElId ||
15247                         this.DDM.handleWasClicked(target, this.id)) );
15248     },
15249
15250     /**
15251      * Allows you to specify a tag name that should not start a drag operation
15252      * when clicked.  This is designed to facilitate embedding links within a
15253      * drag handle that do something other than start the drag.
15254      * @method addInvalidHandleType
15255      * @param {string} tagName the type of element to exclude
15256      */
15257     addInvalidHandleType: function(tagName) {
15258         var type = tagName.toUpperCase();
15259         this.invalidHandleTypes[type] = type;
15260     },
15261
15262     /**
15263      * Lets you to specify an element id for a child of a drag handle
15264      * that should not initiate a drag
15265      * @method addInvalidHandleId
15266      * @param {string} id the element id of the element you wish to ignore
15267      */
15268     addInvalidHandleId: function(id) {
15269         if (typeof id !== "string") {
15270             id = Roo.id(id);
15271         }
15272         this.invalidHandleIds[id] = id;
15273     },
15274
15275     /**
15276      * Lets you specify a css class of elements that will not initiate a drag
15277      * @method addInvalidHandleClass
15278      * @param {string} cssClass the class of the elements you wish to ignore
15279      */
15280     addInvalidHandleClass: function(cssClass) {
15281         this.invalidHandleClasses.push(cssClass);
15282     },
15283
15284     /**
15285      * Unsets an excluded tag name set by addInvalidHandleType
15286      * @method removeInvalidHandleType
15287      * @param {string} tagName the type of element to unexclude
15288      */
15289     removeInvalidHandleType: function(tagName) {
15290         var type = tagName.toUpperCase();
15291         // this.invalidHandleTypes[type] = null;
15292         delete this.invalidHandleTypes[type];
15293     },
15294
15295     /**
15296      * Unsets an invalid handle id
15297      * @method removeInvalidHandleId
15298      * @param {string} id the id of the element to re-enable
15299      */
15300     removeInvalidHandleId: function(id) {
15301         if (typeof id !== "string") {
15302             id = Roo.id(id);
15303         }
15304         delete this.invalidHandleIds[id];
15305     },
15306
15307     /**
15308      * Unsets an invalid css class
15309      * @method removeInvalidHandleClass
15310      * @param {string} cssClass the class of the element(s) you wish to
15311      * re-enable
15312      */
15313     removeInvalidHandleClass: function(cssClass) {
15314         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
15315             if (this.invalidHandleClasses[i] == cssClass) {
15316                 delete this.invalidHandleClasses[i];
15317             }
15318         }
15319     },
15320
15321     /**
15322      * Checks the tag exclusion list to see if this click should be ignored
15323      * @method isValidHandleChild
15324      * @param {HTMLElement} node the HTMLElement to evaluate
15325      * @return {boolean} true if this is a valid tag type, false if not
15326      */
15327     isValidHandleChild: function(node) {
15328
15329         var valid = true;
15330         // var n = (node.nodeName == "#text") ? node.parentNode : node;
15331         var nodeName;
15332         try {
15333             nodeName = node.nodeName.toUpperCase();
15334         } catch(e) {
15335             nodeName = node.nodeName;
15336         }
15337         valid = valid && !this.invalidHandleTypes[nodeName];
15338         valid = valid && !this.invalidHandleIds[node.id];
15339
15340         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
15341             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
15342         }
15343
15344
15345         return valid;
15346
15347     },
15348
15349     /**
15350      * Create the array of horizontal tick marks if an interval was specified
15351      * in setXConstraint().
15352      * @method setXTicks
15353      * @private
15354      */
15355     setXTicks: function(iStartX, iTickSize) {
15356         this.xTicks = [];
15357         this.xTickSize = iTickSize;
15358
15359         var tickMap = {};
15360
15361         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
15362             if (!tickMap[i]) {
15363                 this.xTicks[this.xTicks.length] = i;
15364                 tickMap[i] = true;
15365             }
15366         }
15367
15368         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
15369             if (!tickMap[i]) {
15370                 this.xTicks[this.xTicks.length] = i;
15371                 tickMap[i] = true;
15372             }
15373         }
15374
15375         this.xTicks.sort(this.DDM.numericSort) ;
15376     },
15377
15378     /**
15379      * Create the array of vertical tick marks if an interval was specified in
15380      * setYConstraint().
15381      * @method setYTicks
15382      * @private
15383      */
15384     setYTicks: function(iStartY, iTickSize) {
15385         this.yTicks = [];
15386         this.yTickSize = iTickSize;
15387
15388         var tickMap = {};
15389
15390         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
15391             if (!tickMap[i]) {
15392                 this.yTicks[this.yTicks.length] = i;
15393                 tickMap[i] = true;
15394             }
15395         }
15396
15397         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
15398             if (!tickMap[i]) {
15399                 this.yTicks[this.yTicks.length] = i;
15400                 tickMap[i] = true;
15401             }
15402         }
15403
15404         this.yTicks.sort(this.DDM.numericSort) ;
15405     },
15406
15407     /**
15408      * By default, the element can be dragged any place on the screen.  Use
15409      * this method to limit the horizontal travel of the element.  Pass in
15410      * 0,0 for the parameters if you want to lock the drag to the y axis.
15411      * @method setXConstraint
15412      * @param {int} iLeft the number of pixels the element can move to the left
15413      * @param {int} iRight the number of pixels the element can move to the
15414      * right
15415      * @param {int} iTickSize optional parameter for specifying that the
15416      * element
15417      * should move iTickSize pixels at a time.
15418      */
15419     setXConstraint: function(iLeft, iRight, iTickSize) {
15420         this.leftConstraint = iLeft;
15421         this.rightConstraint = iRight;
15422
15423         this.minX = this.initPageX - iLeft;
15424         this.maxX = this.initPageX + iRight;
15425         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
15426
15427         this.constrainX = true;
15428     },
15429
15430     /**
15431      * Clears any constraints applied to this instance.  Also clears ticks
15432      * since they can't exist independent of a constraint at this time.
15433      * @method clearConstraints
15434      */
15435     clearConstraints: function() {
15436         this.constrainX = false;
15437         this.constrainY = false;
15438         this.clearTicks();
15439     },
15440
15441     /**
15442      * Clears any tick interval defined for this instance
15443      * @method clearTicks
15444      */
15445     clearTicks: function() {
15446         this.xTicks = null;
15447         this.yTicks = null;
15448         this.xTickSize = 0;
15449         this.yTickSize = 0;
15450     },
15451
15452     /**
15453      * By default, the element can be dragged any place on the screen.  Set
15454      * this to limit the vertical travel of the element.  Pass in 0,0 for the
15455      * parameters if you want to lock the drag to the x axis.
15456      * @method setYConstraint
15457      * @param {int} iUp the number of pixels the element can move up
15458      * @param {int} iDown the number of pixels the element can move down
15459      * @param {int} iTickSize optional parameter for specifying that the
15460      * element should move iTickSize pixels at a time.
15461      */
15462     setYConstraint: function(iUp, iDown, iTickSize) {
15463         this.topConstraint = iUp;
15464         this.bottomConstraint = iDown;
15465
15466         this.minY = this.initPageY - iUp;
15467         this.maxY = this.initPageY + iDown;
15468         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
15469
15470         this.constrainY = true;
15471
15472     },
15473
15474     /**
15475      * resetConstraints must be called if you manually reposition a dd element.
15476      * @method resetConstraints
15477      * @param {boolean} maintainOffset
15478      */
15479     resetConstraints: function() {
15480
15481
15482         // Maintain offsets if necessary
15483         if (this.initPageX || this.initPageX === 0) {
15484             // figure out how much this thing has moved
15485             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
15486             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
15487
15488             this.setInitPosition(dx, dy);
15489
15490         // This is the first time we have detected the element's position
15491         } else {
15492             this.setInitPosition();
15493         }
15494
15495         if (this.constrainX) {
15496             this.setXConstraint( this.leftConstraint,
15497                                  this.rightConstraint,
15498                                  this.xTickSize        );
15499         }
15500
15501         if (this.constrainY) {
15502             this.setYConstraint( this.topConstraint,
15503                                  this.bottomConstraint,
15504                                  this.yTickSize         );
15505         }
15506     },
15507
15508     /**
15509      * Normally the drag element is moved pixel by pixel, but we can specify
15510      * that it move a number of pixels at a time.  This method resolves the
15511      * location when we have it set up like this.
15512      * @method getTick
15513      * @param {int} val where we want to place the object
15514      * @param {int[]} tickArray sorted array of valid points
15515      * @return {int} the closest tick
15516      * @private
15517      */
15518     getTick: function(val, tickArray) {
15519
15520         if (!tickArray) {
15521             // If tick interval is not defined, it is effectively 1 pixel,
15522             // so we return the value passed to us.
15523             return val;
15524         } else if (tickArray[0] >= val) {
15525             // The value is lower than the first tick, so we return the first
15526             // tick.
15527             return tickArray[0];
15528         } else {
15529             for (var i=0, len=tickArray.length; i<len; ++i) {
15530                 var next = i + 1;
15531                 if (tickArray[next] && tickArray[next] >= val) {
15532                     var diff1 = val - tickArray[i];
15533                     var diff2 = tickArray[next] - val;
15534                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
15535                 }
15536             }
15537
15538             // The value is larger than the last tick, so we return the last
15539             // tick.
15540             return tickArray[tickArray.length - 1];
15541         }
15542     },
15543
15544     /**
15545      * toString method
15546      * @method toString
15547      * @return {string} string representation of the dd obj
15548      */
15549     toString: function() {
15550         return ("DragDrop " + this.id);
15551     }
15552
15553 });
15554
15555 })();
15556 /*
15557  * Based on:
15558  * Ext JS Library 1.1.1
15559  * Copyright(c) 2006-2007, Ext JS, LLC.
15560  *
15561  * Originally Released Under LGPL - original licence link has changed is not relivant.
15562  *
15563  * Fork - LGPL
15564  * <script type="text/javascript">
15565  */
15566
15567
15568 /**
15569  * The drag and drop utility provides a framework for building drag and drop
15570  * applications.  In addition to enabling drag and drop for specific elements,
15571  * the drag and drop elements are tracked by the manager class, and the
15572  * interactions between the various elements are tracked during the drag and
15573  * the implementing code is notified about these important moments.
15574  */
15575
15576 // Only load the library once.  Rewriting the manager class would orphan
15577 // existing drag and drop instances.
15578 if (!Roo.dd.DragDropMgr) {
15579
15580 /**
15581  * @class Roo.dd.DragDropMgr
15582  * DragDropMgr is a singleton that tracks the element interaction for
15583  * all DragDrop items in the window.  Generally, you will not call
15584  * this class directly, but it does have helper methods that could
15585  * be useful in your DragDrop implementations.
15586  * @singleton
15587  */
15588 Roo.dd.DragDropMgr = function() {
15589
15590     var Event = Roo.EventManager;
15591
15592     return {
15593
15594         /**
15595          * Two dimensional Array of registered DragDrop objects.  The first
15596          * dimension is the DragDrop item group, the second the DragDrop
15597          * object.
15598          * @property ids
15599          * @type {string: string}
15600          * @private
15601          * @static
15602          */
15603         ids: {},
15604
15605         /**
15606          * Array of element ids defined as drag handles.  Used to determine
15607          * if the element that generated the mousedown event is actually the
15608          * handle and not the html element itself.
15609          * @property handleIds
15610          * @type {string: string}
15611          * @private
15612          * @static
15613          */
15614         handleIds: {},
15615
15616         /**
15617          * the DragDrop object that is currently being dragged
15618          * @property dragCurrent
15619          * @type DragDrop
15620          * @private
15621          * @static
15622          **/
15623         dragCurrent: null,
15624
15625         /**
15626          * the DragDrop object(s) that are being hovered over
15627          * @property dragOvers
15628          * @type Array
15629          * @private
15630          * @static
15631          */
15632         dragOvers: {},
15633
15634         /**
15635          * the X distance between the cursor and the object being dragged
15636          * @property deltaX
15637          * @type int
15638          * @private
15639          * @static
15640          */
15641         deltaX: 0,
15642
15643         /**
15644          * the Y distance between the cursor and the object being dragged
15645          * @property deltaY
15646          * @type int
15647          * @private
15648          * @static
15649          */
15650         deltaY: 0,
15651
15652         /**
15653          * Flag to determine if we should prevent the default behavior of the
15654          * events we define. By default this is true, but this can be set to
15655          * false if you need the default behavior (not recommended)
15656          * @property preventDefault
15657          * @type boolean
15658          * @static
15659          */
15660         preventDefault: true,
15661
15662         /**
15663          * Flag to determine if we should stop the propagation of the events
15664          * we generate. This is true by default but you may want to set it to
15665          * false if the html element contains other features that require the
15666          * mouse click.
15667          * @property stopPropagation
15668          * @type boolean
15669          * @static
15670          */
15671         stopPropagation: true,
15672
15673         /**
15674          * Internal flag that is set to true when drag and drop has been
15675          * intialized
15676          * @property initialized
15677          * @private
15678          * @static
15679          */
15680         initalized: false,
15681
15682         /**
15683          * All drag and drop can be disabled.
15684          * @property locked
15685          * @private
15686          * @static
15687          */
15688         locked: false,
15689
15690         /**
15691          * Called the first time an element is registered.
15692          * @method init
15693          * @private
15694          * @static
15695          */
15696         init: function() {
15697             this.initialized = true;
15698         },
15699
15700         /**
15701          * In point mode, drag and drop interaction is defined by the
15702          * location of the cursor during the drag/drop
15703          * @property POINT
15704          * @type int
15705          * @static
15706          */
15707         POINT: 0,
15708
15709         /**
15710          * In intersect mode, drag and drop interactio nis defined by the
15711          * overlap of two or more drag and drop objects.
15712          * @property INTERSECT
15713          * @type int
15714          * @static
15715          */
15716         INTERSECT: 1,
15717
15718         /**
15719          * The current drag and drop mode.  Default: POINT
15720          * @property mode
15721          * @type int
15722          * @static
15723          */
15724         mode: 0,
15725
15726         /**
15727          * Runs method on all drag and drop objects
15728          * @method _execOnAll
15729          * @private
15730          * @static
15731          */
15732         _execOnAll: function(sMethod, args) {
15733             for (var i in this.ids) {
15734                 for (var j in this.ids[i]) {
15735                     var oDD = this.ids[i][j];
15736                     if (! this.isTypeOfDD(oDD)) {
15737                         continue;
15738                     }
15739                     oDD[sMethod].apply(oDD, args);
15740                 }
15741             }
15742         },
15743
15744         /**
15745          * Drag and drop initialization.  Sets up the global event handlers
15746          * @method _onLoad
15747          * @private
15748          * @static
15749          */
15750         _onLoad: function() {
15751
15752             this.init();
15753
15754
15755             Event.on(document, "mouseup",   this.handleMouseUp, this, true);
15756             Event.on(document, "mousemove", this.handleMouseMove, this, true);
15757             Event.on(window,   "unload",    this._onUnload, this, true);
15758             Event.on(window,   "resize",    this._onResize, this, true);
15759             // Event.on(window,   "mouseout",    this._test);
15760
15761         },
15762
15763         /**
15764          * Reset constraints on all drag and drop objs
15765          * @method _onResize
15766          * @private
15767          * @static
15768          */
15769         _onResize: function(e) {
15770             this._execOnAll("resetConstraints", []);
15771         },
15772
15773         /**
15774          * Lock all drag and drop functionality
15775          * @method lock
15776          * @static
15777          */
15778         lock: function() { this.locked = true; },
15779
15780         /**
15781          * Unlock all drag and drop functionality
15782          * @method unlock
15783          * @static
15784          */
15785         unlock: function() { this.locked = false; },
15786
15787         /**
15788          * Is drag and drop locked?
15789          * @method isLocked
15790          * @return {boolean} True if drag and drop is locked, false otherwise.
15791          * @static
15792          */
15793         isLocked: function() { return this.locked; },
15794
15795         /**
15796          * Location cache that is set for all drag drop objects when a drag is
15797          * initiated, cleared when the drag is finished.
15798          * @property locationCache
15799          * @private
15800          * @static
15801          */
15802         locationCache: {},
15803
15804         /**
15805          * Set useCache to false if you want to force object the lookup of each
15806          * drag and drop linked element constantly during a drag.
15807          * @property useCache
15808          * @type boolean
15809          * @static
15810          */
15811         useCache: true,
15812
15813         /**
15814          * The number of pixels that the mouse needs to move after the
15815          * mousedown before the drag is initiated.  Default=3;
15816          * @property clickPixelThresh
15817          * @type int
15818          * @static
15819          */
15820         clickPixelThresh: 3,
15821
15822         /**
15823          * The number of milliseconds after the mousedown event to initiate the
15824          * drag if we don't get a mouseup event. Default=1000
15825          * @property clickTimeThresh
15826          * @type int
15827          * @static
15828          */
15829         clickTimeThresh: 350,
15830
15831         /**
15832          * Flag that indicates that either the drag pixel threshold or the
15833          * mousdown time threshold has been met
15834          * @property dragThreshMet
15835          * @type boolean
15836          * @private
15837          * @static
15838          */
15839         dragThreshMet: false,
15840
15841         /**
15842          * Timeout used for the click time threshold
15843          * @property clickTimeout
15844          * @type Object
15845          * @private
15846          * @static
15847          */
15848         clickTimeout: null,
15849
15850         /**
15851          * The X position of the mousedown event stored for later use when a
15852          * drag threshold is met.
15853          * @property startX
15854          * @type int
15855          * @private
15856          * @static
15857          */
15858         startX: 0,
15859
15860         /**
15861          * The Y position of the mousedown event stored for later use when a
15862          * drag threshold is met.
15863          * @property startY
15864          * @type int
15865          * @private
15866          * @static
15867          */
15868         startY: 0,
15869
15870         /**
15871          * Each DragDrop instance must be registered with the DragDropMgr.
15872          * This is executed in DragDrop.init()
15873          * @method regDragDrop
15874          * @param {DragDrop} oDD the DragDrop object to register
15875          * @param {String} sGroup the name of the group this element belongs to
15876          * @static
15877          */
15878         regDragDrop: function(oDD, sGroup) {
15879             if (!this.initialized) { this.init(); }
15880
15881             if (!this.ids[sGroup]) {
15882                 this.ids[sGroup] = {};
15883             }
15884             this.ids[sGroup][oDD.id] = oDD;
15885         },
15886
15887         /**
15888          * Removes the supplied dd instance from the supplied group. Executed
15889          * by DragDrop.removeFromGroup, so don't call this function directly.
15890          * @method removeDDFromGroup
15891          * @private
15892          * @static
15893          */
15894         removeDDFromGroup: function(oDD, sGroup) {
15895             if (!this.ids[sGroup]) {
15896                 this.ids[sGroup] = {};
15897             }
15898
15899             var obj = this.ids[sGroup];
15900             if (obj && obj[oDD.id]) {
15901                 delete obj[oDD.id];
15902             }
15903         },
15904
15905         /**
15906          * Unregisters a drag and drop item.  This is executed in
15907          * DragDrop.unreg, use that method instead of calling this directly.
15908          * @method _remove
15909          * @private
15910          * @static
15911          */
15912         _remove: function(oDD) {
15913             for (var g in oDD.groups) {
15914                 if (g && this.ids[g][oDD.id]) {
15915                     delete this.ids[g][oDD.id];
15916                 }
15917             }
15918             delete this.handleIds[oDD.id];
15919         },
15920
15921         /**
15922          * Each DragDrop handle element must be registered.  This is done
15923          * automatically when executing DragDrop.setHandleElId()
15924          * @method regHandle
15925          * @param {String} sDDId the DragDrop id this element is a handle for
15926          * @param {String} sHandleId the id of the element that is the drag
15927          * handle
15928          * @static
15929          */
15930         regHandle: function(sDDId, sHandleId) {
15931             if (!this.handleIds[sDDId]) {
15932                 this.handleIds[sDDId] = {};
15933             }
15934             this.handleIds[sDDId][sHandleId] = sHandleId;
15935         },
15936
15937         /**
15938          * Utility function to determine if a given element has been
15939          * registered as a drag drop item.
15940          * @method isDragDrop
15941          * @param {String} id the element id to check
15942          * @return {boolean} true if this element is a DragDrop item,
15943          * false otherwise
15944          * @static
15945          */
15946         isDragDrop: function(id) {
15947             return ( this.getDDById(id) ) ? true : false;
15948         },
15949
15950         /**
15951          * Returns the drag and drop instances that are in all groups the
15952          * passed in instance belongs to.
15953          * @method getRelated
15954          * @param {DragDrop} p_oDD the obj to get related data for
15955          * @param {boolean} bTargetsOnly if true, only return targetable objs
15956          * @return {DragDrop[]} the related instances
15957          * @static
15958          */
15959         getRelated: function(p_oDD, bTargetsOnly) {
15960             var oDDs = [];
15961             for (var i in p_oDD.groups) {
15962                 for (j in this.ids[i]) {
15963                     var dd = this.ids[i][j];
15964                     if (! this.isTypeOfDD(dd)) {
15965                         continue;
15966                     }
15967                     if (!bTargetsOnly || dd.isTarget) {
15968                         oDDs[oDDs.length] = dd;
15969                     }
15970                 }
15971             }
15972
15973             return oDDs;
15974         },
15975
15976         /**
15977          * Returns true if the specified dd target is a legal target for
15978          * the specifice drag obj
15979          * @method isLegalTarget
15980          * @param {DragDrop} the drag obj
15981          * @param {DragDrop} the target
15982          * @return {boolean} true if the target is a legal target for the
15983          * dd obj
15984          * @static
15985          */
15986         isLegalTarget: function (oDD, oTargetDD) {
15987             var targets = this.getRelated(oDD, true);
15988             for (var i=0, len=targets.length;i<len;++i) {
15989                 if (targets[i].id == oTargetDD.id) {
15990                     return true;
15991                 }
15992             }
15993
15994             return false;
15995         },
15996
15997         /**
15998          * My goal is to be able to transparently determine if an object is
15999          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
16000          * returns "object", oDD.constructor.toString() always returns
16001          * "DragDrop" and not the name of the subclass.  So for now it just
16002          * evaluates a well-known variable in DragDrop.
16003          * @method isTypeOfDD
16004          * @param {Object} the object to evaluate
16005          * @return {boolean} true if typeof oDD = DragDrop
16006          * @static
16007          */
16008         isTypeOfDD: function (oDD) {
16009             return (oDD && oDD.__ygDragDrop);
16010         },
16011
16012         /**
16013          * Utility function to determine if a given element has been
16014          * registered as a drag drop handle for the given Drag Drop object.
16015          * @method isHandle
16016          * @param {String} id the element id to check
16017          * @return {boolean} true if this element is a DragDrop handle, false
16018          * otherwise
16019          * @static
16020          */
16021         isHandle: function(sDDId, sHandleId) {
16022             return ( this.handleIds[sDDId] &&
16023                             this.handleIds[sDDId][sHandleId] );
16024         },
16025
16026         /**
16027          * Returns the DragDrop instance for a given id
16028          * @method getDDById
16029          * @param {String} id the id of the DragDrop object
16030          * @return {DragDrop} the drag drop object, null if it is not found
16031          * @static
16032          */
16033         getDDById: function(id) {
16034             for (var i in this.ids) {
16035                 if (this.ids[i][id]) {
16036                     return this.ids[i][id];
16037                 }
16038             }
16039             return null;
16040         },
16041
16042         /**
16043          * Fired after a registered DragDrop object gets the mousedown event.
16044          * Sets up the events required to track the object being dragged
16045          * @method handleMouseDown
16046          * @param {Event} e the event
16047          * @param oDD the DragDrop object being dragged
16048          * @private
16049          * @static
16050          */
16051         handleMouseDown: function(e, oDD) {
16052             if(Roo.QuickTips){
16053                 Roo.QuickTips.disable();
16054             }
16055             this.currentTarget = e.getTarget();
16056
16057             this.dragCurrent = oDD;
16058
16059             var el = oDD.getEl();
16060
16061             // track start position
16062             this.startX = e.getPageX();
16063             this.startY = e.getPageY();
16064
16065             this.deltaX = this.startX - el.offsetLeft;
16066             this.deltaY = this.startY - el.offsetTop;
16067
16068             this.dragThreshMet = false;
16069
16070             this.clickTimeout = setTimeout(
16071                     function() {
16072                         var DDM = Roo.dd.DDM;
16073                         DDM.startDrag(DDM.startX, DDM.startY);
16074                     },
16075                     this.clickTimeThresh );
16076         },
16077
16078         /**
16079          * Fired when either the drag pixel threshol or the mousedown hold
16080          * time threshold has been met.
16081          * @method startDrag
16082          * @param x {int} the X position of the original mousedown
16083          * @param y {int} the Y position of the original mousedown
16084          * @static
16085          */
16086         startDrag: function(x, y) {
16087             clearTimeout(this.clickTimeout);
16088             if (this.dragCurrent) {
16089                 this.dragCurrent.b4StartDrag(x, y);
16090                 this.dragCurrent.startDrag(x, y);
16091             }
16092             this.dragThreshMet = true;
16093         },
16094
16095         /**
16096          * Internal function to handle the mouseup event.  Will be invoked
16097          * from the context of the document.
16098          * @method handleMouseUp
16099          * @param {Event} e the event
16100          * @private
16101          * @static
16102          */
16103         handleMouseUp: function(e) {
16104
16105             if(Roo.QuickTips){
16106                 Roo.QuickTips.enable();
16107             }
16108             if (! this.dragCurrent) {
16109                 return;
16110             }
16111
16112             clearTimeout(this.clickTimeout);
16113
16114             if (this.dragThreshMet) {
16115                 this.fireEvents(e, true);
16116             } else {
16117             }
16118
16119             this.stopDrag(e);
16120
16121             this.stopEvent(e);
16122         },
16123
16124         /**
16125          * Utility to stop event propagation and event default, if these
16126          * features are turned on.
16127          * @method stopEvent
16128          * @param {Event} e the event as returned by this.getEvent()
16129          * @static
16130          */
16131         stopEvent: function(e){
16132             if(this.stopPropagation) {
16133                 e.stopPropagation();
16134             }
16135
16136             if (this.preventDefault) {
16137                 e.preventDefault();
16138             }
16139         },
16140
16141         /**
16142          * Internal function to clean up event handlers after the drag
16143          * operation is complete
16144          * @method stopDrag
16145          * @param {Event} e the event
16146          * @private
16147          * @static
16148          */
16149         stopDrag: function(e) {
16150             // Fire the drag end event for the item that was dragged
16151             if (this.dragCurrent) {
16152                 if (this.dragThreshMet) {
16153                     this.dragCurrent.b4EndDrag(e);
16154                     this.dragCurrent.endDrag(e);
16155                 }
16156
16157                 this.dragCurrent.onMouseUp(e);
16158             }
16159
16160             this.dragCurrent = null;
16161             this.dragOvers = {};
16162         },
16163
16164         /**
16165          * Internal function to handle the mousemove event.  Will be invoked
16166          * from the context of the html element.
16167          *
16168          * @TODO figure out what we can do about mouse events lost when the
16169          * user drags objects beyond the window boundary.  Currently we can
16170          * detect this in internet explorer by verifying that the mouse is
16171          * down during the mousemove event.  Firefox doesn't give us the
16172          * button state on the mousemove event.
16173          * @method handleMouseMove
16174          * @param {Event} e the event
16175          * @private
16176          * @static
16177          */
16178         handleMouseMove: function(e) {
16179             if (! this.dragCurrent) {
16180                 return true;
16181             }
16182
16183             // var button = e.which || e.button;
16184
16185             // check for IE mouseup outside of page boundary
16186             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
16187                 this.stopEvent(e);
16188                 return this.handleMouseUp(e);
16189             }
16190
16191             if (!this.dragThreshMet) {
16192                 var diffX = Math.abs(this.startX - e.getPageX());
16193                 var diffY = Math.abs(this.startY - e.getPageY());
16194                 if (diffX > this.clickPixelThresh ||
16195                             diffY > this.clickPixelThresh) {
16196                     this.startDrag(this.startX, this.startY);
16197                 }
16198             }
16199
16200             if (this.dragThreshMet) {
16201                 this.dragCurrent.b4Drag(e);
16202                 this.dragCurrent.onDrag(e);
16203                 if(!this.dragCurrent.moveOnly){
16204                     this.fireEvents(e, false);
16205                 }
16206             }
16207
16208             this.stopEvent(e);
16209
16210             return true;
16211         },
16212
16213         /**
16214          * Iterates over all of the DragDrop elements to find ones we are
16215          * hovering over or dropping on
16216          * @method fireEvents
16217          * @param {Event} e the event
16218          * @param {boolean} isDrop is this a drop op or a mouseover op?
16219          * @private
16220          * @static
16221          */
16222         fireEvents: function(e, isDrop) {
16223             var dc = this.dragCurrent;
16224
16225             // If the user did the mouse up outside of the window, we could
16226             // get here even though we have ended the drag.
16227             if (!dc || dc.isLocked()) {
16228                 return;
16229             }
16230
16231             var pt = e.getPoint();
16232
16233             // cache the previous dragOver array
16234             var oldOvers = [];
16235
16236             var outEvts   = [];
16237             var overEvts  = [];
16238             var dropEvts  = [];
16239             var enterEvts = [];
16240
16241             // Check to see if the object(s) we were hovering over is no longer
16242             // being hovered over so we can fire the onDragOut event
16243             for (var i in this.dragOvers) {
16244
16245                 var ddo = this.dragOvers[i];
16246
16247                 if (! this.isTypeOfDD(ddo)) {
16248                     continue;
16249                 }
16250
16251                 if (! this.isOverTarget(pt, ddo, this.mode)) {
16252                     outEvts.push( ddo );
16253                 }
16254
16255                 oldOvers[i] = true;
16256                 delete this.dragOvers[i];
16257             }
16258
16259             for (var sGroup in dc.groups) {
16260
16261                 if ("string" != typeof sGroup) {
16262                     continue;
16263                 }
16264
16265                 for (i in this.ids[sGroup]) {
16266                     var oDD = this.ids[sGroup][i];
16267                     if (! this.isTypeOfDD(oDD)) {
16268                         continue;
16269                     }
16270
16271                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
16272                         if (this.isOverTarget(pt, oDD, this.mode)) {
16273                             // look for drop interactions
16274                             if (isDrop) {
16275                                 dropEvts.push( oDD );
16276                             // look for drag enter and drag over interactions
16277                             } else {
16278
16279                                 // initial drag over: dragEnter fires
16280                                 if (!oldOvers[oDD.id]) {
16281                                     enterEvts.push( oDD );
16282                                 // subsequent drag overs: dragOver fires
16283                                 } else {
16284                                     overEvts.push( oDD );
16285                                 }
16286
16287                                 this.dragOvers[oDD.id] = oDD;
16288                             }
16289                         }
16290                     }
16291                 }
16292             }
16293
16294             if (this.mode) {
16295                 if (outEvts.length) {
16296                     dc.b4DragOut(e, outEvts);
16297                     dc.onDragOut(e, outEvts);
16298                 }
16299
16300                 if (enterEvts.length) {
16301                     dc.onDragEnter(e, enterEvts);
16302                 }
16303
16304                 if (overEvts.length) {
16305                     dc.b4DragOver(e, overEvts);
16306                     dc.onDragOver(e, overEvts);
16307                 }
16308
16309                 if (dropEvts.length) {
16310                     dc.b4DragDrop(e, dropEvts);
16311                     dc.onDragDrop(e, dropEvts);
16312                 }
16313
16314             } else {
16315                 // fire dragout events
16316                 var len = 0;
16317                 for (i=0, len=outEvts.length; i<len; ++i) {
16318                     dc.b4DragOut(e, outEvts[i].id);
16319                     dc.onDragOut(e, outEvts[i].id);
16320                 }
16321
16322                 // fire enter events
16323                 for (i=0,len=enterEvts.length; i<len; ++i) {
16324                     // dc.b4DragEnter(e, oDD.id);
16325                     dc.onDragEnter(e, enterEvts[i].id);
16326                 }
16327
16328                 // fire over events
16329                 for (i=0,len=overEvts.length; i<len; ++i) {
16330                     dc.b4DragOver(e, overEvts[i].id);
16331                     dc.onDragOver(e, overEvts[i].id);
16332                 }
16333
16334                 // fire drop events
16335                 for (i=0, len=dropEvts.length; i<len; ++i) {
16336                     dc.b4DragDrop(e, dropEvts[i].id);
16337                     dc.onDragDrop(e, dropEvts[i].id);
16338                 }
16339
16340             }
16341
16342             // notify about a drop that did not find a target
16343             if (isDrop && !dropEvts.length) {
16344                 dc.onInvalidDrop(e);
16345             }
16346
16347         },
16348
16349         /**
16350          * Helper function for getting the best match from the list of drag
16351          * and drop objects returned by the drag and drop events when we are
16352          * in INTERSECT mode.  It returns either the first object that the
16353          * cursor is over, or the object that has the greatest overlap with
16354          * the dragged element.
16355          * @method getBestMatch
16356          * @param  {DragDrop[]} dds The array of drag and drop objects
16357          * targeted
16358          * @return {DragDrop}       The best single match
16359          * @static
16360          */
16361         getBestMatch: function(dds) {
16362             var winner = null;
16363             // Return null if the input is not what we expect
16364             //if (!dds || !dds.length || dds.length == 0) {
16365                // winner = null;
16366             // If there is only one item, it wins
16367             //} else if (dds.length == 1) {
16368
16369             var len = dds.length;
16370
16371             if (len == 1) {
16372                 winner = dds[0];
16373             } else {
16374                 // Loop through the targeted items
16375                 for (var i=0; i<len; ++i) {
16376                     var dd = dds[i];
16377                     // If the cursor is over the object, it wins.  If the
16378                     // cursor is over multiple matches, the first one we come
16379                     // to wins.
16380                     if (dd.cursorIsOver) {
16381                         winner = dd;
16382                         break;
16383                     // Otherwise the object with the most overlap wins
16384                     } else {
16385                         if (!winner ||
16386                             winner.overlap.getArea() < dd.overlap.getArea()) {
16387                             winner = dd;
16388                         }
16389                     }
16390                 }
16391             }
16392
16393             return winner;
16394         },
16395
16396         /**
16397          * Refreshes the cache of the top-left and bottom-right points of the
16398          * drag and drop objects in the specified group(s).  This is in the
16399          * format that is stored in the drag and drop instance, so typical
16400          * usage is:
16401          * <code>
16402          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
16403          * </code>
16404          * Alternatively:
16405          * <code>
16406          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
16407          * </code>
16408          * @TODO this really should be an indexed array.  Alternatively this
16409          * method could accept both.
16410          * @method refreshCache
16411          * @param {Object} groups an associative array of groups to refresh
16412          * @static
16413          */
16414         refreshCache: function(groups) {
16415             for (var sGroup in groups) {
16416                 if ("string" != typeof sGroup) {
16417                     continue;
16418                 }
16419                 for (var i in this.ids[sGroup]) {
16420                     var oDD = this.ids[sGroup][i];
16421
16422                     if (this.isTypeOfDD(oDD)) {
16423                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
16424                         var loc = this.getLocation(oDD);
16425                         if (loc) {
16426                             this.locationCache[oDD.id] = loc;
16427                         } else {
16428                             delete this.locationCache[oDD.id];
16429                             // this will unregister the drag and drop object if
16430                             // the element is not in a usable state
16431                             // oDD.unreg();
16432                         }
16433                     }
16434                 }
16435             }
16436         },
16437
16438         /**
16439          * This checks to make sure an element exists and is in the DOM.  The
16440          * main purpose is to handle cases where innerHTML is used to remove
16441          * drag and drop objects from the DOM.  IE provides an 'unspecified
16442          * error' when trying to access the offsetParent of such an element
16443          * @method verifyEl
16444          * @param {HTMLElement} el the element to check
16445          * @return {boolean} true if the element looks usable
16446          * @static
16447          */
16448         verifyEl: function(el) {
16449             if (el) {
16450                 var parent;
16451                 if(Roo.isIE){
16452                     try{
16453                         parent = el.offsetParent;
16454                     }catch(e){}
16455                 }else{
16456                     parent = el.offsetParent;
16457                 }
16458                 if (parent) {
16459                     return true;
16460                 }
16461             }
16462
16463             return false;
16464         },
16465
16466         /**
16467          * Returns a Region object containing the drag and drop element's position
16468          * and size, including the padding configured for it
16469          * @method getLocation
16470          * @param {DragDrop} oDD the drag and drop object to get the
16471          *                       location for
16472          * @return {Roo.lib.Region} a Region object representing the total area
16473          *                             the element occupies, including any padding
16474          *                             the instance is configured for.
16475          * @static
16476          */
16477         getLocation: function(oDD) {
16478             if (! this.isTypeOfDD(oDD)) {
16479                 return null;
16480             }
16481
16482             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
16483
16484             try {
16485                 pos= Roo.lib.Dom.getXY(el);
16486             } catch (e) { }
16487
16488             if (!pos) {
16489                 return null;
16490             }
16491
16492             x1 = pos[0];
16493             x2 = x1 + el.offsetWidth;
16494             y1 = pos[1];
16495             y2 = y1 + el.offsetHeight;
16496
16497             t = y1 - oDD.padding[0];
16498             r = x2 + oDD.padding[1];
16499             b = y2 + oDD.padding[2];
16500             l = x1 - oDD.padding[3];
16501
16502             return new Roo.lib.Region( t, r, b, l );
16503         },
16504
16505         /**
16506          * Checks the cursor location to see if it over the target
16507          * @method isOverTarget
16508          * @param {Roo.lib.Point} pt The point to evaluate
16509          * @param {DragDrop} oTarget the DragDrop object we are inspecting
16510          * @return {boolean} true if the mouse is over the target
16511          * @private
16512          * @static
16513          */
16514         isOverTarget: function(pt, oTarget, intersect) {
16515             // use cache if available
16516             var loc = this.locationCache[oTarget.id];
16517             if (!loc || !this.useCache) {
16518                 loc = this.getLocation(oTarget);
16519                 this.locationCache[oTarget.id] = loc;
16520
16521             }
16522
16523             if (!loc) {
16524                 return false;
16525             }
16526
16527             oTarget.cursorIsOver = loc.contains( pt );
16528
16529             // DragDrop is using this as a sanity check for the initial mousedown
16530             // in this case we are done.  In POINT mode, if the drag obj has no
16531             // contraints, we are also done. Otherwise we need to evaluate the
16532             // location of the target as related to the actual location of the
16533             // dragged element.
16534             var dc = this.dragCurrent;
16535             if (!dc || !dc.getTargetCoord ||
16536                     (!intersect && !dc.constrainX && !dc.constrainY)) {
16537                 return oTarget.cursorIsOver;
16538             }
16539
16540             oTarget.overlap = null;
16541
16542             // Get the current location of the drag element, this is the
16543             // location of the mouse event less the delta that represents
16544             // where the original mousedown happened on the element.  We
16545             // need to consider constraints and ticks as well.
16546             var pos = dc.getTargetCoord(pt.x, pt.y);
16547
16548             var el = dc.getDragEl();
16549             var curRegion = new Roo.lib.Region( pos.y,
16550                                                    pos.x + el.offsetWidth,
16551                                                    pos.y + el.offsetHeight,
16552                                                    pos.x );
16553
16554             var overlap = curRegion.intersect(loc);
16555
16556             if (overlap) {
16557                 oTarget.overlap = overlap;
16558                 return (intersect) ? true : oTarget.cursorIsOver;
16559             } else {
16560                 return false;
16561             }
16562         },
16563
16564         /**
16565          * unload event handler
16566          * @method _onUnload
16567          * @private
16568          * @static
16569          */
16570         _onUnload: function(e, me) {
16571             Roo.dd.DragDropMgr.unregAll();
16572         },
16573
16574         /**
16575          * Cleans up the drag and drop events and objects.
16576          * @method unregAll
16577          * @private
16578          * @static
16579          */
16580         unregAll: function() {
16581
16582             if (this.dragCurrent) {
16583                 this.stopDrag();
16584                 this.dragCurrent = null;
16585             }
16586
16587             this._execOnAll("unreg", []);
16588
16589             for (i in this.elementCache) {
16590                 delete this.elementCache[i];
16591             }
16592
16593             this.elementCache = {};
16594             this.ids = {};
16595         },
16596
16597         /**
16598          * A cache of DOM elements
16599          * @property elementCache
16600          * @private
16601          * @static
16602          */
16603         elementCache: {},
16604
16605         /**
16606          * Get the wrapper for the DOM element specified
16607          * @method getElWrapper
16608          * @param {String} id the id of the element to get
16609          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
16610          * @private
16611          * @deprecated This wrapper isn't that useful
16612          * @static
16613          */
16614         getElWrapper: function(id) {
16615             var oWrapper = this.elementCache[id];
16616             if (!oWrapper || !oWrapper.el) {
16617                 oWrapper = this.elementCache[id] =
16618                     new this.ElementWrapper(Roo.getDom(id));
16619             }
16620             return oWrapper;
16621         },
16622
16623         /**
16624          * Returns the actual DOM element
16625          * @method getElement
16626          * @param {String} id the id of the elment to get
16627          * @return {Object} The element
16628          * @deprecated use Roo.getDom instead
16629          * @static
16630          */
16631         getElement: function(id) {
16632             return Roo.getDom(id);
16633         },
16634
16635         /**
16636          * Returns the style property for the DOM element (i.e.,
16637          * document.getElById(id).style)
16638          * @method getCss
16639          * @param {String} id the id of the elment to get
16640          * @return {Object} The style property of the element
16641          * @deprecated use Roo.getDom instead
16642          * @static
16643          */
16644         getCss: function(id) {
16645             var el = Roo.getDom(id);
16646             return (el) ? el.style : null;
16647         },
16648
16649         /**
16650          * Inner class for cached elements
16651          * @class DragDropMgr.ElementWrapper
16652          * @for DragDropMgr
16653          * @private
16654          * @deprecated
16655          */
16656         ElementWrapper: function(el) {
16657                 /**
16658                  * The element
16659                  * @property el
16660                  */
16661                 this.el = el || null;
16662                 /**
16663                  * The element id
16664                  * @property id
16665                  */
16666                 this.id = this.el && el.id;
16667                 /**
16668                  * A reference to the style property
16669                  * @property css
16670                  */
16671                 this.css = this.el && el.style;
16672             },
16673
16674         /**
16675          * Returns the X position of an html element
16676          * @method getPosX
16677          * @param el the element for which to get the position
16678          * @return {int} the X coordinate
16679          * @for DragDropMgr
16680          * @deprecated use Roo.lib.Dom.getX instead
16681          * @static
16682          */
16683         getPosX: function(el) {
16684             return Roo.lib.Dom.getX(el);
16685         },
16686
16687         /**
16688          * Returns the Y position of an html element
16689          * @method getPosY
16690          * @param el the element for which to get the position
16691          * @return {int} the Y coordinate
16692          * @deprecated use Roo.lib.Dom.getY instead
16693          * @static
16694          */
16695         getPosY: function(el) {
16696             return Roo.lib.Dom.getY(el);
16697         },
16698
16699         /**
16700          * Swap two nodes.  In IE, we use the native method, for others we
16701          * emulate the IE behavior
16702          * @method swapNode
16703          * @param n1 the first node to swap
16704          * @param n2 the other node to swap
16705          * @static
16706          */
16707         swapNode: function(n1, n2) {
16708             if (n1.swapNode) {
16709                 n1.swapNode(n2);
16710             } else {
16711                 var p = n2.parentNode;
16712                 var s = n2.nextSibling;
16713
16714                 if (s == n1) {
16715                     p.insertBefore(n1, n2);
16716                 } else if (n2 == n1.nextSibling) {
16717                     p.insertBefore(n2, n1);
16718                 } else {
16719                     n1.parentNode.replaceChild(n2, n1);
16720                     p.insertBefore(n1, s);
16721                 }
16722             }
16723         },
16724
16725         /**
16726          * Returns the current scroll position
16727          * @method getScroll
16728          * @private
16729          * @static
16730          */
16731         getScroll: function () {
16732             var t, l, dde=document.documentElement, db=document.body;
16733             if (dde && (dde.scrollTop || dde.scrollLeft)) {
16734                 t = dde.scrollTop;
16735                 l = dde.scrollLeft;
16736             } else if (db) {
16737                 t = db.scrollTop;
16738                 l = db.scrollLeft;
16739             } else {
16740
16741             }
16742             return { top: t, left: l };
16743         },
16744
16745         /**
16746          * Returns the specified element style property
16747          * @method getStyle
16748          * @param {HTMLElement} el          the element
16749          * @param {string}      styleProp   the style property
16750          * @return {string} The value of the style property
16751          * @deprecated use Roo.lib.Dom.getStyle
16752          * @static
16753          */
16754         getStyle: function(el, styleProp) {
16755             return Roo.fly(el).getStyle(styleProp);
16756         },
16757
16758         /**
16759          * Gets the scrollTop
16760          * @method getScrollTop
16761          * @return {int} the document's scrollTop
16762          * @static
16763          */
16764         getScrollTop: function () { return this.getScroll().top; },
16765
16766         /**
16767          * Gets the scrollLeft
16768          * @method getScrollLeft
16769          * @return {int} the document's scrollTop
16770          * @static
16771          */
16772         getScrollLeft: function () { return this.getScroll().left; },
16773
16774         /**
16775          * Sets the x/y position of an element to the location of the
16776          * target element.
16777          * @method moveToEl
16778          * @param {HTMLElement} moveEl      The element to move
16779          * @param {HTMLElement} targetEl    The position reference element
16780          * @static
16781          */
16782         moveToEl: function (moveEl, targetEl) {
16783             var aCoord = Roo.lib.Dom.getXY(targetEl);
16784             Roo.lib.Dom.setXY(moveEl, aCoord);
16785         },
16786
16787         /**
16788          * Numeric array sort function
16789          * @method numericSort
16790          * @static
16791          */
16792         numericSort: function(a, b) { return (a - b); },
16793
16794         /**
16795          * Internal counter
16796          * @property _timeoutCount
16797          * @private
16798          * @static
16799          */
16800         _timeoutCount: 0,
16801
16802         /**
16803          * Trying to make the load order less important.  Without this we get
16804          * an error if this file is loaded before the Event Utility.
16805          * @method _addListeners
16806          * @private
16807          * @static
16808          */
16809         _addListeners: function() {
16810             var DDM = Roo.dd.DDM;
16811             if ( Roo.lib.Event && document ) {
16812                 DDM._onLoad();
16813             } else {
16814                 if (DDM._timeoutCount > 2000) {
16815                 } else {
16816                     setTimeout(DDM._addListeners, 10);
16817                     if (document && document.body) {
16818                         DDM._timeoutCount += 1;
16819                     }
16820                 }
16821             }
16822         },
16823
16824         /**
16825          * Recursively searches the immediate parent and all child nodes for
16826          * the handle element in order to determine wheter or not it was
16827          * clicked.
16828          * @method handleWasClicked
16829          * @param node the html element to inspect
16830          * @static
16831          */
16832         handleWasClicked: function(node, id) {
16833             if (this.isHandle(id, node.id)) {
16834                 return true;
16835             } else {
16836                 // check to see if this is a text node child of the one we want
16837                 var p = node.parentNode;
16838
16839                 while (p) {
16840                     if (this.isHandle(id, p.id)) {
16841                         return true;
16842                     } else {
16843                         p = p.parentNode;
16844                     }
16845                 }
16846             }
16847
16848             return false;
16849         }
16850
16851     };
16852
16853 }();
16854
16855 // shorter alias, save a few bytes
16856 Roo.dd.DDM = Roo.dd.DragDropMgr;
16857 Roo.dd.DDM._addListeners();
16858
16859 }/*
16860  * Based on:
16861  * Ext JS Library 1.1.1
16862  * Copyright(c) 2006-2007, Ext JS, LLC.
16863  *
16864  * Originally Released Under LGPL - original licence link has changed is not relivant.
16865  *
16866  * Fork - LGPL
16867  * <script type="text/javascript">
16868  */
16869
16870 /**
16871  * @class Roo.dd.DD
16872  * A DragDrop implementation where the linked element follows the
16873  * mouse cursor during a drag.
16874  * @extends Roo.dd.DragDrop
16875  * @constructor
16876  * @param {String} id the id of the linked element
16877  * @param {String} sGroup the group of related DragDrop items
16878  * @param {object} config an object containing configurable attributes
16879  *                Valid properties for DD:
16880  *                    scroll
16881  */
16882 Roo.dd.DD = function(id, sGroup, config) {
16883     if (id) {
16884         this.init(id, sGroup, config);
16885     }
16886 };
16887
16888 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
16889
16890     /**
16891      * When set to true, the utility automatically tries to scroll the browser
16892      * window wehn a drag and drop element is dragged near the viewport boundary.
16893      * Defaults to true.
16894      * @property scroll
16895      * @type boolean
16896      */
16897     scroll: true,
16898
16899     /**
16900      * Sets the pointer offset to the distance between the linked element's top
16901      * left corner and the location the element was clicked
16902      * @method autoOffset
16903      * @param {int} iPageX the X coordinate of the click
16904      * @param {int} iPageY the Y coordinate of the click
16905      */
16906     autoOffset: function(iPageX, iPageY) {
16907         var x = iPageX - this.startPageX;
16908         var y = iPageY - this.startPageY;
16909         this.setDelta(x, y);
16910     },
16911
16912     /**
16913      * Sets the pointer offset.  You can call this directly to force the
16914      * offset to be in a particular location (e.g., pass in 0,0 to set it
16915      * to the center of the object)
16916      * @method setDelta
16917      * @param {int} iDeltaX the distance from the left
16918      * @param {int} iDeltaY the distance from the top
16919      */
16920     setDelta: function(iDeltaX, iDeltaY) {
16921         this.deltaX = iDeltaX;
16922         this.deltaY = iDeltaY;
16923     },
16924
16925     /**
16926      * Sets the drag element to the location of the mousedown or click event,
16927      * maintaining the cursor location relative to the location on the element
16928      * that was clicked.  Override this if you want to place the element in a
16929      * location other than where the cursor is.
16930      * @method setDragElPos
16931      * @param {int} iPageX the X coordinate of the mousedown or drag event
16932      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16933      */
16934     setDragElPos: function(iPageX, iPageY) {
16935         // the first time we do this, we are going to check to make sure
16936         // the element has css positioning
16937
16938         var el = this.getDragEl();
16939         this.alignElWithMouse(el, iPageX, iPageY);
16940     },
16941
16942     /**
16943      * Sets the element to the location of the mousedown or click event,
16944      * maintaining the cursor location relative to the location on the element
16945      * that was clicked.  Override this if you want to place the element in a
16946      * location other than where the cursor is.
16947      * @method alignElWithMouse
16948      * @param {HTMLElement} el the element to move
16949      * @param {int} iPageX the X coordinate of the mousedown or drag event
16950      * @param {int} iPageY the Y coordinate of the mousedown or drag event
16951      */
16952     alignElWithMouse: function(el, iPageX, iPageY) {
16953         var oCoord = this.getTargetCoord(iPageX, iPageY);
16954         var fly = el.dom ? el : Roo.fly(el);
16955         if (!this.deltaSetXY) {
16956             var aCoord = [oCoord.x, oCoord.y];
16957             fly.setXY(aCoord);
16958             var newLeft = fly.getLeft(true);
16959             var newTop  = fly.getTop(true);
16960             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
16961         } else {
16962             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
16963         }
16964
16965         this.cachePosition(oCoord.x, oCoord.y);
16966         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
16967         return oCoord;
16968     },
16969
16970     /**
16971      * Saves the most recent position so that we can reset the constraints and
16972      * tick marks on-demand.  We need to know this so that we can calculate the
16973      * number of pixels the element is offset from its original position.
16974      * @method cachePosition
16975      * @param iPageX the current x position (optional, this just makes it so we
16976      * don't have to look it up again)
16977      * @param iPageY the current y position (optional, this just makes it so we
16978      * don't have to look it up again)
16979      */
16980     cachePosition: function(iPageX, iPageY) {
16981         if (iPageX) {
16982             this.lastPageX = iPageX;
16983             this.lastPageY = iPageY;
16984         } else {
16985             var aCoord = Roo.lib.Dom.getXY(this.getEl());
16986             this.lastPageX = aCoord[0];
16987             this.lastPageY = aCoord[1];
16988         }
16989     },
16990
16991     /**
16992      * Auto-scroll the window if the dragged object has been moved beyond the
16993      * visible window boundary.
16994      * @method autoScroll
16995      * @param {int} x the drag element's x position
16996      * @param {int} y the drag element's y position
16997      * @param {int} h the height of the drag element
16998      * @param {int} w the width of the drag element
16999      * @private
17000      */
17001     autoScroll: function(x, y, h, w) {
17002
17003         if (this.scroll) {
17004             // The client height
17005             var clientH = Roo.lib.Dom.getViewWidth();
17006
17007             // The client width
17008             var clientW = Roo.lib.Dom.getViewHeight();
17009
17010             // The amt scrolled down
17011             var st = this.DDM.getScrollTop();
17012
17013             // The amt scrolled right
17014             var sl = this.DDM.getScrollLeft();
17015
17016             // Location of the bottom of the element
17017             var bot = h + y;
17018
17019             // Location of the right of the element
17020             var right = w + x;
17021
17022             // The distance from the cursor to the bottom of the visible area,
17023             // adjusted so that we don't scroll if the cursor is beyond the
17024             // element drag constraints
17025             var toBot = (clientH + st - y - this.deltaY);
17026
17027             // The distance from the cursor to the right of the visible area
17028             var toRight = (clientW + sl - x - this.deltaX);
17029
17030
17031             // How close to the edge the cursor must be before we scroll
17032             // var thresh = (document.all) ? 100 : 40;
17033             var thresh = 40;
17034
17035             // How many pixels to scroll per autoscroll op.  This helps to reduce
17036             // clunky scrolling. IE is more sensitive about this ... it needs this
17037             // value to be higher.
17038             var scrAmt = (document.all) ? 80 : 30;
17039
17040             // Scroll down if we are near the bottom of the visible page and the
17041             // obj extends below the crease
17042             if ( bot > clientH && toBot < thresh ) {
17043                 window.scrollTo(sl, st + scrAmt);
17044             }
17045
17046             // Scroll up if the window is scrolled down and the top of the object
17047             // goes above the top border
17048             if ( y < st && st > 0 && y - st < thresh ) {
17049                 window.scrollTo(sl, st - scrAmt);
17050             }
17051
17052             // Scroll right if the obj is beyond the right border and the cursor is
17053             // near the border.
17054             if ( right > clientW && toRight < thresh ) {
17055                 window.scrollTo(sl + scrAmt, st);
17056             }
17057
17058             // Scroll left if the window has been scrolled to the right and the obj
17059             // extends past the left border
17060             if ( x < sl && sl > 0 && x - sl < thresh ) {
17061                 window.scrollTo(sl - scrAmt, st);
17062             }
17063         }
17064     },
17065
17066     /**
17067      * Finds the location the element should be placed if we want to move
17068      * it to where the mouse location less the click offset would place us.
17069      * @method getTargetCoord
17070      * @param {int} iPageX the X coordinate of the click
17071      * @param {int} iPageY the Y coordinate of the click
17072      * @return an object that contains the coordinates (Object.x and Object.y)
17073      * @private
17074      */
17075     getTargetCoord: function(iPageX, iPageY) {
17076
17077
17078         var x = iPageX - this.deltaX;
17079         var y = iPageY - this.deltaY;
17080
17081         if (this.constrainX) {
17082             if (x < this.minX) { x = this.minX; }
17083             if (x > this.maxX) { x = this.maxX; }
17084         }
17085
17086         if (this.constrainY) {
17087             if (y < this.minY) { y = this.minY; }
17088             if (y > this.maxY) { y = this.maxY; }
17089         }
17090
17091         x = this.getTick(x, this.xTicks);
17092         y = this.getTick(y, this.yTicks);
17093
17094
17095         return {x:x, y:y};
17096     },
17097
17098     /*
17099      * Sets up config options specific to this class. Overrides
17100      * Roo.dd.DragDrop, but all versions of this method through the
17101      * inheritance chain are called
17102      */
17103     applyConfig: function() {
17104         Roo.dd.DD.superclass.applyConfig.call(this);
17105         this.scroll = (this.config.scroll !== false);
17106     },
17107
17108     /*
17109      * Event that fires prior to the onMouseDown event.  Overrides
17110      * Roo.dd.DragDrop.
17111      */
17112     b4MouseDown: function(e) {
17113         // this.resetConstraints();
17114         this.autoOffset(e.getPageX(),
17115                             e.getPageY());
17116     },
17117
17118     /*
17119      * Event that fires prior to the onDrag event.  Overrides
17120      * Roo.dd.DragDrop.
17121      */
17122     b4Drag: function(e) {
17123         this.setDragElPos(e.getPageX(),
17124                             e.getPageY());
17125     },
17126
17127     toString: function() {
17128         return ("DD " + this.id);
17129     }
17130
17131     //////////////////////////////////////////////////////////////////////////
17132     // Debugging ygDragDrop events that can be overridden
17133     //////////////////////////////////////////////////////////////////////////
17134     /*
17135     startDrag: function(x, y) {
17136     },
17137
17138     onDrag: function(e) {
17139     },
17140
17141     onDragEnter: function(e, id) {
17142     },
17143
17144     onDragOver: function(e, id) {
17145     },
17146
17147     onDragOut: function(e, id) {
17148     },
17149
17150     onDragDrop: function(e, id) {
17151     },
17152
17153     endDrag: function(e) {
17154     }
17155
17156     */
17157
17158 });/*
17159  * Based on:
17160  * Ext JS Library 1.1.1
17161  * Copyright(c) 2006-2007, Ext JS, LLC.
17162  *
17163  * Originally Released Under LGPL - original licence link has changed is not relivant.
17164  *
17165  * Fork - LGPL
17166  * <script type="text/javascript">
17167  */
17168
17169 /**
17170  * @class Roo.dd.DDProxy
17171  * A DragDrop implementation that inserts an empty, bordered div into
17172  * the document that follows the cursor during drag operations.  At the time of
17173  * the click, the frame div is resized to the dimensions of the linked html
17174  * element, and moved to the exact location of the linked element.
17175  *
17176  * References to the "frame" element refer to the single proxy element that
17177  * was created to be dragged in place of all DDProxy elements on the
17178  * page.
17179  *
17180  * @extends Roo.dd.DD
17181  * @constructor
17182  * @param {String} id the id of the linked html element
17183  * @param {String} sGroup the group of related DragDrop objects
17184  * @param {object} config an object containing configurable attributes
17185  *                Valid properties for DDProxy in addition to those in DragDrop:
17186  *                   resizeFrame, centerFrame, dragElId
17187  */
17188 Roo.dd.DDProxy = function(id, sGroup, config) {
17189     if (id) {
17190         this.init(id, sGroup, config);
17191         this.initFrame();
17192     }
17193 };
17194
17195 /**
17196  * The default drag frame div id
17197  * @property Roo.dd.DDProxy.dragElId
17198  * @type String
17199  * @static
17200  */
17201 Roo.dd.DDProxy.dragElId = "ygddfdiv";
17202
17203 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
17204
17205     /**
17206      * By default we resize the drag frame to be the same size as the element
17207      * we want to drag (this is to get the frame effect).  We can turn it off
17208      * if we want a different behavior.
17209      * @property resizeFrame
17210      * @type boolean
17211      */
17212     resizeFrame: true,
17213
17214     /**
17215      * By default the frame is positioned exactly where the drag element is, so
17216      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
17217      * you do not have constraints on the obj is to have the drag frame centered
17218      * around the cursor.  Set centerFrame to true for this effect.
17219      * @property centerFrame
17220      * @type boolean
17221      */
17222     centerFrame: false,
17223
17224     /**
17225      * Creates the proxy element if it does not yet exist
17226      * @method createFrame
17227      */
17228     createFrame: function() {
17229         var self = this;
17230         var body = document.body;
17231
17232         if (!body || !body.firstChild) {
17233             setTimeout( function() { self.createFrame(); }, 50 );
17234             return;
17235         }
17236
17237         var div = this.getDragEl();
17238
17239         if (!div) {
17240             div    = document.createElement("div");
17241             div.id = this.dragElId;
17242             var s  = div.style;
17243
17244             s.position   = "absolute";
17245             s.visibility = "hidden";
17246             s.cursor     = "move";
17247             s.border     = "2px solid #aaa";
17248             s.zIndex     = 999;
17249
17250             // appendChild can blow up IE if invoked prior to the window load event
17251             // while rendering a table.  It is possible there are other scenarios
17252             // that would cause this to happen as well.
17253             body.insertBefore(div, body.firstChild);
17254         }
17255     },
17256
17257     /**
17258      * Initialization for the drag frame element.  Must be called in the
17259      * constructor of all subclasses
17260      * @method initFrame
17261      */
17262     initFrame: function() {
17263         this.createFrame();
17264     },
17265
17266     applyConfig: function() {
17267         Roo.dd.DDProxy.superclass.applyConfig.call(this);
17268
17269         this.resizeFrame = (this.config.resizeFrame !== false);
17270         this.centerFrame = (this.config.centerFrame);
17271         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
17272     },
17273
17274     /**
17275      * Resizes the drag frame to the dimensions of the clicked object, positions
17276      * it over the object, and finally displays it
17277      * @method showFrame
17278      * @param {int} iPageX X click position
17279      * @param {int} iPageY Y click position
17280      * @private
17281      */
17282     showFrame: function(iPageX, iPageY) {
17283         var el = this.getEl();
17284         var dragEl = this.getDragEl();
17285         var s = dragEl.style;
17286
17287         this._resizeProxy();
17288
17289         if (this.centerFrame) {
17290             this.setDelta( Math.round(parseInt(s.width,  10)/2),
17291                            Math.round(parseInt(s.height, 10)/2) );
17292         }
17293
17294         this.setDragElPos(iPageX, iPageY);
17295
17296         Roo.fly(dragEl).show();
17297     },
17298
17299     /**
17300      * The proxy is automatically resized to the dimensions of the linked
17301      * element when a drag is initiated, unless resizeFrame is set to false
17302      * @method _resizeProxy
17303      * @private
17304      */
17305     _resizeProxy: function() {
17306         if (this.resizeFrame) {
17307             var el = this.getEl();
17308             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
17309         }
17310     },
17311
17312     // overrides Roo.dd.DragDrop
17313     b4MouseDown: function(e) {
17314         var x = e.getPageX();
17315         var y = e.getPageY();
17316         this.autoOffset(x, y);
17317         this.setDragElPos(x, y);
17318     },
17319
17320     // overrides Roo.dd.DragDrop
17321     b4StartDrag: function(x, y) {
17322         // show the drag frame
17323         this.showFrame(x, y);
17324     },
17325
17326     // overrides Roo.dd.DragDrop
17327     b4EndDrag: function(e) {
17328         Roo.fly(this.getDragEl()).hide();
17329     },
17330
17331     // overrides Roo.dd.DragDrop
17332     // By default we try to move the element to the last location of the frame.
17333     // This is so that the default behavior mirrors that of Roo.dd.DD.
17334     endDrag: function(e) {
17335
17336         var lel = this.getEl();
17337         var del = this.getDragEl();
17338
17339         // Show the drag frame briefly so we can get its position
17340         del.style.visibility = "";
17341
17342         this.beforeMove();
17343         // Hide the linked element before the move to get around a Safari
17344         // rendering bug.
17345         lel.style.visibility = "hidden";
17346         Roo.dd.DDM.moveToEl(lel, del);
17347         del.style.visibility = "hidden";
17348         lel.style.visibility = "";
17349
17350         this.afterDrag();
17351     },
17352
17353     beforeMove : function(){
17354
17355     },
17356
17357     afterDrag : function(){
17358
17359     },
17360
17361     toString: function() {
17362         return ("DDProxy " + this.id);
17363     }
17364
17365 });
17366 /*
17367  * Based on:
17368  * Ext JS Library 1.1.1
17369  * Copyright(c) 2006-2007, Ext JS, LLC.
17370  *
17371  * Originally Released Under LGPL - original licence link has changed is not relivant.
17372  *
17373  * Fork - LGPL
17374  * <script type="text/javascript">
17375  */
17376
17377  /**
17378  * @class Roo.dd.DDTarget
17379  * A DragDrop implementation that does not move, but can be a drop
17380  * target.  You would get the same result by simply omitting implementation
17381  * for the event callbacks, but this way we reduce the processing cost of the
17382  * event listener and the callbacks.
17383  * @extends Roo.dd.DragDrop
17384  * @constructor
17385  * @param {String} id the id of the element that is a drop target
17386  * @param {String} sGroup the group of related DragDrop objects
17387  * @param {object} config an object containing configurable attributes
17388  *                 Valid properties for DDTarget in addition to those in
17389  *                 DragDrop:
17390  *                    none
17391  */
17392 Roo.dd.DDTarget = function(id, sGroup, config) {
17393     if (id) {
17394         this.initTarget(id, sGroup, config);
17395     }
17396     if (config.listeners || config.events) { 
17397        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
17398             listeners : config.listeners || {}, 
17399             events : config.events || {} 
17400         });    
17401     }
17402 };
17403
17404 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
17405 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
17406     toString: function() {
17407         return ("DDTarget " + this.id);
17408     }
17409 });
17410 /*
17411  * Based on:
17412  * Ext JS Library 1.1.1
17413  * Copyright(c) 2006-2007, Ext JS, LLC.
17414  *
17415  * Originally Released Under LGPL - original licence link has changed is not relivant.
17416  *
17417  * Fork - LGPL
17418  * <script type="text/javascript">
17419  */
17420  
17421
17422 /**
17423  * @class Roo.dd.ScrollManager
17424  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
17425  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
17426  * @singleton
17427  */
17428 Roo.dd.ScrollManager = function(){
17429     var ddm = Roo.dd.DragDropMgr;
17430     var els = {};
17431     var dragEl = null;
17432     var proc = {};
17433     
17434     var onStop = function(e){
17435         dragEl = null;
17436         clearProc();
17437     };
17438     
17439     var triggerRefresh = function(){
17440         if(ddm.dragCurrent){
17441              ddm.refreshCache(ddm.dragCurrent.groups);
17442         }
17443     };
17444     
17445     var doScroll = function(){
17446         if(ddm.dragCurrent){
17447             var dds = Roo.dd.ScrollManager;
17448             if(!dds.animate){
17449                 if(proc.el.scroll(proc.dir, dds.increment)){
17450                     triggerRefresh();
17451                 }
17452             }else{
17453                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
17454             }
17455         }
17456     };
17457     
17458     var clearProc = function(){
17459         if(proc.id){
17460             clearInterval(proc.id);
17461         }
17462         proc.id = 0;
17463         proc.el = null;
17464         proc.dir = "";
17465     };
17466     
17467     var startProc = function(el, dir){
17468         clearProc();
17469         proc.el = el;
17470         proc.dir = dir;
17471         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
17472     };
17473     
17474     var onFire = function(e, isDrop){
17475         if(isDrop || !ddm.dragCurrent){ return; }
17476         var dds = Roo.dd.ScrollManager;
17477         if(!dragEl || dragEl != ddm.dragCurrent){
17478             dragEl = ddm.dragCurrent;
17479             // refresh regions on drag start
17480             dds.refreshCache();
17481         }
17482         
17483         var xy = Roo.lib.Event.getXY(e);
17484         var pt = new Roo.lib.Point(xy[0], xy[1]);
17485         for(var id in els){
17486             var el = els[id], r = el._region;
17487             if(r && r.contains(pt) && el.isScrollable()){
17488                 if(r.bottom - pt.y <= dds.thresh){
17489                     if(proc.el != el){
17490                         startProc(el, "down");
17491                     }
17492                     return;
17493                 }else if(r.right - pt.x <= dds.thresh){
17494                     if(proc.el != el){
17495                         startProc(el, "left");
17496                     }
17497                     return;
17498                 }else if(pt.y - r.top <= dds.thresh){
17499                     if(proc.el != el){
17500                         startProc(el, "up");
17501                     }
17502                     return;
17503                 }else if(pt.x - r.left <= dds.thresh){
17504                     if(proc.el != el){
17505                         startProc(el, "right");
17506                     }
17507                     return;
17508                 }
17509             }
17510         }
17511         clearProc();
17512     };
17513     
17514     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
17515     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
17516     
17517     return {
17518         /**
17519          * Registers new overflow element(s) to auto scroll
17520          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
17521          */
17522         register : function(el){
17523             if(el instanceof Array){
17524                 for(var i = 0, len = el.length; i < len; i++) {
17525                         this.register(el[i]);
17526                 }
17527             }else{
17528                 el = Roo.get(el);
17529                 els[el.id] = el;
17530             }
17531         },
17532         
17533         /**
17534          * Unregisters overflow element(s) so they are no longer scrolled
17535          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
17536          */
17537         unregister : function(el){
17538             if(el instanceof Array){
17539                 for(var i = 0, len = el.length; i < len; i++) {
17540                         this.unregister(el[i]);
17541                 }
17542             }else{
17543                 el = Roo.get(el);
17544                 delete els[el.id];
17545             }
17546         },
17547         
17548         /**
17549          * The number of pixels from the edge of a container the pointer needs to be to 
17550          * trigger scrolling (defaults to 25)
17551          * @type Number
17552          */
17553         thresh : 25,
17554         
17555         /**
17556          * The number of pixels to scroll in each scroll increment (defaults to 50)
17557          * @type Number
17558          */
17559         increment : 100,
17560         
17561         /**
17562          * The frequency of scrolls in milliseconds (defaults to 500)
17563          * @type Number
17564          */
17565         frequency : 500,
17566         
17567         /**
17568          * True to animate the scroll (defaults to true)
17569          * @type Boolean
17570          */
17571         animate: true,
17572         
17573         /**
17574          * The animation duration in seconds - 
17575          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
17576          * @type Number
17577          */
17578         animDuration: .4,
17579         
17580         /**
17581          * Manually trigger a cache refresh.
17582          */
17583         refreshCache : function(){
17584             for(var id in els){
17585                 if(typeof els[id] == 'object'){ // for people extending the object prototype
17586                     els[id]._region = els[id].getRegion();
17587                 }
17588             }
17589         }
17590     };
17591 }();/*
17592  * Based on:
17593  * Ext JS Library 1.1.1
17594  * Copyright(c) 2006-2007, Ext JS, LLC.
17595  *
17596  * Originally Released Under LGPL - original licence link has changed is not relivant.
17597  *
17598  * Fork - LGPL
17599  * <script type="text/javascript">
17600  */
17601  
17602
17603 /**
17604  * @class Roo.dd.Registry
17605  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
17606  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
17607  * @singleton
17608  */
17609 Roo.dd.Registry = function(){
17610     var elements = {}; 
17611     var handles = {}; 
17612     var autoIdSeed = 0;
17613
17614     var getId = function(el, autogen){
17615         if(typeof el == "string"){
17616             return el;
17617         }
17618         var id = el.id;
17619         if(!id && autogen !== false){
17620             id = "roodd-" + (++autoIdSeed);
17621             el.id = id;
17622         }
17623         return id;
17624     };
17625     
17626     return {
17627     /**
17628      * Register a drag drop element
17629      * @param {String|HTMLElement} element The id or DOM node to register
17630      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
17631      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
17632      * knows how to interpret, plus there are some specific properties known to the Registry that should be
17633      * populated in the data object (if applicable):
17634      * <pre>
17635 Value      Description<br />
17636 ---------  ------------------------------------------<br />
17637 handles    Array of DOM nodes that trigger dragging<br />
17638            for the element being registered<br />
17639 isHandle   True if the element passed in triggers<br />
17640            dragging itself, else false
17641 </pre>
17642      */
17643         register : function(el, data){
17644             data = data || {};
17645             if(typeof el == "string"){
17646                 el = document.getElementById(el);
17647             }
17648             data.ddel = el;
17649             elements[getId(el)] = data;
17650             if(data.isHandle !== false){
17651                 handles[data.ddel.id] = data;
17652             }
17653             if(data.handles){
17654                 var hs = data.handles;
17655                 for(var i = 0, len = hs.length; i < len; i++){
17656                         handles[getId(hs[i])] = data;
17657                 }
17658             }
17659         },
17660
17661     /**
17662      * Unregister a drag drop element
17663      * @param {String|HTMLElement}  element The id or DOM node to unregister
17664      */
17665         unregister : function(el){
17666             var id = getId(el, false);
17667             var data = elements[id];
17668             if(data){
17669                 delete elements[id];
17670                 if(data.handles){
17671                     var hs = data.handles;
17672                     for(var i = 0, len = hs.length; i < len; i++){
17673                         delete handles[getId(hs[i], false)];
17674                     }
17675                 }
17676             }
17677         },
17678
17679     /**
17680      * Returns the handle registered for a DOM Node by id
17681      * @param {String|HTMLElement} id The DOM node or id to look up
17682      * @return {Object} handle The custom handle data
17683      */
17684         getHandle : function(id){
17685             if(typeof id != "string"){ // must be element?
17686                 id = id.id;
17687             }
17688             return handles[id];
17689         },
17690
17691     /**
17692      * Returns the handle that is registered for the DOM node that is the target of the event
17693      * @param {Event} e The event
17694      * @return {Object} handle The custom handle data
17695      */
17696         getHandleFromEvent : function(e){
17697             var t = Roo.lib.Event.getTarget(e);
17698             return t ? handles[t.id] : null;
17699         },
17700
17701     /**
17702      * Returns a custom data object that is registered for a DOM node by id
17703      * @param {String|HTMLElement} id The DOM node or id to look up
17704      * @return {Object} data The custom data
17705      */
17706         getTarget : function(id){
17707             if(typeof id != "string"){ // must be element?
17708                 id = id.id;
17709             }
17710             return elements[id];
17711         },
17712
17713     /**
17714      * Returns a custom data object that is registered for the DOM node that is the target of the event
17715      * @param {Event} e The event
17716      * @return {Object} data The custom data
17717      */
17718         getTargetFromEvent : function(e){
17719             var t = Roo.lib.Event.getTarget(e);
17720             return t ? elements[t.id] || handles[t.id] : null;
17721         }
17722     };
17723 }();/*
17724  * Based on:
17725  * Ext JS Library 1.1.1
17726  * Copyright(c) 2006-2007, Ext JS, LLC.
17727  *
17728  * Originally Released Under LGPL - original licence link has changed is not relivant.
17729  *
17730  * Fork - LGPL
17731  * <script type="text/javascript">
17732  */
17733  
17734
17735 /**
17736  * @class Roo.dd.StatusProxy
17737  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
17738  * default drag proxy used by all Roo.dd components.
17739  * @constructor
17740  * @param {Object} config
17741  */
17742 Roo.dd.StatusProxy = function(config){
17743     Roo.apply(this, config);
17744     this.id = this.id || Roo.id();
17745     this.el = new Roo.Layer({
17746         dh: {
17747             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
17748                 {tag: "div", cls: "x-dd-drop-icon"},
17749                 {tag: "div", cls: "x-dd-drag-ghost"}
17750             ]
17751         }, 
17752         shadow: !config || config.shadow !== false
17753     });
17754     this.ghost = Roo.get(this.el.dom.childNodes[1]);
17755     this.dropStatus = this.dropNotAllowed;
17756 };
17757
17758 Roo.dd.StatusProxy.prototype = {
17759     /**
17760      * @cfg {String} dropAllowed
17761      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
17762      */
17763     dropAllowed : "x-dd-drop-ok",
17764     /**
17765      * @cfg {String} dropNotAllowed
17766      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
17767      */
17768     dropNotAllowed : "x-dd-drop-nodrop",
17769
17770     /**
17771      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
17772      * over the current target element.
17773      * @param {String} cssClass The css class for the new drop status indicator image
17774      */
17775     setStatus : function(cssClass){
17776         cssClass = cssClass || this.dropNotAllowed;
17777         if(this.dropStatus != cssClass){
17778             this.el.replaceClass(this.dropStatus, cssClass);
17779             this.dropStatus = cssClass;
17780         }
17781     },
17782
17783     /**
17784      * Resets the status indicator to the default dropNotAllowed value
17785      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
17786      */
17787     reset : function(clearGhost){
17788         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
17789         this.dropStatus = this.dropNotAllowed;
17790         if(clearGhost){
17791             this.ghost.update("");
17792         }
17793     },
17794
17795     /**
17796      * Updates the contents of the ghost element
17797      * @param {String} html The html that will replace the current innerHTML of the ghost element
17798      */
17799     update : function(html){
17800         if(typeof html == "string"){
17801             this.ghost.update(html);
17802         }else{
17803             this.ghost.update("");
17804             html.style.margin = "0";
17805             this.ghost.dom.appendChild(html);
17806         }
17807         // ensure float = none set?? cant remember why though.
17808         var el = this.ghost.dom.firstChild;
17809                 if(el){
17810                         Roo.fly(el).setStyle('float', 'none');
17811                 }
17812     },
17813     
17814     /**
17815      * Returns the underlying proxy {@link Roo.Layer}
17816      * @return {Roo.Layer} el
17817     */
17818     getEl : function(){
17819         return this.el;
17820     },
17821
17822     /**
17823      * Returns the ghost element
17824      * @return {Roo.Element} el
17825      */
17826     getGhost : function(){
17827         return this.ghost;
17828     },
17829
17830     /**
17831      * Hides the proxy
17832      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
17833      */
17834     hide : function(clear){
17835         this.el.hide();
17836         if(clear){
17837             this.reset(true);
17838         }
17839     },
17840
17841     /**
17842      * Stops the repair animation if it's currently running
17843      */
17844     stop : function(){
17845         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
17846             this.anim.stop();
17847         }
17848     },
17849
17850     /**
17851      * Displays this proxy
17852      */
17853     show : function(){
17854         this.el.show();
17855     },
17856
17857     /**
17858      * Force the Layer to sync its shadow and shim positions to the element
17859      */
17860     sync : function(){
17861         this.el.sync();
17862     },
17863
17864     /**
17865      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
17866      * invalid drop operation by the item being dragged.
17867      * @param {Array} xy The XY position of the element ([x, y])
17868      * @param {Function} callback The function to call after the repair is complete
17869      * @param {Object} scope The scope in which to execute the callback
17870      */
17871     repair : function(xy, callback, scope){
17872         this.callback = callback;
17873         this.scope = scope;
17874         if(xy && this.animRepair !== false){
17875             this.el.addClass("x-dd-drag-repair");
17876             this.el.hideUnders(true);
17877             this.anim = this.el.shift({
17878                 duration: this.repairDuration || .5,
17879                 easing: 'easeOut',
17880                 xy: xy,
17881                 stopFx: true,
17882                 callback: this.afterRepair,
17883                 scope: this
17884             });
17885         }else{
17886             this.afterRepair();
17887         }
17888     },
17889
17890     // private
17891     afterRepair : function(){
17892         this.hide(true);
17893         if(typeof this.callback == "function"){
17894             this.callback.call(this.scope || this);
17895         }
17896         this.callback = null;
17897         this.scope = null;
17898     }
17899 };/*
17900  * Based on:
17901  * Ext JS Library 1.1.1
17902  * Copyright(c) 2006-2007, Ext JS, LLC.
17903  *
17904  * Originally Released Under LGPL - original licence link has changed is not relivant.
17905  *
17906  * Fork - LGPL
17907  * <script type="text/javascript">
17908  */
17909
17910 /**
17911  * @class Roo.dd.DragSource
17912  * @extends Roo.dd.DDProxy
17913  * A simple class that provides the basic implementation needed to make any element draggable.
17914  * @constructor
17915  * @param {String/HTMLElement/Element} el The container element
17916  * @param {Object} config
17917  */
17918 Roo.dd.DragSource = function(el, config){
17919     this.el = Roo.get(el);
17920     this.dragData = {};
17921     
17922     Roo.apply(this, config);
17923     
17924     if(!this.proxy){
17925         this.proxy = new Roo.dd.StatusProxy();
17926     }
17927
17928     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
17929           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
17930     
17931     this.dragging = false;
17932 };
17933
17934 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
17935     /**
17936      * @cfg {String} dropAllowed
17937      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
17938      */
17939     dropAllowed : "x-dd-drop-ok",
17940     /**
17941      * @cfg {String} dropNotAllowed
17942      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
17943      */
17944     dropNotAllowed : "x-dd-drop-nodrop",
17945
17946     /**
17947      * Returns the data object associated with this drag source
17948      * @return {Object} data An object containing arbitrary data
17949      */
17950     getDragData : function(e){
17951         return this.dragData;
17952     },
17953
17954     // private
17955     onDragEnter : function(e, id){
17956         var target = Roo.dd.DragDropMgr.getDDById(id);
17957         this.cachedTarget = target;
17958         if(this.beforeDragEnter(target, e, id) !== false){
17959             if(target.isNotifyTarget){
17960                 var status = target.notifyEnter(this, e, this.dragData);
17961                 this.proxy.setStatus(status);
17962             }else{
17963                 this.proxy.setStatus(this.dropAllowed);
17964             }
17965             
17966             if(this.afterDragEnter){
17967                 /**
17968                  * An empty function by default, but provided so that you can perform a custom action
17969                  * when the dragged item enters the drop target by providing an implementation.
17970                  * @param {Roo.dd.DragDrop} target The drop target
17971                  * @param {Event} e The event object
17972                  * @param {String} id The id of the dragged element
17973                  * @method afterDragEnter
17974                  */
17975                 this.afterDragEnter(target, e, id);
17976             }
17977         }
17978     },
17979
17980     /**
17981      * An empty function by default, but provided so that you can perform a custom action
17982      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
17983      * @param {Roo.dd.DragDrop} target The drop target
17984      * @param {Event} e The event object
17985      * @param {String} id The id of the dragged element
17986      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
17987      */
17988     beforeDragEnter : function(target, e, id){
17989         return true;
17990     },
17991
17992     // private
17993     alignElWithMouse: function() {
17994         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
17995         this.proxy.sync();
17996     },
17997
17998     // private
17999     onDragOver : function(e, id){
18000         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18001         if(this.beforeDragOver(target, e, id) !== false){
18002             if(target.isNotifyTarget){
18003                 var status = target.notifyOver(this, e, this.dragData);
18004                 this.proxy.setStatus(status);
18005             }
18006
18007             if(this.afterDragOver){
18008                 /**
18009                  * An empty function by default, but provided so that you can perform a custom action
18010                  * while the dragged item is over the drop target by providing an implementation.
18011                  * @param {Roo.dd.DragDrop} target The drop target
18012                  * @param {Event} e The event object
18013                  * @param {String} id The id of the dragged element
18014                  * @method afterDragOver
18015                  */
18016                 this.afterDragOver(target, e, id);
18017             }
18018         }
18019     },
18020
18021     /**
18022      * An empty function by default, but provided so that you can perform a custom action
18023      * while the dragged item is over the drop target and optionally cancel the onDragOver.
18024      * @param {Roo.dd.DragDrop} target The drop target
18025      * @param {Event} e The event object
18026      * @param {String} id The id of the dragged element
18027      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18028      */
18029     beforeDragOver : function(target, e, id){
18030         return true;
18031     },
18032
18033     // private
18034     onDragOut : function(e, id){
18035         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18036         if(this.beforeDragOut(target, e, id) !== false){
18037             if(target.isNotifyTarget){
18038                 target.notifyOut(this, e, this.dragData);
18039             }
18040             this.proxy.reset();
18041             if(this.afterDragOut){
18042                 /**
18043                  * An empty function by default, but provided so that you can perform a custom action
18044                  * after the dragged item is dragged out of the target without dropping.
18045                  * @param {Roo.dd.DragDrop} target The drop target
18046                  * @param {Event} e The event object
18047                  * @param {String} id The id of the dragged element
18048                  * @method afterDragOut
18049                  */
18050                 this.afterDragOut(target, e, id);
18051             }
18052         }
18053         this.cachedTarget = null;
18054     },
18055
18056     /**
18057      * An empty function by default, but provided so that you can perform a custom action before the dragged
18058      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
18059      * @param {Roo.dd.DragDrop} target The drop target
18060      * @param {Event} e The event object
18061      * @param {String} id The id of the dragged element
18062      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18063      */
18064     beforeDragOut : function(target, e, id){
18065         return true;
18066     },
18067     
18068     // private
18069     onDragDrop : function(e, id){
18070         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
18071         if(this.beforeDragDrop(target, e, id) !== false){
18072             if(target.isNotifyTarget){
18073                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
18074                     this.onValidDrop(target, e, id);
18075                 }else{
18076                     this.onInvalidDrop(target, e, id);
18077                 }
18078             }else{
18079                 this.onValidDrop(target, e, id);
18080             }
18081             
18082             if(this.afterDragDrop){
18083                 /**
18084                  * An empty function by default, but provided so that you can perform a custom action
18085                  * after a valid drag drop has occurred by providing an implementation.
18086                  * @param {Roo.dd.DragDrop} target The drop target
18087                  * @param {Event} e The event object
18088                  * @param {String} id The id of the dropped element
18089                  * @method afterDragDrop
18090                  */
18091                 this.afterDragDrop(target, e, id);
18092             }
18093         }
18094         delete this.cachedTarget;
18095     },
18096
18097     /**
18098      * An empty function by default, but provided so that you can perform a custom action before the dragged
18099      * item is dropped onto the target and optionally cancel the onDragDrop.
18100      * @param {Roo.dd.DragDrop} target The drop target
18101      * @param {Event} e The event object
18102      * @param {String} id The id of the dragged element
18103      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
18104      */
18105     beforeDragDrop : function(target, e, id){
18106         return true;
18107     },
18108
18109     // private
18110     onValidDrop : function(target, e, id){
18111         this.hideProxy();
18112         if(this.afterValidDrop){
18113             /**
18114              * An empty function by default, but provided so that you can perform a custom action
18115              * after a valid drop has occurred by providing an implementation.
18116              * @param {Object} target The target DD 
18117              * @param {Event} e The event object
18118              * @param {String} id The id of the dropped element
18119              * @method afterInvalidDrop
18120              */
18121             this.afterValidDrop(target, e, id);
18122         }
18123     },
18124
18125     // private
18126     getRepairXY : function(e, data){
18127         return this.el.getXY();  
18128     },
18129
18130     // private
18131     onInvalidDrop : function(target, e, id){
18132         this.beforeInvalidDrop(target, e, id);
18133         if(this.cachedTarget){
18134             if(this.cachedTarget.isNotifyTarget){
18135                 this.cachedTarget.notifyOut(this, e, this.dragData);
18136             }
18137             this.cacheTarget = null;
18138         }
18139         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
18140
18141         if(this.afterInvalidDrop){
18142             /**
18143              * An empty function by default, but provided so that you can perform a custom action
18144              * after an invalid drop has occurred by providing an implementation.
18145              * @param {Event} e The event object
18146              * @param {String} id The id of the dropped element
18147              * @method afterInvalidDrop
18148              */
18149             this.afterInvalidDrop(e, id);
18150         }
18151     },
18152
18153     // private
18154     afterRepair : function(){
18155         if(Roo.enableFx){
18156             this.el.highlight(this.hlColor || "c3daf9");
18157         }
18158         this.dragging = false;
18159     },
18160
18161     /**
18162      * An empty function by default, but provided so that you can perform a custom action after an invalid
18163      * drop has occurred.
18164      * @param {Roo.dd.DragDrop} target The drop target
18165      * @param {Event} e The event object
18166      * @param {String} id The id of the dragged element
18167      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
18168      */
18169     beforeInvalidDrop : function(target, e, id){
18170         return true;
18171     },
18172
18173     // private
18174     handleMouseDown : function(e){
18175         if(this.dragging) {
18176             return;
18177         }
18178         var data = this.getDragData(e);
18179         if(data && this.onBeforeDrag(data, e) !== false){
18180             this.dragData = data;
18181             this.proxy.stop();
18182             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
18183         } 
18184     },
18185
18186     /**
18187      * An empty function by default, but provided so that you can perform a custom action before the initial
18188      * drag event begins and optionally cancel it.
18189      * @param {Object} data An object containing arbitrary data to be shared with drop targets
18190      * @param {Event} e The event object
18191      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
18192      */
18193     onBeforeDrag : function(data, e){
18194         return true;
18195     },
18196
18197     /**
18198      * An empty function by default, but provided so that you can perform a custom action once the initial
18199      * drag event has begun.  The drag cannot be canceled from this function.
18200      * @param {Number} x The x position of the click on the dragged object
18201      * @param {Number} y The y position of the click on the dragged object
18202      */
18203     onStartDrag : Roo.emptyFn,
18204
18205     // private - YUI override
18206     startDrag : function(x, y){
18207         this.proxy.reset();
18208         this.dragging = true;
18209         this.proxy.update("");
18210         this.onInitDrag(x, y);
18211         this.proxy.show();
18212     },
18213
18214     // private
18215     onInitDrag : function(x, y){
18216         var clone = this.el.dom.cloneNode(true);
18217         clone.id = Roo.id(); // prevent duplicate ids
18218         this.proxy.update(clone);
18219         this.onStartDrag(x, y);
18220         return true;
18221     },
18222
18223     /**
18224      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
18225      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
18226      */
18227     getProxy : function(){
18228         return this.proxy;  
18229     },
18230
18231     /**
18232      * Hides the drag source's {@link Roo.dd.StatusProxy}
18233      */
18234     hideProxy : function(){
18235         this.proxy.hide();  
18236         this.proxy.reset(true);
18237         this.dragging = false;
18238     },
18239
18240     // private
18241     triggerCacheRefresh : function(){
18242         Roo.dd.DDM.refreshCache(this.groups);
18243     },
18244
18245     // private - override to prevent hiding
18246     b4EndDrag: function(e) {
18247     },
18248
18249     // private - override to prevent moving
18250     endDrag : function(e){
18251         this.onEndDrag(this.dragData, e);
18252     },
18253
18254     // private
18255     onEndDrag : function(data, e){
18256     },
18257     
18258     // private - pin to cursor
18259     autoOffset : function(x, y) {
18260         this.setDelta(-12, -20);
18261     }    
18262 });/*
18263  * Based on:
18264  * Ext JS Library 1.1.1
18265  * Copyright(c) 2006-2007, Ext JS, LLC.
18266  *
18267  * Originally Released Under LGPL - original licence link has changed is not relivant.
18268  *
18269  * Fork - LGPL
18270  * <script type="text/javascript">
18271  */
18272
18273
18274 /**
18275  * @class Roo.dd.DropTarget
18276  * @extends Roo.dd.DDTarget
18277  * A simple class that provides the basic implementation needed to make any element a drop target that can have
18278  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
18279  * @constructor
18280  * @param {String/HTMLElement/Element} el The container element
18281  * @param {Object} config
18282  */
18283 Roo.dd.DropTarget = function(el, config){
18284     this.el = Roo.get(el);
18285     
18286     var listeners = false; ;
18287     if (config && config.listeners) {
18288         listeners= config.listeners;
18289         delete config.listeners;
18290     }
18291     Roo.apply(this, config);
18292     
18293     if(this.containerScroll){
18294         Roo.dd.ScrollManager.register(this.el);
18295     }
18296     this.addEvents( {
18297          /**
18298          * @scope Roo.dd.DropTarget
18299          */
18300          
18301          /**
18302          * @event enter
18303          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
18304          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
18305          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
18306          * 
18307          * IMPORTANT : it should set this.overClass and this.dropAllowed
18308          * 
18309          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18310          * @param {Event} e The event
18311          * @param {Object} data An object containing arbitrary data supplied by the drag source
18312          */
18313         "enter" : true,
18314         
18315          /**
18316          * @event over
18317          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
18318          * This method will be called on every mouse movement while the drag source is over the drop target.
18319          * This default implementation simply returns the dropAllowed config value.
18320          * 
18321          * IMPORTANT : it should set this.dropAllowed
18322          * 
18323          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18324          * @param {Event} e The event
18325          * @param {Object} data An object containing arbitrary data supplied by the drag source
18326          
18327          */
18328         "over" : true,
18329         /**
18330          * @event out
18331          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
18332          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
18333          * overClass (if any) from the drop element.
18334          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18335          * @param {Event} e The event
18336          * @param {Object} data An object containing arbitrary data supplied by the drag source
18337          */
18338          "out" : true,
18339          
18340         /**
18341          * @event drop
18342          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
18343          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
18344          * implementation that does something to process the drop event and returns true so that the drag source's
18345          * repair action does not run.
18346          * 
18347          * IMPORTANT : it should set this.success
18348          * 
18349          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18350          * @param {Event} e The event
18351          * @param {Object} data An object containing arbitrary data supplied by the drag source
18352         */
18353          "drop" : true
18354     });
18355             
18356      
18357     Roo.dd.DropTarget.superclass.constructor.call(  this, 
18358         this.el.dom, 
18359         this.ddGroup || this.group,
18360         {
18361             isTarget: true,
18362             listeners : listeners || {} 
18363            
18364         
18365         }
18366     );
18367
18368 };
18369
18370 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
18371     /**
18372      * @cfg {String} overClass
18373      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
18374      */
18375      /**
18376      * @cfg {String} ddGroup
18377      * The drag drop group to handle drop events for
18378      */
18379      
18380     /**
18381      * @cfg {String} dropAllowed
18382      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
18383      */
18384     dropAllowed : "x-dd-drop-ok",
18385     /**
18386      * @cfg {String} dropNotAllowed
18387      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
18388      */
18389     dropNotAllowed : "x-dd-drop-nodrop",
18390     /**
18391      * @cfg {boolean} success
18392      * set this after drop listener.. 
18393      */
18394     success : false,
18395     /**
18396      * @cfg {boolean|String} valid true/false or string (add/sub/ok/nodrop)
18397      * if the drop point is valid for over/enter..
18398      */
18399     valid : false,
18400     // private
18401     isTarget : true,
18402
18403     // private
18404     isNotifyTarget : true,
18405     
18406     /**
18407      * @hide
18408      */
18409     notifyEnter : function(dd, e, data){
18410         this.valid = true;
18411         this.fireEvent('enter', dd, e, data);
18412         if(this.overClass){
18413             this.el.addClass(this.overClass);
18414         }
18415         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18416             this.valid ? this.dropAllowed : this.dropNotAllowed
18417         );
18418     },
18419
18420     /**
18421      * @hide
18422      */
18423     notifyOver : function(dd, e, data){
18424         this.valid = true;
18425         this.fireEvent('over', dd, e, data);
18426         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
18427             this.valid ? this.dropAllowed : this.dropNotAllowed
18428         );
18429     },
18430
18431     /**
18432      * @hide
18433      */
18434     notifyOut : function(dd, e, data){
18435         this.fireEvent('out', dd, e, data);
18436         if(this.overClass){
18437             this.el.removeClass(this.overClass);
18438         }
18439     },
18440
18441     /**
18442      * @hide
18443      */
18444     notifyDrop : function(dd, e, data){
18445         this.success = false;
18446         this.fireEvent('drop', dd, e, data);
18447         return this.success;
18448     }
18449 });/*
18450  * Based on:
18451  * Ext JS Library 1.1.1
18452  * Copyright(c) 2006-2007, Ext JS, LLC.
18453  *
18454  * Originally Released Under LGPL - original licence link has changed is not relivant.
18455  *
18456  * Fork - LGPL
18457  * <script type="text/javascript">
18458  */
18459
18460
18461 /**
18462  * @class Roo.dd.DragZone
18463  * @extends Roo.dd.DragSource
18464  * This class provides a container DD instance that proxies for multiple child node sources.<br />
18465  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
18466  * @constructor
18467  * @param {String/HTMLElement/Element} el The container element
18468  * @param {Object} config
18469  */
18470 Roo.dd.DragZone = function(el, config){
18471     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
18472     if(this.containerScroll){
18473         Roo.dd.ScrollManager.register(this.el);
18474     }
18475 };
18476
18477 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
18478     /**
18479      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
18480      * for auto scrolling during drag operations.
18481      */
18482     /**
18483      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
18484      * method after a failed drop (defaults to "c3daf9" - light blue)
18485      */
18486
18487     /**
18488      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
18489      * for a valid target to drag based on the mouse down. Override this method
18490      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
18491      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
18492      * @param {EventObject} e The mouse down event
18493      * @return {Object} The dragData
18494      */
18495     getDragData : function(e){
18496         return Roo.dd.Registry.getHandleFromEvent(e);
18497     },
18498     
18499     /**
18500      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
18501      * this.dragData.ddel
18502      * @param {Number} x The x position of the click on the dragged object
18503      * @param {Number} y The y position of the click on the dragged object
18504      * @return {Boolean} true to continue the drag, false to cancel
18505      */
18506     onInitDrag : function(x, y){
18507         this.proxy.update(this.dragData.ddel.cloneNode(true));
18508         this.onStartDrag(x, y);
18509         return true;
18510     },
18511     
18512     /**
18513      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
18514      */
18515     afterRepair : function(){
18516         if(Roo.enableFx){
18517             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
18518         }
18519         this.dragging = false;
18520     },
18521
18522     /**
18523      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
18524      * the XY of this.dragData.ddel
18525      * @param {EventObject} e The mouse up event
18526      * @return {Array} The xy location (e.g. [100, 200])
18527      */
18528     getRepairXY : function(e){
18529         return Roo.Element.fly(this.dragData.ddel).getXY();  
18530     }
18531 });/*
18532  * Based on:
18533  * Ext JS Library 1.1.1
18534  * Copyright(c) 2006-2007, Ext JS, LLC.
18535  *
18536  * Originally Released Under LGPL - original licence link has changed is not relivant.
18537  *
18538  * Fork - LGPL
18539  * <script type="text/javascript">
18540  */
18541 /**
18542  * @class Roo.dd.DropZone
18543  * @extends Roo.dd.DropTarget
18544  * This class provides a container DD instance that proxies for multiple child node targets.<br />
18545  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
18546  * @constructor
18547  * @param {String/HTMLElement/Element} el The container element
18548  * @param {Object} config
18549  */
18550 Roo.dd.DropZone = function(el, config){
18551     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
18552 };
18553
18554 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
18555     /**
18556      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
18557      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
18558      * provide your own custom lookup.
18559      * @param {Event} e The event
18560      * @return {Object} data The custom data
18561      */
18562     getTargetFromEvent : function(e){
18563         return Roo.dd.Registry.getTargetFromEvent(e);
18564     },
18565
18566     /**
18567      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
18568      * that it has registered.  This method has no default implementation and should be overridden to provide
18569      * node-specific processing if necessary.
18570      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
18571      * {@link #getTargetFromEvent} for this node)
18572      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18573      * @param {Event} e The event
18574      * @param {Object} data An object containing arbitrary data supplied by the drag source
18575      */
18576     onNodeEnter : function(n, dd, e, data){
18577         
18578     },
18579
18580     /**
18581      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
18582      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
18583      * overridden to provide the proper feedback.
18584      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18585      * {@link #getTargetFromEvent} for this node)
18586      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18587      * @param {Event} e The event
18588      * @param {Object} data An object containing arbitrary data supplied by the drag source
18589      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18590      * underlying {@link Roo.dd.StatusProxy} can be updated
18591      */
18592     onNodeOver : function(n, dd, e, data){
18593         return this.dropAllowed;
18594     },
18595
18596     /**
18597      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
18598      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
18599      * node-specific processing if necessary.
18600      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18601      * {@link #getTargetFromEvent} for this node)
18602      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18603      * @param {Event} e The event
18604      * @param {Object} data An object containing arbitrary data supplied by the drag source
18605      */
18606     onNodeOut : function(n, dd, e, data){
18607         
18608     },
18609
18610     /**
18611      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
18612      * the drop node.  The default implementation returns false, so it should be overridden to provide the
18613      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
18614      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
18615      * {@link #getTargetFromEvent} for this node)
18616      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18617      * @param {Event} e The event
18618      * @param {Object} data An object containing arbitrary data supplied by the drag source
18619      * @return {Boolean} True if the drop was valid, else false
18620      */
18621     onNodeDrop : function(n, dd, e, data){
18622         return false;
18623     },
18624
18625     /**
18626      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
18627      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
18628      * it should be overridden to provide the proper feedback if necessary.
18629      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18630      * @param {Event} e The event
18631      * @param {Object} data An object containing arbitrary data supplied by the drag source
18632      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18633      * underlying {@link Roo.dd.StatusProxy} can be updated
18634      */
18635     onContainerOver : function(dd, e, data){
18636         return this.dropNotAllowed;
18637     },
18638
18639     /**
18640      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
18641      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
18642      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
18643      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
18644      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18645      * @param {Event} e The event
18646      * @param {Object} data An object containing arbitrary data supplied by the drag source
18647      * @return {Boolean} True if the drop was valid, else false
18648      */
18649     onContainerDrop : function(dd, e, data){
18650         return false;
18651     },
18652
18653     /**
18654      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
18655      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
18656      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
18657      * you should override this method and provide a custom implementation.
18658      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18659      * @param {Event} e The event
18660      * @param {Object} data An object containing arbitrary data supplied by the drag source
18661      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18662      * underlying {@link Roo.dd.StatusProxy} can be updated
18663      */
18664     notifyEnter : function(dd, e, data){
18665         return this.dropNotAllowed;
18666     },
18667
18668     /**
18669      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
18670      * This method will be called on every mouse movement while the drag source is over the drop zone.
18671      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
18672      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
18673      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
18674      * registered node, it will call {@link #onContainerOver}.
18675      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18676      * @param {Event} e The event
18677      * @param {Object} data An object containing arbitrary data supplied by the drag source
18678      * @return {String} status The CSS class that communicates the drop status back to the source so that the
18679      * underlying {@link Roo.dd.StatusProxy} can be updated
18680      */
18681     notifyOver : function(dd, e, data){
18682         var n = this.getTargetFromEvent(e);
18683         if(!n){ // not over valid drop target
18684             if(this.lastOverNode){
18685                 this.onNodeOut(this.lastOverNode, dd, e, data);
18686                 this.lastOverNode = null;
18687             }
18688             return this.onContainerOver(dd, e, data);
18689         }
18690         if(this.lastOverNode != n){
18691             if(this.lastOverNode){
18692                 this.onNodeOut(this.lastOverNode, dd, e, data);
18693             }
18694             this.onNodeEnter(n, dd, e, data);
18695             this.lastOverNode = n;
18696         }
18697         return this.onNodeOver(n, dd, e, data);
18698     },
18699
18700     /**
18701      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
18702      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
18703      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
18704      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
18705      * @param {Event} e The event
18706      * @param {Object} data An object containing arbitrary data supplied by the drag zone
18707      */
18708     notifyOut : function(dd, e, data){
18709         if(this.lastOverNode){
18710             this.onNodeOut(this.lastOverNode, dd, e, data);
18711             this.lastOverNode = null;
18712         }
18713     },
18714
18715     /**
18716      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
18717      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
18718      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
18719      * otherwise it will call {@link #onContainerDrop}.
18720      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
18721      * @param {Event} e The event
18722      * @param {Object} data An object containing arbitrary data supplied by the drag source
18723      * @return {Boolean} True if the drop was valid, else false
18724      */
18725     notifyDrop : function(dd, e, data){
18726         if(this.lastOverNode){
18727             this.onNodeOut(this.lastOverNode, dd, e, data);
18728             this.lastOverNode = null;
18729         }
18730         var n = this.getTargetFromEvent(e);
18731         return n ?
18732             this.onNodeDrop(n, dd, e, data) :
18733             this.onContainerDrop(dd, e, data);
18734     },
18735
18736     // private
18737     triggerCacheRefresh : function(){
18738         Roo.dd.DDM.refreshCache(this.groups);
18739     }  
18740 });/*
18741  * Based on:
18742  * Ext JS Library 1.1.1
18743  * Copyright(c) 2006-2007, Ext JS, LLC.
18744  *
18745  * Originally Released Under LGPL - original licence link has changed is not relivant.
18746  *
18747  * Fork - LGPL
18748  * <script type="text/javascript">
18749  */
18750
18751
18752 /**
18753  * @class Roo.data.SortTypes
18754  * @singleton
18755  * Defines the default sorting (casting?) comparison functions used when sorting data.
18756  */
18757 Roo.data.SortTypes = {
18758     /**
18759      * Default sort that does nothing
18760      * @param {Mixed} s The value being converted
18761      * @return {Mixed} The comparison value
18762      */
18763     none : function(s){
18764         return s;
18765     },
18766     
18767     /**
18768      * The regular expression used to strip tags
18769      * @type {RegExp}
18770      * @property
18771      */
18772     stripTagsRE : /<\/?[^>]+>/gi,
18773     
18774     /**
18775      * Strips all HTML tags to sort on text only
18776      * @param {Mixed} s The value being converted
18777      * @return {String} The comparison value
18778      */
18779     asText : function(s){
18780         return String(s).replace(this.stripTagsRE, "");
18781     },
18782     
18783     /**
18784      * Strips all HTML tags to sort on text only - Case insensitive
18785      * @param {Mixed} s The value being converted
18786      * @return {String} The comparison value
18787      */
18788     asUCText : function(s){
18789         return String(s).toUpperCase().replace(this.stripTagsRE, "");
18790     },
18791     
18792     /**
18793      * Case insensitive string
18794      * @param {Mixed} s The value being converted
18795      * @return {String} The comparison value
18796      */
18797     asUCString : function(s) {
18798         return String(s).toUpperCase();
18799     },
18800     
18801     /**
18802      * Date sorting
18803      * @param {Mixed} s The value being converted
18804      * @return {Number} The comparison value
18805      */
18806     asDate : function(s) {
18807         if(!s){
18808             return 0;
18809         }
18810         if(s instanceof Date){
18811             return s.getTime();
18812         }
18813         return Date.parse(String(s));
18814     },
18815     
18816     /**
18817      * Float sorting
18818      * @param {Mixed} s The value being converted
18819      * @return {Float} The comparison value
18820      */
18821     asFloat : function(s) {
18822         var val = parseFloat(String(s).replace(/,/g, ""));
18823         if(isNaN(val)) val = 0;
18824         return val;
18825     },
18826     
18827     /**
18828      * Integer sorting
18829      * @param {Mixed} s The value being converted
18830      * @return {Number} The comparison value
18831      */
18832     asInt : function(s) {
18833         var val = parseInt(String(s).replace(/,/g, ""));
18834         if(isNaN(val)) val = 0;
18835         return val;
18836     }
18837 };/*
18838  * Based on:
18839  * Ext JS Library 1.1.1
18840  * Copyright(c) 2006-2007, Ext JS, LLC.
18841  *
18842  * Originally Released Under LGPL - original licence link has changed is not relivant.
18843  *
18844  * Fork - LGPL
18845  * <script type="text/javascript">
18846  */
18847
18848 /**
18849 * @class Roo.data.Record
18850  * Instances of this class encapsulate both record <em>definition</em> information, and record
18851  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
18852  * to access Records cached in an {@link Roo.data.Store} object.<br>
18853  * <p>
18854  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
18855  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
18856  * objects.<br>
18857  * <p>
18858  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
18859  * @constructor
18860  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
18861  * {@link #create}. The parameters are the same.
18862  * @param {Array} data An associative Array of data values keyed by the field name.
18863  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
18864  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
18865  * not specified an integer id is generated.
18866  */
18867 Roo.data.Record = function(data, id){
18868     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
18869     this.data = data;
18870 };
18871
18872 /**
18873  * Generate a constructor for a specific record layout.
18874  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
18875  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
18876  * Each field definition object may contain the following properties: <ul>
18877  * <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,
18878  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
18879  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
18880  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
18881  * is being used, then this is a string containing the javascript expression to reference the data relative to 
18882  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
18883  * to the data item relative to the record element. If the mapping expression is the same as the field name,
18884  * this may be omitted.</p></li>
18885  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
18886  * <ul><li>auto (Default, implies no conversion)</li>
18887  * <li>string</li>
18888  * <li>int</li>
18889  * <li>float</li>
18890  * <li>boolean</li>
18891  * <li>date</li></ul></p></li>
18892  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
18893  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
18894  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
18895  * by the Reader into an object that will be stored in the Record. It is passed the
18896  * following parameters:<ul>
18897  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
18898  * </ul></p></li>
18899  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
18900  * </ul>
18901  * <br>usage:<br><pre><code>
18902 var TopicRecord = Roo.data.Record.create(
18903     {name: 'title', mapping: 'topic_title'},
18904     {name: 'author', mapping: 'username'},
18905     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
18906     {name: 'lastPost', mapping: 'post_time', type: 'date'},
18907     {name: 'lastPoster', mapping: 'user2'},
18908     {name: 'excerpt', mapping: 'post_text'}
18909 );
18910
18911 var myNewRecord = new TopicRecord({
18912     title: 'Do my job please',
18913     author: 'noobie',
18914     totalPosts: 1,
18915     lastPost: new Date(),
18916     lastPoster: 'Animal',
18917     excerpt: 'No way dude!'
18918 });
18919 myStore.add(myNewRecord);
18920 </code></pre>
18921  * @method create
18922  * @static
18923  */
18924 Roo.data.Record.create = function(o){
18925     var f = function(){
18926         f.superclass.constructor.apply(this, arguments);
18927     };
18928     Roo.extend(f, Roo.data.Record);
18929     var p = f.prototype;
18930     p.fields = new Roo.util.MixedCollection(false, function(field){
18931         return field.name;
18932     });
18933     for(var i = 0, len = o.length; i < len; i++){
18934         p.fields.add(new Roo.data.Field(o[i]));
18935     }
18936     f.getField = function(name){
18937         return p.fields.get(name);  
18938     };
18939     return f;
18940 };
18941
18942 Roo.data.Record.AUTO_ID = 1000;
18943 Roo.data.Record.EDIT = 'edit';
18944 Roo.data.Record.REJECT = 'reject';
18945 Roo.data.Record.COMMIT = 'commit';
18946
18947 Roo.data.Record.prototype = {
18948     /**
18949      * Readonly flag - true if this record has been modified.
18950      * @type Boolean
18951      */
18952     dirty : false,
18953     editing : false,
18954     error: null,
18955     modified: null,
18956
18957     // private
18958     join : function(store){
18959         this.store = store;
18960     },
18961
18962     /**
18963      * Set the named field to the specified value.
18964      * @param {String} name The name of the field to set.
18965      * @param {Object} value The value to set the field to.
18966      */
18967     set : function(name, value){
18968         if(this.data[name] == value){
18969             return;
18970         }
18971         this.dirty = true;
18972         if(!this.modified){
18973             this.modified = {};
18974         }
18975         if(typeof this.modified[name] == 'undefined'){
18976             this.modified[name] = this.data[name];
18977         }
18978         this.data[name] = value;
18979         if(!this.editing){
18980             this.store.afterEdit(this);
18981         }       
18982     },
18983
18984     /**
18985      * Get the value of the named field.
18986      * @param {String} name The name of the field to get the value of.
18987      * @return {Object} The value of the field.
18988      */
18989     get : function(name){
18990         return this.data[name]; 
18991     },
18992
18993     // private
18994     beginEdit : function(){
18995         this.editing = true;
18996         this.modified = {}; 
18997     },
18998
18999     // private
19000     cancelEdit : function(){
19001         this.editing = false;
19002         delete this.modified;
19003     },
19004
19005     // private
19006     endEdit : function(){
19007         this.editing = false;
19008         if(this.dirty && this.store){
19009             this.store.afterEdit(this);
19010         }
19011     },
19012
19013     /**
19014      * Usually called by the {@link Roo.data.Store} which owns the Record.
19015      * Rejects all changes made to the Record since either creation, or the last commit operation.
19016      * Modified fields are reverted to their original values.
19017      * <p>
19018      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19019      * of reject operations.
19020      */
19021     reject : function(){
19022         var m = this.modified;
19023         for(var n in m){
19024             if(typeof m[n] != "function"){
19025                 this.data[n] = m[n];
19026             }
19027         }
19028         this.dirty = false;
19029         delete this.modified;
19030         this.editing = false;
19031         if(this.store){
19032             this.store.afterReject(this);
19033         }
19034     },
19035
19036     /**
19037      * Usually called by the {@link Roo.data.Store} which owns the Record.
19038      * Commits all changes made to the Record since either creation, or the last commit operation.
19039      * <p>
19040      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
19041      * of commit operations.
19042      */
19043     commit : function(){
19044         this.dirty = false;
19045         delete this.modified;
19046         this.editing = false;
19047         if(this.store){
19048             this.store.afterCommit(this);
19049         }
19050     },
19051
19052     // private
19053     hasError : function(){
19054         return this.error != null;
19055     },
19056
19057     // private
19058     clearError : function(){
19059         this.error = null;
19060     },
19061
19062     /**
19063      * Creates a copy of this record.
19064      * @param {String} id (optional) A new record id if you don't want to use this record's id
19065      * @return {Record}
19066      */
19067     copy : function(newId) {
19068         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
19069     }
19070 };/*
19071  * Based on:
19072  * Ext JS Library 1.1.1
19073  * Copyright(c) 2006-2007, Ext JS, LLC.
19074  *
19075  * Originally Released Under LGPL - original licence link has changed is not relivant.
19076  *
19077  * Fork - LGPL
19078  * <script type="text/javascript">
19079  */
19080
19081
19082
19083 /**
19084  * @class Roo.data.Store
19085  * @extends Roo.util.Observable
19086  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
19087  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
19088  * <p>
19089  * 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
19090  * has no knowledge of the format of the data returned by the Proxy.<br>
19091  * <p>
19092  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
19093  * instances from the data object. These records are cached and made available through accessor functions.
19094  * @constructor
19095  * Creates a new Store.
19096  * @param {Object} config A config object containing the objects needed for the Store to access data,
19097  * and read the data into Records.
19098  */
19099 Roo.data.Store = function(config){
19100     this.data = new Roo.util.MixedCollection(false);
19101     this.data.getKey = function(o){
19102         return o.id;
19103     };
19104     this.baseParams = {};
19105     // private
19106     this.paramNames = {
19107         "start" : "start",
19108         "limit" : "limit",
19109         "sort" : "sort",
19110         "dir" : "dir"
19111     };
19112
19113     if(config && config.data){
19114         this.inlineData = config.data;
19115         delete config.data;
19116     }
19117
19118     Roo.apply(this, config);
19119     
19120     if(this.reader){ // reader passed
19121         this.reader = Roo.factory(this.reader, Roo.data);
19122         this.reader.xmodule = this.xmodule || false;
19123         if(!this.recordType){
19124             this.recordType = this.reader.recordType;
19125         }
19126         if(this.reader.onMetaChange){
19127             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
19128         }
19129     }
19130
19131     if(this.recordType){
19132         this.fields = this.recordType.prototype.fields;
19133     }
19134     this.modified = [];
19135
19136     this.addEvents({
19137         /**
19138          * @event datachanged
19139          * Fires when the data cache has changed, and a widget which is using this Store
19140          * as a Record cache should refresh its view.
19141          * @param {Store} this
19142          */
19143         datachanged : true,
19144         /**
19145          * @event metachange
19146          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
19147          * @param {Store} this
19148          * @param {Object} meta The JSON metadata
19149          */
19150         metachange : true,
19151         /**
19152          * @event add
19153          * Fires when Records have been added to the Store
19154          * @param {Store} this
19155          * @param {Roo.data.Record[]} records The array of Records added
19156          * @param {Number} index The index at which the record(s) were added
19157          */
19158         add : true,
19159         /**
19160          * @event remove
19161          * Fires when a Record has been removed from the Store
19162          * @param {Store} this
19163          * @param {Roo.data.Record} record The Record that was removed
19164          * @param {Number} index The index at which the record was removed
19165          */
19166         remove : true,
19167         /**
19168          * @event update
19169          * Fires when a Record has been updated
19170          * @param {Store} this
19171          * @param {Roo.data.Record} record The Record that was updated
19172          * @param {String} operation The update operation being performed.  Value may be one of:
19173          * <pre><code>
19174  Roo.data.Record.EDIT
19175  Roo.data.Record.REJECT
19176  Roo.data.Record.COMMIT
19177          * </code></pre>
19178          */
19179         update : true,
19180         /**
19181          * @event clear
19182          * Fires when the data cache has been cleared.
19183          * @param {Store} this
19184          */
19185         clear : true,
19186         /**
19187          * @event beforeload
19188          * Fires before a request is made for a new data object.  If the beforeload handler returns false
19189          * the load action will be canceled.
19190          * @param {Store} this
19191          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19192          */
19193         beforeload : true,
19194         /**
19195          * @event load
19196          * Fires after a new set of Records has been loaded.
19197          * @param {Store} this
19198          * @param {Roo.data.Record[]} records The Records that were loaded
19199          * @param {Object} options The loading options that were specified (see {@link #load} for details)
19200          */
19201         load : true,
19202         /**
19203          * @event loadexception
19204          * Fires if an exception occurs in the Proxy during loading.
19205          * Called with the signature of the Proxy's "loadexception" event.
19206          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
19207          * 
19208          * @param {Proxy} 
19209          * @param {Object} return from JsonData.reader() - success, totalRecords, records
19210          * @param {Object} load options 
19211          * @param {Object} jsonData from your request (normally this contains the Exception)
19212          */
19213         loadexception : true
19214     });
19215     
19216     if(this.proxy){
19217         this.proxy = Roo.factory(this.proxy, Roo.data);
19218         this.proxy.xmodule = this.xmodule || false;
19219         this.relayEvents(this.proxy,  ["loadexception"]);
19220     }
19221     this.sortToggle = {};
19222
19223     Roo.data.Store.superclass.constructor.call(this);
19224
19225     if(this.inlineData){
19226         this.loadData(this.inlineData);
19227         delete this.inlineData;
19228     }
19229 };
19230 Roo.extend(Roo.data.Store, Roo.util.Observable, {
19231      /**
19232     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
19233     * without a remote query - used by combo/forms at present.
19234     */
19235     
19236     /**
19237     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
19238     */
19239     /**
19240     * @cfg {Array} data Inline data to be loaded when the store is initialized.
19241     */
19242     /**
19243     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
19244     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
19245     */
19246     /**
19247     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
19248     * on any HTTP request
19249     */
19250     /**
19251     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
19252     */
19253     /**
19254     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
19255     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
19256     */
19257     remoteSort : false,
19258
19259     /**
19260     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
19261      * loaded or when a record is removed. (defaults to false).
19262     */
19263     pruneModifiedRecords : false,
19264
19265     // private
19266     lastOptions : null,
19267
19268     /**
19269      * Add Records to the Store and fires the add event.
19270      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19271      */
19272     add : function(records){
19273         records = [].concat(records);
19274         for(var i = 0, len = records.length; i < len; i++){
19275             records[i].join(this);
19276         }
19277         var index = this.data.length;
19278         this.data.addAll(records);
19279         this.fireEvent("add", this, records, index);
19280     },
19281
19282     /**
19283      * Remove a Record from the Store and fires the remove event.
19284      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
19285      */
19286     remove : function(record){
19287         var index = this.data.indexOf(record);
19288         this.data.removeAt(index);
19289         if(this.pruneModifiedRecords){
19290             this.modified.remove(record);
19291         }
19292         this.fireEvent("remove", this, record, index);
19293     },
19294
19295     /**
19296      * Remove all Records from the Store and fires the clear event.
19297      */
19298     removeAll : function(){
19299         this.data.clear();
19300         if(this.pruneModifiedRecords){
19301             this.modified = [];
19302         }
19303         this.fireEvent("clear", this);
19304     },
19305
19306     /**
19307      * Inserts Records to the Store at the given index and fires the add event.
19308      * @param {Number} index The start index at which to insert the passed Records.
19309      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
19310      */
19311     insert : function(index, records){
19312         records = [].concat(records);
19313         for(var i = 0, len = records.length; i < len; i++){
19314             this.data.insert(index, records[i]);
19315             records[i].join(this);
19316         }
19317         this.fireEvent("add", this, records, index);
19318     },
19319
19320     /**
19321      * Get the index within the cache of the passed Record.
19322      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
19323      * @return {Number} The index of the passed Record. Returns -1 if not found.
19324      */
19325     indexOf : function(record){
19326         return this.data.indexOf(record);
19327     },
19328
19329     /**
19330      * Get the index within the cache of the Record with the passed id.
19331      * @param {String} id The id of the Record to find.
19332      * @return {Number} The index of the Record. Returns -1 if not found.
19333      */
19334     indexOfId : function(id){
19335         return this.data.indexOfKey(id);
19336     },
19337
19338     /**
19339      * Get the Record with the specified id.
19340      * @param {String} id The id of the Record to find.
19341      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
19342      */
19343     getById : function(id){
19344         return this.data.key(id);
19345     },
19346
19347     /**
19348      * Get the Record at the specified index.
19349      * @param {Number} index The index of the Record to find.
19350      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
19351      */
19352     getAt : function(index){
19353         return this.data.itemAt(index);
19354     },
19355
19356     /**
19357      * Returns a range of Records between specified indices.
19358      * @param {Number} startIndex (optional) The starting index (defaults to 0)
19359      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
19360      * @return {Roo.data.Record[]} An array of Records
19361      */
19362     getRange : function(start, end){
19363         return this.data.getRange(start, end);
19364     },
19365
19366     // private
19367     storeOptions : function(o){
19368         o = Roo.apply({}, o);
19369         delete o.callback;
19370         delete o.scope;
19371         this.lastOptions = o;
19372     },
19373
19374     /**
19375      * Loads the Record cache from the configured Proxy using the configured Reader.
19376      * <p>
19377      * If using remote paging, then the first load call must specify the <em>start</em>
19378      * and <em>limit</em> properties in the options.params property to establish the initial
19379      * position within the dataset, and the number of Records to cache on each read from the Proxy.
19380      * <p>
19381      * <strong>It is important to note that for remote data sources, loading is asynchronous,
19382      * and this call will return before the new data has been loaded. Perform any post-processing
19383      * in a callback function, or in a "load" event handler.</strong>
19384      * <p>
19385      * @param {Object} options An object containing properties which control loading options:<ul>
19386      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
19387      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
19388      * passed the following arguments:<ul>
19389      * <li>r : Roo.data.Record[]</li>
19390      * <li>options: Options object from the load call</li>
19391      * <li>success: Boolean success indicator</li></ul></li>
19392      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
19393      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
19394      * </ul>
19395      */
19396     load : function(options){
19397         options = options || {};
19398         if(this.fireEvent("beforeload", this, options) !== false){
19399             this.storeOptions(options);
19400             var p = Roo.apply(options.params || {}, this.baseParams);
19401             // if meta was not loaded from remote source.. try requesting it.
19402             if (!this.reader.metaFromRemote) {
19403                 p._requestMeta = 1;
19404             }
19405             if(this.sortInfo && this.remoteSort){
19406                 var pn = this.paramNames;
19407                 p[pn["sort"]] = this.sortInfo.field;
19408                 p[pn["dir"]] = this.sortInfo.direction;
19409             }
19410             this.proxy.load(p, this.reader, this.loadRecords, this, options);
19411         }
19412     },
19413
19414     /**
19415      * Reloads the Record cache from the configured Proxy using the configured Reader and
19416      * the options from the last load operation performed.
19417      * @param {Object} options (optional) An object containing properties which may override the options
19418      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
19419      * the most recently used options are reused).
19420      */
19421     reload : function(options){
19422         this.load(Roo.applyIf(options||{}, this.lastOptions));
19423     },
19424
19425     // private
19426     // Called as a callback by the Reader during a load operation.
19427     loadRecords : function(o, options, success){
19428         if(!o || success === false){
19429             if(success !== false){
19430                 this.fireEvent("load", this, [], options);
19431             }
19432             if(options.callback){
19433                 options.callback.call(options.scope || this, [], options, false);
19434             }
19435             return;
19436         }
19437         // if data returned failure - throw an exception.
19438         if (o.success === false) {
19439             this.fireEvent("loadexception", this, o, options, this.reader.jsonData);
19440             return;
19441         }
19442         var r = o.records, t = o.totalRecords || r.length;
19443         if(!options || options.add !== true){
19444             if(this.pruneModifiedRecords){
19445                 this.modified = [];
19446             }
19447             for(var i = 0, len = r.length; i < len; i++){
19448                 r[i].join(this);
19449             }
19450             if(this.snapshot){
19451                 this.data = this.snapshot;
19452                 delete this.snapshot;
19453             }
19454             this.data.clear();
19455             this.data.addAll(r);
19456             this.totalLength = t;
19457             this.applySort();
19458             this.fireEvent("datachanged", this);
19459         }else{
19460             this.totalLength = Math.max(t, this.data.length+r.length);
19461             this.add(r);
19462         }
19463         this.fireEvent("load", this, r, options);
19464         if(options.callback){
19465             options.callback.call(options.scope || this, r, options, true);
19466         }
19467     },
19468
19469     /**
19470      * Loads data from a passed data block. A Reader which understands the format of the data
19471      * must have been configured in the constructor.
19472      * @param {Object} data The data block from which to read the Records.  The format of the data expected
19473      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
19474      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
19475      */
19476     loadData : function(o, append){
19477         var r = this.reader.readRecords(o);
19478         this.loadRecords(r, {add: append}, true);
19479     },
19480
19481     /**
19482      * Gets the number of cached records.
19483      * <p>
19484      * <em>If using paging, this may not be the total size of the dataset. If the data object
19485      * used by the Reader contains the dataset size, then the getTotalCount() function returns
19486      * the data set size</em>
19487      */
19488     getCount : function(){
19489         return this.data.length || 0;
19490     },
19491
19492     /**
19493      * Gets the total number of records in the dataset as returned by the server.
19494      * <p>
19495      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
19496      * the dataset size</em>
19497      */
19498     getTotalCount : function(){
19499         return this.totalLength || 0;
19500     },
19501
19502     /**
19503      * Returns the sort state of the Store as an object with two properties:
19504      * <pre><code>
19505  field {String} The name of the field by which the Records are sorted
19506  direction {String} The sort order, "ASC" or "DESC"
19507      * </code></pre>
19508      */
19509     getSortState : function(){
19510         return this.sortInfo;
19511     },
19512
19513     // private
19514     applySort : function(){
19515         if(this.sortInfo && !this.remoteSort){
19516             var s = this.sortInfo, f = s.field;
19517             var st = this.fields.get(f).sortType;
19518             var fn = function(r1, r2){
19519                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
19520                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
19521             };
19522             this.data.sort(s.direction, fn);
19523             if(this.snapshot && this.snapshot != this.data){
19524                 this.snapshot.sort(s.direction, fn);
19525             }
19526         }
19527     },
19528
19529     /**
19530      * Sets the default sort column and order to be used by the next load operation.
19531      * @param {String} fieldName The name of the field to sort by.
19532      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19533      */
19534     setDefaultSort : function(field, dir){
19535         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
19536     },
19537
19538     /**
19539      * Sort the Records.
19540      * If remote sorting is used, the sort is performed on the server, and the cache is
19541      * reloaded. If local sorting is used, the cache is sorted internally.
19542      * @param {String} fieldName The name of the field to sort by.
19543      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
19544      */
19545     sort : function(fieldName, dir){
19546         var f = this.fields.get(fieldName);
19547         if(!dir){
19548             if(this.sortInfo && this.sortInfo.field == f.name){ // toggle sort dir
19549                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
19550             }else{
19551                 dir = f.sortDir;
19552             }
19553         }
19554         this.sortToggle[f.name] = dir;
19555         this.sortInfo = {field: f.name, direction: dir};
19556         if(!this.remoteSort){
19557             this.applySort();
19558             this.fireEvent("datachanged", this);
19559         }else{
19560             this.load(this.lastOptions);
19561         }
19562     },
19563
19564     /**
19565      * Calls the specified function for each of the Records in the cache.
19566      * @param {Function} fn The function to call. The Record is passed as the first parameter.
19567      * Returning <em>false</em> aborts and exits the iteration.
19568      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
19569      */
19570     each : function(fn, scope){
19571         this.data.each(fn, scope);
19572     },
19573
19574     /**
19575      * Gets all records modified since the last commit.  Modified records are persisted across load operations
19576      * (e.g., during paging).
19577      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
19578      */
19579     getModifiedRecords : function(){
19580         return this.modified;
19581     },
19582
19583     // private
19584     createFilterFn : function(property, value, anyMatch){
19585         if(!value.exec){ // not a regex
19586             value = String(value);
19587             if(value.length == 0){
19588                 return false;
19589             }
19590             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
19591         }
19592         return function(r){
19593             return value.test(r.data[property]);
19594         };
19595     },
19596
19597     /**
19598      * Sums the value of <i>property</i> for each record between start and end and returns the result.
19599      * @param {String} property A field on your records
19600      * @param {Number} start The record index to start at (defaults to 0)
19601      * @param {Number} end The last record index to include (defaults to length - 1)
19602      * @return {Number} The sum
19603      */
19604     sum : function(property, start, end){
19605         var rs = this.data.items, v = 0;
19606         start = start || 0;
19607         end = (end || end === 0) ? end : rs.length-1;
19608
19609         for(var i = start; i <= end; i++){
19610             v += (rs[i].data[property] || 0);
19611         }
19612         return v;
19613     },
19614
19615     /**
19616      * Filter the records by a specified property.
19617      * @param {String} field A field on your records
19618      * @param {String/RegExp} value Either a string that the field
19619      * should start with or a RegExp to test against the field
19620      * @param {Boolean} anyMatch True to match any part not just the beginning
19621      */
19622     filter : function(property, value, anyMatch){
19623         var fn = this.createFilterFn(property, value, anyMatch);
19624         return fn ? this.filterBy(fn) : this.clearFilter();
19625     },
19626
19627     /**
19628      * Filter by a function. The specified function will be called with each
19629      * record in this data source. If the function returns true the record is included,
19630      * otherwise it is filtered.
19631      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19632      * @param {Object} scope (optional) The scope of the function (defaults to this)
19633      */
19634     filterBy : function(fn, scope){
19635         this.snapshot = this.snapshot || this.data;
19636         this.data = this.queryBy(fn, scope||this);
19637         this.fireEvent("datachanged", this);
19638     },
19639
19640     /**
19641      * Query the records by a specified property.
19642      * @param {String} field A field on your records
19643      * @param {String/RegExp} value Either a string that the field
19644      * should start with or a RegExp to test against the field
19645      * @param {Boolean} anyMatch True to match any part not just the beginning
19646      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19647      */
19648     query : function(property, value, anyMatch){
19649         var fn = this.createFilterFn(property, value, anyMatch);
19650         return fn ? this.queryBy(fn) : this.data.clone();
19651     },
19652
19653     /**
19654      * Query by a function. The specified function will be called with each
19655      * record in this data source. If the function returns true the record is included
19656      * in the results.
19657      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
19658      * @param {Object} scope (optional) The scope of the function (defaults to this)
19659       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
19660      **/
19661     queryBy : function(fn, scope){
19662         var data = this.snapshot || this.data;
19663         return data.filterBy(fn, scope||this);
19664     },
19665
19666     /**
19667      * Collects unique values for a particular dataIndex from this store.
19668      * @param {String} dataIndex The property to collect
19669      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
19670      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
19671      * @return {Array} An array of the unique values
19672      **/
19673     collect : function(dataIndex, allowNull, bypassFilter){
19674         var d = (bypassFilter === true && this.snapshot) ?
19675                 this.snapshot.items : this.data.items;
19676         var v, sv, r = [], l = {};
19677         for(var i = 0, len = d.length; i < len; i++){
19678             v = d[i].data[dataIndex];
19679             sv = String(v);
19680             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
19681                 l[sv] = true;
19682                 r[r.length] = v;
19683             }
19684         }
19685         return r;
19686     },
19687
19688     /**
19689      * Revert to a view of the Record cache with no filtering applied.
19690      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
19691      */
19692     clearFilter : function(suppressEvent){
19693         if(this.snapshot && this.snapshot != this.data){
19694             this.data = this.snapshot;
19695             delete this.snapshot;
19696             if(suppressEvent !== true){
19697                 this.fireEvent("datachanged", this);
19698             }
19699         }
19700     },
19701
19702     // private
19703     afterEdit : function(record){
19704         if(this.modified.indexOf(record) == -1){
19705             this.modified.push(record);
19706         }
19707         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
19708     },
19709
19710     // private
19711     afterReject : function(record){
19712         this.modified.remove(record);
19713         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
19714     },
19715
19716     // private
19717     afterCommit : function(record){
19718         this.modified.remove(record);
19719         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
19720     },
19721
19722     /**
19723      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
19724      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
19725      */
19726     commitChanges : function(){
19727         var m = this.modified.slice(0);
19728         this.modified = [];
19729         for(var i = 0, len = m.length; i < len; i++){
19730             m[i].commit();
19731         }
19732     },
19733
19734     /**
19735      * Cancel outstanding changes on all changed records.
19736      */
19737     rejectChanges : function(){
19738         var m = this.modified.slice(0);
19739         this.modified = [];
19740         for(var i = 0, len = m.length; i < len; i++){
19741             m[i].reject();
19742         }
19743     },
19744
19745     onMetaChange : function(meta, rtype, o){
19746         this.recordType = rtype;
19747         this.fields = rtype.prototype.fields;
19748         delete this.snapshot;
19749         this.sortInfo = meta.sortInfo || this.sortInfo;
19750         this.modified = [];
19751         this.fireEvent('metachange', this, this.reader.meta);
19752     }
19753 });/*
19754  * Based on:
19755  * Ext JS Library 1.1.1
19756  * Copyright(c) 2006-2007, Ext JS, LLC.
19757  *
19758  * Originally Released Under LGPL - original licence link has changed is not relivant.
19759  *
19760  * Fork - LGPL
19761  * <script type="text/javascript">
19762  */
19763
19764 /**
19765  * @class Roo.data.SimpleStore
19766  * @extends Roo.data.Store
19767  * Small helper class to make creating Stores from Array data easier.
19768  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
19769  * @cfg {Array} fields An array of field definition objects, or field name strings.
19770  * @cfg {Array} data The multi-dimensional array of data
19771  * @constructor
19772  * @param {Object} config
19773  */
19774 Roo.data.SimpleStore = function(config){
19775     Roo.data.SimpleStore.superclass.constructor.call(this, {
19776         isLocal : true,
19777         reader: new Roo.data.ArrayReader({
19778                 id: config.id
19779             },
19780             Roo.data.Record.create(config.fields)
19781         ),
19782         proxy : new Roo.data.MemoryProxy(config.data)
19783     });
19784     this.load();
19785 };
19786 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
19787  * Based on:
19788  * Ext JS Library 1.1.1
19789  * Copyright(c) 2006-2007, Ext JS, LLC.
19790  *
19791  * Originally Released Under LGPL - original licence link has changed is not relivant.
19792  *
19793  * Fork - LGPL
19794  * <script type="text/javascript">
19795  */
19796
19797 /**
19798 /**
19799  * @extends Roo.data.Store
19800  * @class Roo.data.JsonStore
19801  * Small helper class to make creating Stores for JSON data easier. <br/>
19802 <pre><code>
19803 var store = new Roo.data.JsonStore({
19804     url: 'get-images.php',
19805     root: 'images',
19806     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
19807 });
19808 </code></pre>
19809  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
19810  * JsonReader and HttpProxy (unless inline data is provided).</b>
19811  * @cfg {Array} fields An array of field definition objects, or field name strings.
19812  * @constructor
19813  * @param {Object} config
19814  */
19815 Roo.data.JsonStore = function(c){
19816     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
19817         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
19818         reader: new Roo.data.JsonReader(c, c.fields)
19819     }));
19820 };
19821 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
19822  * Based on:
19823  * Ext JS Library 1.1.1
19824  * Copyright(c) 2006-2007, Ext JS, LLC.
19825  *
19826  * Originally Released Under LGPL - original licence link has changed is not relivant.
19827  *
19828  * Fork - LGPL
19829  * <script type="text/javascript">
19830  */
19831
19832  
19833 Roo.data.Field = function(config){
19834     if(typeof config == "string"){
19835         config = {name: config};
19836     }
19837     Roo.apply(this, config);
19838     
19839     if(!this.type){
19840         this.type = "auto";
19841     }
19842     
19843     var st = Roo.data.SortTypes;
19844     // named sortTypes are supported, here we look them up
19845     if(typeof this.sortType == "string"){
19846         this.sortType = st[this.sortType];
19847     }
19848     
19849     // set default sortType for strings and dates
19850     if(!this.sortType){
19851         switch(this.type){
19852             case "string":
19853                 this.sortType = st.asUCString;
19854                 break;
19855             case "date":
19856                 this.sortType = st.asDate;
19857                 break;
19858             default:
19859                 this.sortType = st.none;
19860         }
19861     }
19862
19863     // define once
19864     var stripRe = /[\$,%]/g;
19865
19866     // prebuilt conversion function for this field, instead of
19867     // switching every time we're reading a value
19868     if(!this.convert){
19869         var cv, dateFormat = this.dateFormat;
19870         switch(this.type){
19871             case "":
19872             case "auto":
19873             case undefined:
19874                 cv = function(v){ return v; };
19875                 break;
19876             case "string":
19877                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
19878                 break;
19879             case "int":
19880                 cv = function(v){
19881                     return v !== undefined && v !== null && v !== '' ?
19882                            parseInt(String(v).replace(stripRe, ""), 10) : '';
19883                     };
19884                 break;
19885             case "float":
19886                 cv = function(v){
19887                     return v !== undefined && v !== null && v !== '' ?
19888                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
19889                     };
19890                 break;
19891             case "bool":
19892             case "boolean":
19893                 cv = function(v){ return v === true || v === "true" || v == 1; };
19894                 break;
19895             case "date":
19896                 cv = function(v){
19897                     if(!v){
19898                         return '';
19899                     }
19900                     if(v instanceof Date){
19901                         return v;
19902                     }
19903                     if(dateFormat){
19904                         if(dateFormat == "timestamp"){
19905                             return new Date(v*1000);
19906                         }
19907                         return Date.parseDate(v, dateFormat);
19908                     }
19909                     var parsed = Date.parse(v);
19910                     return parsed ? new Date(parsed) : null;
19911                 };
19912              break;
19913             
19914         }
19915         this.convert = cv;
19916     }
19917 };
19918
19919 Roo.data.Field.prototype = {
19920     dateFormat: null,
19921     defaultValue: "",
19922     mapping: null,
19923     sortType : null,
19924     sortDir : "ASC"
19925 };/*
19926  * Based on:
19927  * Ext JS Library 1.1.1
19928  * Copyright(c) 2006-2007, Ext JS, LLC.
19929  *
19930  * Originally Released Under LGPL - original licence link has changed is not relivant.
19931  *
19932  * Fork - LGPL
19933  * <script type="text/javascript">
19934  */
19935  
19936 // Base class for reading structured data from a data source.  This class is intended to be
19937 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
19938
19939 /**
19940  * @class Roo.data.DataReader
19941  * Base class for reading structured data from a data source.  This class is intended to be
19942  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
19943  */
19944
19945 Roo.data.DataReader = function(meta, recordType){
19946     
19947     this.meta = meta;
19948     
19949     this.recordType = recordType instanceof Array ? 
19950         Roo.data.Record.create(recordType) : recordType;
19951 };
19952
19953 Roo.data.DataReader.prototype = {
19954      /**
19955      * Create an empty record
19956      * @param {Object} data (optional) - overlay some values
19957      * @return {Roo.data.Record} record created.
19958      */
19959     newRow :  function(d) {
19960         var da =  {};
19961         this.recordType.prototype.fields.each(function(c) {
19962             switch( c.type) {
19963                 case 'int' : da[c.name] = 0; break;
19964                 case 'date' : da[c.name] = new Date(); break;
19965                 case 'float' : da[c.name] = 0.0; break;
19966                 case 'boolean' : da[c.name] = false; break;
19967                 default : da[c.name] = ""; break;
19968             }
19969             
19970         });
19971         return new this.recordType(Roo.apply(da, d));
19972     }
19973     
19974 };/*
19975  * Based on:
19976  * Ext JS Library 1.1.1
19977  * Copyright(c) 2006-2007, Ext JS, LLC.
19978  *
19979  * Originally Released Under LGPL - original licence link has changed is not relivant.
19980  *
19981  * Fork - LGPL
19982  * <script type="text/javascript">
19983  */
19984
19985 /**
19986  * @class Roo.data.DataProxy
19987  * @extends Roo.data.Observable
19988  * This class is an abstract base class for implementations which provide retrieval of
19989  * unformatted data objects.<br>
19990  * <p>
19991  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
19992  * (of the appropriate type which knows how to parse the data object) to provide a block of
19993  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
19994  * <p>
19995  * Custom implementations must implement the load method as described in
19996  * {@link Roo.data.HttpProxy#load}.
19997  */
19998 Roo.data.DataProxy = function(){
19999     this.addEvents({
20000         /**
20001          * @event beforeload
20002          * Fires before a network request is made to retrieve a data object.
20003          * @param {Object} This DataProxy object.
20004          * @param {Object} params The params parameter to the load function.
20005          */
20006         beforeload : true,
20007         /**
20008          * @event load
20009          * Fires before the load method's callback is called.
20010          * @param {Object} This DataProxy object.
20011          * @param {Object} o The data object.
20012          * @param {Object} arg The callback argument object passed to the load function.
20013          */
20014         load : true,
20015         /**
20016          * @event loadexception
20017          * Fires if an Exception occurs during data retrieval.
20018          * @param {Object} This DataProxy object.
20019          * @param {Object} o The data object.
20020          * @param {Object} arg The callback argument object passed to the load function.
20021          * @param {Object} e The Exception.
20022          */
20023         loadexception : true
20024     });
20025     Roo.data.DataProxy.superclass.constructor.call(this);
20026 };
20027
20028 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
20029
20030     /**
20031      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
20032      */
20033 /*
20034  * Based on:
20035  * Ext JS Library 1.1.1
20036  * Copyright(c) 2006-2007, Ext JS, LLC.
20037  *
20038  * Originally Released Under LGPL - original licence link has changed is not relivant.
20039  *
20040  * Fork - LGPL
20041  * <script type="text/javascript">
20042  */
20043 /**
20044  * @class Roo.data.MemoryProxy
20045  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
20046  * to the Reader when its load method is called.
20047  * @constructor
20048  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
20049  */
20050 Roo.data.MemoryProxy = function(data){
20051     if (data.data) {
20052         data = data.data;
20053     }
20054     Roo.data.MemoryProxy.superclass.constructor.call(this);
20055     this.data = data;
20056 };
20057
20058 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
20059     /**
20060      * Load data from the requested source (in this case an in-memory
20061      * data object passed to the constructor), read the data object into
20062      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20063      * process that block using the passed callback.
20064      * @param {Object} params This parameter is not used by the MemoryProxy class.
20065      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20066      * object into a block of Roo.data.Records.
20067      * @param {Function} callback The function into which to pass the block of Roo.data.records.
20068      * The function must be passed <ul>
20069      * <li>The Record block object</li>
20070      * <li>The "arg" argument from the load function</li>
20071      * <li>A boolean success indicator</li>
20072      * </ul>
20073      * @param {Object} scope The scope in which to call the callback
20074      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20075      */
20076     load : function(params, reader, callback, scope, arg){
20077         params = params || {};
20078         var result;
20079         try {
20080             result = reader.readRecords(this.data);
20081         }catch(e){
20082             this.fireEvent("loadexception", this, arg, null, e);
20083             callback.call(scope, null, arg, false);
20084             return;
20085         }
20086         callback.call(scope, result, arg, true);
20087     },
20088     
20089     // private
20090     update : function(params, records){
20091         
20092     }
20093 });/*
20094  * Based on:
20095  * Ext JS Library 1.1.1
20096  * Copyright(c) 2006-2007, Ext JS, LLC.
20097  *
20098  * Originally Released Under LGPL - original licence link has changed is not relivant.
20099  *
20100  * Fork - LGPL
20101  * <script type="text/javascript">
20102  */
20103 /**
20104  * @class Roo.data.HttpProxy
20105  * @extends Roo.data.DataProxy
20106  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
20107  * configured to reference a certain URL.<br><br>
20108  * <p>
20109  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
20110  * from which the running page was served.<br><br>
20111  * <p>
20112  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
20113  * <p>
20114  * Be aware that to enable the browser to parse an XML document, the server must set
20115  * the Content-Type header in the HTTP response to "text/xml".
20116  * @constructor
20117  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
20118  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
20119  * will be used to make the request.
20120  */
20121 Roo.data.HttpProxy = function(conn){
20122     Roo.data.HttpProxy.superclass.constructor.call(this);
20123     // is conn a conn config or a real conn?
20124     this.conn = conn;
20125     this.useAjax = !conn || !conn.events;
20126   
20127 };
20128
20129 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
20130     // thse are take from connection...
20131     
20132     /**
20133      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
20134      */
20135     /**
20136      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
20137      * extra parameters to each request made by this object. (defaults to undefined)
20138      */
20139     /**
20140      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
20141      *  to each request made by this object. (defaults to undefined)
20142      */
20143     /**
20144      * @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)
20145      */
20146     /**
20147      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
20148      */
20149      /**
20150      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
20151      * @type Boolean
20152      */
20153   
20154
20155     /**
20156      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
20157      * @type Boolean
20158      */
20159     /**
20160      * Return the {@link Roo.data.Connection} object being used by this Proxy.
20161      * @return {Connection} The Connection object. This object may be used to subscribe to events on
20162      * a finer-grained basis than the DataProxy events.
20163      */
20164     getConnection : function(){
20165         return this.useAjax ? Roo.Ajax : this.conn;
20166     },
20167
20168     /**
20169      * Load data from the configured {@link Roo.data.Connection}, read the data object into
20170      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
20171      * process that block using the passed callback.
20172      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20173      * for the request to the remote server.
20174      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20175      * object into a block of Roo.data.Records.
20176      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20177      * The function must be passed <ul>
20178      * <li>The Record block object</li>
20179      * <li>The "arg" argument from the load function</li>
20180      * <li>A boolean success indicator</li>
20181      * </ul>
20182      * @param {Object} scope The scope in which to call the callback
20183      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20184      */
20185     load : function(params, reader, callback, scope, arg){
20186         if(this.fireEvent("beforeload", this, params) !== false){
20187             var  o = {
20188                 params : params || {},
20189                 request: {
20190                     callback : callback,
20191                     scope : scope,
20192                     arg : arg
20193                 },
20194                 reader: reader,
20195                 callback : this.loadResponse,
20196                 scope: this
20197             };
20198             if(this.useAjax){
20199                 Roo.applyIf(o, this.conn);
20200                 if(this.activeRequest){
20201                     Roo.Ajax.abort(this.activeRequest);
20202                 }
20203                 this.activeRequest = Roo.Ajax.request(o);
20204             }else{
20205                 this.conn.request(o);
20206             }
20207         }else{
20208             callback.call(scope||this, null, arg, false);
20209         }
20210     },
20211
20212     // private
20213     loadResponse : function(o, success, response){
20214         delete this.activeRequest;
20215         if(!success){
20216             this.fireEvent("loadexception", this, o, response);
20217             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20218             return;
20219         }
20220         var result;
20221         try {
20222             result = o.reader.read(response);
20223         }catch(e){
20224             this.fireEvent("loadexception", this, o, response, e);
20225             o.request.callback.call(o.request.scope, null, o.request.arg, false);
20226             return;
20227         }
20228         
20229         this.fireEvent("load", this, o, o.request.arg);
20230         o.request.callback.call(o.request.scope, result, o.request.arg, true);
20231     },
20232
20233     // private
20234     update : function(dataSet){
20235
20236     },
20237
20238     // private
20239     updateResponse : function(dataSet){
20240
20241     }
20242 });/*
20243  * Based on:
20244  * Ext JS Library 1.1.1
20245  * Copyright(c) 2006-2007, Ext JS, LLC.
20246  *
20247  * Originally Released Under LGPL - original licence link has changed is not relivant.
20248  *
20249  * Fork - LGPL
20250  * <script type="text/javascript">
20251  */
20252
20253 /**
20254  * @class Roo.data.ScriptTagProxy
20255  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
20256  * other than the originating domain of the running page.<br><br>
20257  * <p>
20258  * <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
20259  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
20260  * <p>
20261  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
20262  * source code that is used as the source inside a &lt;script> tag.<br><br>
20263  * <p>
20264  * In order for the browser to process the returned data, the server must wrap the data object
20265  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
20266  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
20267  * depending on whether the callback name was passed:
20268  * <p>
20269  * <pre><code>
20270 boolean scriptTag = false;
20271 String cb = request.getParameter("callback");
20272 if (cb != null) {
20273     scriptTag = true;
20274     response.setContentType("text/javascript");
20275 } else {
20276     response.setContentType("application/x-json");
20277 }
20278 Writer out = response.getWriter();
20279 if (scriptTag) {
20280     out.write(cb + "(");
20281 }
20282 out.print(dataBlock.toJsonString());
20283 if (scriptTag) {
20284     out.write(");");
20285 }
20286 </pre></code>
20287  *
20288  * @constructor
20289  * @param {Object} config A configuration object.
20290  */
20291 Roo.data.ScriptTagProxy = function(config){
20292     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
20293     Roo.apply(this, config);
20294     this.head = document.getElementsByTagName("head")[0];
20295 };
20296
20297 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
20298
20299 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
20300     /**
20301      * @cfg {String} url The URL from which to request the data object.
20302      */
20303     /**
20304      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
20305      */
20306     timeout : 30000,
20307     /**
20308      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
20309      * the server the name of the callback function set up by the load call to process the returned data object.
20310      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
20311      * javascript output which calls this named function passing the data object as its only parameter.
20312      */
20313     callbackParam : "callback",
20314     /**
20315      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
20316      * name to the request.
20317      */
20318     nocache : true,
20319
20320     /**
20321      * Load data from the configured URL, read the data object into
20322      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
20323      * process that block using the passed callback.
20324      * @param {Object} params An object containing properties which are to be used as HTTP parameters
20325      * for the request to the remote server.
20326      * @param {Roo.data.DataReader} reader The Reader object which converts the data
20327      * object into a block of Roo.data.Records.
20328      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
20329      * The function must be passed <ul>
20330      * <li>The Record block object</li>
20331      * <li>The "arg" argument from the load function</li>
20332      * <li>A boolean success indicator</li>
20333      * </ul>
20334      * @param {Object} scope The scope in which to call the callback
20335      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
20336      */
20337     load : function(params, reader, callback, scope, arg){
20338         if(this.fireEvent("beforeload", this, params) !== false){
20339
20340             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
20341
20342             var url = this.url;
20343             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
20344             if(this.nocache){
20345                 url += "&_dc=" + (new Date().getTime());
20346             }
20347             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
20348             var trans = {
20349                 id : transId,
20350                 cb : "stcCallback"+transId,
20351                 scriptId : "stcScript"+transId,
20352                 params : params,
20353                 arg : arg,
20354                 url : url,
20355                 callback : callback,
20356                 scope : scope,
20357                 reader : reader
20358             };
20359             var conn = this;
20360
20361             window[trans.cb] = function(o){
20362                 conn.handleResponse(o, trans);
20363             };
20364
20365             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
20366
20367             if(this.autoAbort !== false){
20368                 this.abort();
20369             }
20370
20371             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
20372
20373             var script = document.createElement("script");
20374             script.setAttribute("src", url);
20375             script.setAttribute("type", "text/javascript");
20376             script.setAttribute("id", trans.scriptId);
20377             this.head.appendChild(script);
20378
20379             this.trans = trans;
20380         }else{
20381             callback.call(scope||this, null, arg, false);
20382         }
20383     },
20384
20385     // private
20386     isLoading : function(){
20387         return this.trans ? true : false;
20388     },
20389
20390     /**
20391      * Abort the current server request.
20392      */
20393     abort : function(){
20394         if(this.isLoading()){
20395             this.destroyTrans(this.trans);
20396         }
20397     },
20398
20399     // private
20400     destroyTrans : function(trans, isLoaded){
20401         this.head.removeChild(document.getElementById(trans.scriptId));
20402         clearTimeout(trans.timeoutId);
20403         if(isLoaded){
20404             window[trans.cb] = undefined;
20405             try{
20406                 delete window[trans.cb];
20407             }catch(e){}
20408         }else{
20409             // if hasn't been loaded, wait for load to remove it to prevent script error
20410             window[trans.cb] = function(){
20411                 window[trans.cb] = undefined;
20412                 try{
20413                     delete window[trans.cb];
20414                 }catch(e){}
20415             };
20416         }
20417     },
20418
20419     // private
20420     handleResponse : function(o, trans){
20421         this.trans = false;
20422         this.destroyTrans(trans, true);
20423         var result;
20424         try {
20425             result = trans.reader.readRecords(o);
20426         }catch(e){
20427             this.fireEvent("loadexception", this, o, trans.arg, e);
20428             trans.callback.call(trans.scope||window, null, trans.arg, false);
20429             return;
20430         }
20431         this.fireEvent("load", this, o, trans.arg);
20432         trans.callback.call(trans.scope||window, result, trans.arg, true);
20433     },
20434
20435     // private
20436     handleFailure : function(trans){
20437         this.trans = false;
20438         this.destroyTrans(trans, false);
20439         this.fireEvent("loadexception", this, null, trans.arg);
20440         trans.callback.call(trans.scope||window, null, trans.arg, false);
20441     }
20442 });/*
20443  * Based on:
20444  * Ext JS Library 1.1.1
20445  * Copyright(c) 2006-2007, Ext JS, LLC.
20446  *
20447  * Originally Released Under LGPL - original licence link has changed is not relivant.
20448  *
20449  * Fork - LGPL
20450  * <script type="text/javascript">
20451  */
20452
20453 /**
20454  * @class Roo.data.JsonReader
20455  * @extends Roo.data.DataReader
20456  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
20457  * based on mappings in a provided Roo.data.Record constructor.
20458  * 
20459  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
20460  * in the reply previously. 
20461  * 
20462  * <p>
20463  * Example code:
20464  * <pre><code>
20465 var RecordDef = Roo.data.Record.create([
20466     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20467     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20468 ]);
20469 var myReader = new Roo.data.JsonReader({
20470     totalProperty: "results",    // The property which contains the total dataset size (optional)
20471     root: "rows",                // The property which contains an Array of row objects
20472     id: "id"                     // The property within each row object that provides an ID for the record (optional)
20473 }, RecordDef);
20474 </code></pre>
20475  * <p>
20476  * This would consume a JSON file like this:
20477  * <pre><code>
20478 { 'results': 2, 'rows': [
20479     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
20480     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
20481 }
20482 </code></pre>
20483  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
20484  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20485  * paged from the remote server.
20486  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
20487  * @cfg {String} root name of the property which contains the Array of row objects.
20488  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
20489  * @constructor
20490  * Create a new JsonReader
20491  * @param {Object} meta Metadata configuration options
20492  * @param {Object} recordType Either an Array of field definition objects,
20493  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
20494  */
20495 Roo.data.JsonReader = function(meta, recordType){
20496     
20497     meta = meta || {};
20498     // set some defaults:
20499     Roo.applyIf(meta, {
20500         totalProperty: 'total',
20501         successProperty : 'success',
20502         root : 'data',
20503         id : 'id'
20504     });
20505     
20506     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20507 };
20508 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
20509     
20510     /**
20511      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
20512      * Used by Store query builder to append _requestMeta to params.
20513      * 
20514      */
20515     metaFromRemote : false,
20516     /**
20517      * This method is only used by a DataProxy which has retrieved data from a remote server.
20518      * @param {Object} response The XHR object which contains the JSON data in its responseText.
20519      * @return {Object} data A data block which is used by an Roo.data.Store object as
20520      * a cache of Roo.data.Records.
20521      */
20522     read : function(response){
20523         var json = response.responseText;
20524        
20525         var o = /* eval:var:o */ eval("("+json+")");
20526         if(!o) {
20527             throw {message: "JsonReader.read: Json object not found"};
20528         }
20529         
20530         if(o.metaData){
20531             
20532             delete this.ef;
20533             this.metaFromRemote = true;
20534             this.meta = o.metaData;
20535             this.recordType = Roo.data.Record.create(o.metaData.fields);
20536             this.onMetaChange(this.meta, this.recordType, o);
20537         }
20538         return this.readRecords(o);
20539     },
20540
20541     // private function a store will implement
20542     onMetaChange : function(meta, recordType, o){
20543
20544     },
20545
20546     /**
20547          * @ignore
20548          */
20549     simpleAccess: function(obj, subsc) {
20550         return obj[subsc];
20551     },
20552
20553         /**
20554          * @ignore
20555          */
20556     getJsonAccessor: function(){
20557         var re = /[\[\.]/;
20558         return function(expr) {
20559             try {
20560                 return(re.test(expr))
20561                     ? new Function("obj", "return obj." + expr)
20562                     : function(obj){
20563                         return obj[expr];
20564                     };
20565             } catch(e){}
20566             return Roo.emptyFn;
20567         };
20568     }(),
20569
20570     /**
20571      * Create a data block containing Roo.data.Records from an XML document.
20572      * @param {Object} o An object which contains an Array of row objects in the property specified
20573      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
20574      * which contains the total size of the dataset.
20575      * @return {Object} data A data block which is used by an Roo.data.Store object as
20576      * a cache of Roo.data.Records.
20577      */
20578     readRecords : function(o){
20579         /**
20580          * After any data loads, the raw JSON data is available for further custom processing.
20581          * @type Object
20582          */
20583         this.jsonData = o;
20584         var s = this.meta, Record = this.recordType,
20585             f = Record.prototype.fields, fi = f.items, fl = f.length;
20586
20587 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
20588         if (!this.ef) {
20589             if(s.totalProperty) {
20590                     this.getTotal = this.getJsonAccessor(s.totalProperty);
20591                 }
20592                 if(s.successProperty) {
20593                     this.getSuccess = this.getJsonAccessor(s.successProperty);
20594                 }
20595                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
20596                 if (s.id) {
20597                         var g = this.getJsonAccessor(s.id);
20598                         this.getId = function(rec) {
20599                                 var r = g(rec);
20600                                 return (r === undefined || r === "") ? null : r;
20601                         };
20602                 } else {
20603                         this.getId = function(){return null;};
20604                 }
20605             this.ef = [];
20606             for(var jj = 0; jj < fl; jj++){
20607                 f = fi[jj];
20608                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
20609                 this.ef[jj] = this.getJsonAccessor(map);
20610             }
20611         }
20612
20613         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
20614         if(s.totalProperty){
20615             var vt = parseInt(this.getTotal(o), 10);
20616             if(!isNaN(vt)){
20617                 totalRecords = vt;
20618             }
20619         }
20620         if(s.successProperty){
20621             var vs = this.getSuccess(o);
20622             if(vs === false || vs === 'false'){
20623                 success = false;
20624             }
20625         }
20626         var records = [];
20627             for(var i = 0; i < c; i++){
20628                     var n = root[i];
20629                 var values = {};
20630                 var id = this.getId(n);
20631                 for(var j = 0; j < fl; j++){
20632                     f = fi[j];
20633                 var v = this.ef[j](n);
20634                 if (!f.convert) {
20635                     Roo.log('missing convert for ' + f.name);
20636                     Roo.log(f);
20637                     continue;
20638                 }
20639                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
20640                 }
20641                 var record = new Record(values, id);
20642                 record.json = n;
20643                 records[i] = record;
20644             }
20645             return {
20646                 success : success,
20647                 records : records,
20648                 totalRecords : totalRecords
20649             };
20650     }
20651 });/*
20652  * Based on:
20653  * Ext JS Library 1.1.1
20654  * Copyright(c) 2006-2007, Ext JS, LLC.
20655  *
20656  * Originally Released Under LGPL - original licence link has changed is not relivant.
20657  *
20658  * Fork - LGPL
20659  * <script type="text/javascript">
20660  */
20661
20662 /**
20663  * @class Roo.data.XmlReader
20664  * @extends Roo.data.DataReader
20665  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
20666  * based on mappings in a provided Roo.data.Record constructor.<br><br>
20667  * <p>
20668  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
20669  * header in the HTTP response must be set to "text/xml".</em>
20670  * <p>
20671  * Example code:
20672  * <pre><code>
20673 var RecordDef = Roo.data.Record.create([
20674    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
20675    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
20676 ]);
20677 var myReader = new Roo.data.XmlReader({
20678    totalRecords: "results", // The element which contains the total dataset size (optional)
20679    record: "row",           // The repeated element which contains row information
20680    id: "id"                 // The element within the row that provides an ID for the record (optional)
20681 }, RecordDef);
20682 </code></pre>
20683  * <p>
20684  * This would consume an XML file like this:
20685  * <pre><code>
20686 &lt;?xml?>
20687 &lt;dataset>
20688  &lt;results>2&lt;/results>
20689  &lt;row>
20690    &lt;id>1&lt;/id>
20691    &lt;name>Bill&lt;/name>
20692    &lt;occupation>Gardener&lt;/occupation>
20693  &lt;/row>
20694  &lt;row>
20695    &lt;id>2&lt;/id>
20696    &lt;name>Ben&lt;/name>
20697    &lt;occupation>Horticulturalist&lt;/occupation>
20698  &lt;/row>
20699 &lt;/dataset>
20700 </code></pre>
20701  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
20702  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
20703  * paged from the remote server.
20704  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
20705  * @cfg {String} success The DomQuery path to the success attribute used by forms.
20706  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
20707  * a record identifier value.
20708  * @constructor
20709  * Create a new XmlReader
20710  * @param {Object} meta Metadata configuration options
20711  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
20712  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
20713  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
20714  */
20715 Roo.data.XmlReader = function(meta, recordType){
20716     meta = meta || {};
20717     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
20718 };
20719 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
20720     /**
20721      * This method is only used by a DataProxy which has retrieved data from a remote server.
20722          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
20723          * to contain a method called 'responseXML' that returns an XML document object.
20724      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20725      * a cache of Roo.data.Records.
20726      */
20727     read : function(response){
20728         var doc = response.responseXML;
20729         if(!doc) {
20730             throw {message: "XmlReader.read: XML Document not available"};
20731         }
20732         return this.readRecords(doc);
20733     },
20734
20735     /**
20736      * Create a data block containing Roo.data.Records from an XML document.
20737          * @param {Object} doc A parsed XML document.
20738      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
20739      * a cache of Roo.data.Records.
20740      */
20741     readRecords : function(doc){
20742         /**
20743          * After any data loads/reads, the raw XML Document is available for further custom processing.
20744          * @type XMLDocument
20745          */
20746         this.xmlData = doc;
20747         var root = doc.documentElement || doc;
20748         var q = Roo.DomQuery;
20749         var recordType = this.recordType, fields = recordType.prototype.fields;
20750         var sid = this.meta.id;
20751         var totalRecords = 0, success = true;
20752         if(this.meta.totalRecords){
20753             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
20754         }
20755         
20756         if(this.meta.success){
20757             var sv = q.selectValue(this.meta.success, root, true);
20758             success = sv !== false && sv !== 'false';
20759         }
20760         var records = [];
20761         var ns = q.select(this.meta.record, root);
20762         for(var i = 0, len = ns.length; i < len; i++) {
20763                 var n = ns[i];
20764                 var values = {};
20765                 var id = sid ? q.selectValue(sid, n) : undefined;
20766                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20767                     var f = fields.items[j];
20768                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
20769                     v = f.convert(v);
20770                     values[f.name] = v;
20771                 }
20772                 var record = new recordType(values, id);
20773                 record.node = n;
20774                 records[records.length] = record;
20775             }
20776
20777             return {
20778                 success : success,
20779                 records : records,
20780                 totalRecords : totalRecords || records.length
20781             };
20782     }
20783 });/*
20784  * Based on:
20785  * Ext JS Library 1.1.1
20786  * Copyright(c) 2006-2007, Ext JS, LLC.
20787  *
20788  * Originally Released Under LGPL - original licence link has changed is not relivant.
20789  *
20790  * Fork - LGPL
20791  * <script type="text/javascript">
20792  */
20793
20794 /**
20795  * @class Roo.data.ArrayReader
20796  * @extends Roo.data.DataReader
20797  * Data reader class to create an Array of Roo.data.Record objects from an Array.
20798  * Each element of that Array represents a row of data fields. The
20799  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
20800  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
20801  * <p>
20802  * Example code:.
20803  * <pre><code>
20804 var RecordDef = Roo.data.Record.create([
20805     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
20806     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
20807 ]);
20808 var myReader = new Roo.data.ArrayReader({
20809     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
20810 }, RecordDef);
20811 </code></pre>
20812  * <p>
20813  * This would consume an Array like this:
20814  * <pre><code>
20815 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
20816   </code></pre>
20817  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
20818  * @constructor
20819  * Create a new JsonReader
20820  * @param {Object} meta Metadata configuration options.
20821  * @param {Object} recordType Either an Array of field definition objects
20822  * as specified to {@link Roo.data.Record#create},
20823  * or an {@link Roo.data.Record} object
20824  * created using {@link Roo.data.Record#create}.
20825  */
20826 Roo.data.ArrayReader = function(meta, recordType){
20827     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
20828 };
20829
20830 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
20831     /**
20832      * Create a data block containing Roo.data.Records from an XML document.
20833      * @param {Object} o An Array of row objects which represents the dataset.
20834      * @return {Object} data A data block which is used by an Roo.data.Store object as
20835      * a cache of Roo.data.Records.
20836      */
20837     readRecords : function(o){
20838         var sid = this.meta ? this.meta.id : null;
20839         var recordType = this.recordType, fields = recordType.prototype.fields;
20840         var records = [];
20841         var root = o;
20842             for(var i = 0; i < root.length; i++){
20843                     var n = root[i];
20844                 var values = {};
20845                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
20846                 for(var j = 0, jlen = fields.length; j < jlen; j++){
20847                 var f = fields.items[j];
20848                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
20849                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
20850                 v = f.convert(v);
20851                 values[f.name] = v;
20852             }
20853                 var record = new recordType(values, id);
20854                 record.json = n;
20855                 records[records.length] = record;
20856             }
20857             return {
20858                 records : records,
20859                 totalRecords : records.length
20860             };
20861     }
20862 });/*
20863  * Based on:
20864  * Ext JS Library 1.1.1
20865  * Copyright(c) 2006-2007, Ext JS, LLC.
20866  *
20867  * Originally Released Under LGPL - original licence link has changed is not relivant.
20868  *
20869  * Fork - LGPL
20870  * <script type="text/javascript">
20871  */
20872
20873
20874 /**
20875  * @class Roo.data.Tree
20876  * @extends Roo.util.Observable
20877  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
20878  * in the tree have most standard DOM functionality.
20879  * @constructor
20880  * @param {Node} root (optional) The root node
20881  */
20882 Roo.data.Tree = function(root){
20883    this.nodeHash = {};
20884    /**
20885     * The root node for this tree
20886     * @type Node
20887     */
20888    this.root = null;
20889    if(root){
20890        this.setRootNode(root);
20891    }
20892    this.addEvents({
20893        /**
20894         * @event append
20895         * Fires when a new child node is appended to a node in this tree.
20896         * @param {Tree} tree The owner tree
20897         * @param {Node} parent The parent node
20898         * @param {Node} node The newly appended node
20899         * @param {Number} index The index of the newly appended node
20900         */
20901        "append" : true,
20902        /**
20903         * @event remove
20904         * Fires when a child node is removed from a node in this tree.
20905         * @param {Tree} tree The owner tree
20906         * @param {Node} parent The parent node
20907         * @param {Node} node The child node removed
20908         */
20909        "remove" : true,
20910        /**
20911         * @event move
20912         * Fires when a node is moved to a new location in the tree
20913         * @param {Tree} tree The owner tree
20914         * @param {Node} node The node moved
20915         * @param {Node} oldParent The old parent of this node
20916         * @param {Node} newParent The new parent of this node
20917         * @param {Number} index The index it was moved to
20918         */
20919        "move" : true,
20920        /**
20921         * @event insert
20922         * Fires when a new child node is inserted in a node in this tree.
20923         * @param {Tree} tree The owner tree
20924         * @param {Node} parent The parent node
20925         * @param {Node} node The child node inserted
20926         * @param {Node} refNode The child node the node was inserted before
20927         */
20928        "insert" : true,
20929        /**
20930         * @event beforeappend
20931         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
20932         * @param {Tree} tree The owner tree
20933         * @param {Node} parent The parent node
20934         * @param {Node} node The child node to be appended
20935         */
20936        "beforeappend" : true,
20937        /**
20938         * @event beforeremove
20939         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
20940         * @param {Tree} tree The owner tree
20941         * @param {Node} parent The parent node
20942         * @param {Node} node The child node to be removed
20943         */
20944        "beforeremove" : true,
20945        /**
20946         * @event beforemove
20947         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
20948         * @param {Tree} tree The owner tree
20949         * @param {Node} node The node being moved
20950         * @param {Node} oldParent The parent of the node
20951         * @param {Node} newParent The new parent the node is moving to
20952         * @param {Number} index The index it is being moved to
20953         */
20954        "beforemove" : true,
20955        /**
20956         * @event beforeinsert
20957         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
20958         * @param {Tree} tree The owner tree
20959         * @param {Node} parent The parent node
20960         * @param {Node} node The child node to be inserted
20961         * @param {Node} refNode The child node the node is being inserted before
20962         */
20963        "beforeinsert" : true
20964    });
20965
20966     Roo.data.Tree.superclass.constructor.call(this);
20967 };
20968
20969 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
20970     pathSeparator: "/",
20971
20972     proxyNodeEvent : function(){
20973         return this.fireEvent.apply(this, arguments);
20974     },
20975
20976     /**
20977      * Returns the root node for this tree.
20978      * @return {Node}
20979      */
20980     getRootNode : function(){
20981         return this.root;
20982     },
20983
20984     /**
20985      * Sets the root node for this tree.
20986      * @param {Node} node
20987      * @return {Node}
20988      */
20989     setRootNode : function(node){
20990         this.root = node;
20991         node.ownerTree = this;
20992         node.isRoot = true;
20993         this.registerNode(node);
20994         return node;
20995     },
20996
20997     /**
20998      * Gets a node in this tree by its id.
20999      * @param {String} id
21000      * @return {Node}
21001      */
21002     getNodeById : function(id){
21003         return this.nodeHash[id];
21004     },
21005
21006     registerNode : function(node){
21007         this.nodeHash[node.id] = node;
21008     },
21009
21010     unregisterNode : function(node){
21011         delete this.nodeHash[node.id];
21012     },
21013
21014     toString : function(){
21015         return "[Tree"+(this.id?" "+this.id:"")+"]";
21016     }
21017 });
21018
21019 /**
21020  * @class Roo.data.Node
21021  * @extends Roo.util.Observable
21022  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
21023  * @cfg {String} id The id for this node. If one is not specified, one is generated.
21024  * @constructor
21025  * @param {Object} attributes The attributes/config for the node
21026  */
21027 Roo.data.Node = function(attributes){
21028     /**
21029      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
21030      * @type {Object}
21031      */
21032     this.attributes = attributes || {};
21033     this.leaf = this.attributes.leaf;
21034     /**
21035      * The node id. @type String
21036      */
21037     this.id = this.attributes.id;
21038     if(!this.id){
21039         this.id = Roo.id(null, "ynode-");
21040         this.attributes.id = this.id;
21041     }
21042     /**
21043      * All child nodes of this node. @type Array
21044      */
21045     this.childNodes = [];
21046     if(!this.childNodes.indexOf){ // indexOf is a must
21047         this.childNodes.indexOf = function(o){
21048             for(var i = 0, len = this.length; i < len; i++){
21049                 if(this[i] == o) {
21050                     return i;
21051                 }
21052             }
21053             return -1;
21054         };
21055     }
21056     /**
21057      * The parent node for this node. @type Node
21058      */
21059     this.parentNode = null;
21060     /**
21061      * The first direct child node of this node, or null if this node has no child nodes. @type Node
21062      */
21063     this.firstChild = null;
21064     /**
21065      * The last direct child node of this node, or null if this node has no child nodes. @type Node
21066      */
21067     this.lastChild = null;
21068     /**
21069      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
21070      */
21071     this.previousSibling = null;
21072     /**
21073      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
21074      */
21075     this.nextSibling = null;
21076
21077     this.addEvents({
21078        /**
21079         * @event append
21080         * Fires when a new child node is appended
21081         * @param {Tree} tree The owner tree
21082         * @param {Node} this This node
21083         * @param {Node} node The newly appended node
21084         * @param {Number} index The index of the newly appended node
21085         */
21086        "append" : true,
21087        /**
21088         * @event remove
21089         * Fires when a child node is removed
21090         * @param {Tree} tree The owner tree
21091         * @param {Node} this This node
21092         * @param {Node} node The removed node
21093         */
21094        "remove" : true,
21095        /**
21096         * @event move
21097         * Fires when this node is moved to a new location in the tree
21098         * @param {Tree} tree The owner tree
21099         * @param {Node} this This node
21100         * @param {Node} oldParent The old parent of this node
21101         * @param {Node} newParent The new parent of this node
21102         * @param {Number} index The index it was moved to
21103         */
21104        "move" : true,
21105        /**
21106         * @event insert
21107         * Fires when a new child node is inserted.
21108         * @param {Tree} tree The owner tree
21109         * @param {Node} this This node
21110         * @param {Node} node The child node inserted
21111         * @param {Node} refNode The child node the node was inserted before
21112         */
21113        "insert" : true,
21114        /**
21115         * @event beforeappend
21116         * Fires before a new child is appended, return false to cancel the append.
21117         * @param {Tree} tree The owner tree
21118         * @param {Node} this This node
21119         * @param {Node} node The child node to be appended
21120         */
21121        "beforeappend" : true,
21122        /**
21123         * @event beforeremove
21124         * Fires before a child is removed, return false to cancel the remove.
21125         * @param {Tree} tree The owner tree
21126         * @param {Node} this This node
21127         * @param {Node} node The child node to be removed
21128         */
21129        "beforeremove" : true,
21130        /**
21131         * @event beforemove
21132         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
21133         * @param {Tree} tree The owner tree
21134         * @param {Node} this This node
21135         * @param {Node} oldParent The parent of this node
21136         * @param {Node} newParent The new parent this node is moving to
21137         * @param {Number} index The index it is being moved to
21138         */
21139        "beforemove" : true,
21140        /**
21141         * @event beforeinsert
21142         * Fires before a new child is inserted, return false to cancel the insert.
21143         * @param {Tree} tree The owner tree
21144         * @param {Node} this This node
21145         * @param {Node} node The child node to be inserted
21146         * @param {Node} refNode The child node the node is being inserted before
21147         */
21148        "beforeinsert" : true
21149    });
21150     this.listeners = this.attributes.listeners;
21151     Roo.data.Node.superclass.constructor.call(this);
21152 };
21153
21154 Roo.extend(Roo.data.Node, Roo.util.Observable, {
21155     fireEvent : function(evtName){
21156         // first do standard event for this node
21157         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
21158             return false;
21159         }
21160         // then bubble it up to the tree if the event wasn't cancelled
21161         var ot = this.getOwnerTree();
21162         if(ot){
21163             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
21164                 return false;
21165             }
21166         }
21167         return true;
21168     },
21169
21170     /**
21171      * Returns true if this node is a leaf
21172      * @return {Boolean}
21173      */
21174     isLeaf : function(){
21175         return this.leaf === true;
21176     },
21177
21178     // private
21179     setFirstChild : function(node){
21180         this.firstChild = node;
21181     },
21182
21183     //private
21184     setLastChild : function(node){
21185         this.lastChild = node;
21186     },
21187
21188
21189     /**
21190      * Returns true if this node is the last child of its parent
21191      * @return {Boolean}
21192      */
21193     isLast : function(){
21194        return (!this.parentNode ? true : this.parentNode.lastChild == this);
21195     },
21196
21197     /**
21198      * Returns true if this node is the first child of its parent
21199      * @return {Boolean}
21200      */
21201     isFirst : function(){
21202        return (!this.parentNode ? true : this.parentNode.firstChild == this);
21203     },
21204
21205     hasChildNodes : function(){
21206         return !this.isLeaf() && this.childNodes.length > 0;
21207     },
21208
21209     /**
21210      * Insert node(s) as the last child node of this node.
21211      * @param {Node/Array} node The node or Array of nodes to append
21212      * @return {Node} The appended node if single append, or null if an array was passed
21213      */
21214     appendChild : function(node){
21215         var multi = false;
21216         if(node instanceof Array){
21217             multi = node;
21218         }else if(arguments.length > 1){
21219             multi = arguments;
21220         }
21221         // if passed an array or multiple args do them one by one
21222         if(multi){
21223             for(var i = 0, len = multi.length; i < len; i++) {
21224                 this.appendChild(multi[i]);
21225             }
21226         }else{
21227             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
21228                 return false;
21229             }
21230             var index = this.childNodes.length;
21231             var oldParent = node.parentNode;
21232             // it's a move, make sure we move it cleanly
21233             if(oldParent){
21234                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
21235                     return false;
21236                 }
21237                 oldParent.removeChild(node);
21238             }
21239             index = this.childNodes.length;
21240             if(index == 0){
21241                 this.setFirstChild(node);
21242             }
21243             this.childNodes.push(node);
21244             node.parentNode = this;
21245             var ps = this.childNodes[index-1];
21246             if(ps){
21247                 node.previousSibling = ps;
21248                 ps.nextSibling = node;
21249             }else{
21250                 node.previousSibling = null;
21251             }
21252             node.nextSibling = null;
21253             this.setLastChild(node);
21254             node.setOwnerTree(this.getOwnerTree());
21255             this.fireEvent("append", this.ownerTree, this, node, index);
21256             if(oldParent){
21257                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
21258             }
21259             return node;
21260         }
21261     },
21262
21263     /**
21264      * Removes a child node from this node.
21265      * @param {Node} node The node to remove
21266      * @return {Node} The removed node
21267      */
21268     removeChild : function(node){
21269         var index = this.childNodes.indexOf(node);
21270         if(index == -1){
21271             return false;
21272         }
21273         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
21274             return false;
21275         }
21276
21277         // remove it from childNodes collection
21278         this.childNodes.splice(index, 1);
21279
21280         // update siblings
21281         if(node.previousSibling){
21282             node.previousSibling.nextSibling = node.nextSibling;
21283         }
21284         if(node.nextSibling){
21285             node.nextSibling.previousSibling = node.previousSibling;
21286         }
21287
21288         // update child refs
21289         if(this.firstChild == node){
21290             this.setFirstChild(node.nextSibling);
21291         }
21292         if(this.lastChild == node){
21293             this.setLastChild(node.previousSibling);
21294         }
21295
21296         node.setOwnerTree(null);
21297         // clear any references from the node
21298         node.parentNode = null;
21299         node.previousSibling = null;
21300         node.nextSibling = null;
21301         this.fireEvent("remove", this.ownerTree, this, node);
21302         return node;
21303     },
21304
21305     /**
21306      * Inserts the first node before the second node in this nodes childNodes collection.
21307      * @param {Node} node The node to insert
21308      * @param {Node} refNode The node to insert before (if null the node is appended)
21309      * @return {Node} The inserted node
21310      */
21311     insertBefore : function(node, refNode){
21312         if(!refNode){ // like standard Dom, refNode can be null for append
21313             return this.appendChild(node);
21314         }
21315         // nothing to do
21316         if(node == refNode){
21317             return false;
21318         }
21319
21320         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
21321             return false;
21322         }
21323         var index = this.childNodes.indexOf(refNode);
21324         var oldParent = node.parentNode;
21325         var refIndex = index;
21326
21327         // when moving internally, indexes will change after remove
21328         if(oldParent == this && this.childNodes.indexOf(node) < index){
21329             refIndex--;
21330         }
21331
21332         // it's a move, make sure we move it cleanly
21333         if(oldParent){
21334             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
21335                 return false;
21336             }
21337             oldParent.removeChild(node);
21338         }
21339         if(refIndex == 0){
21340             this.setFirstChild(node);
21341         }
21342         this.childNodes.splice(refIndex, 0, node);
21343         node.parentNode = this;
21344         var ps = this.childNodes[refIndex-1];
21345         if(ps){
21346             node.previousSibling = ps;
21347             ps.nextSibling = node;
21348         }else{
21349             node.previousSibling = null;
21350         }
21351         node.nextSibling = refNode;
21352         refNode.previousSibling = node;
21353         node.setOwnerTree(this.getOwnerTree());
21354         this.fireEvent("insert", this.ownerTree, this, node, refNode);
21355         if(oldParent){
21356             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
21357         }
21358         return node;
21359     },
21360
21361     /**
21362      * Returns the child node at the specified index.
21363      * @param {Number} index
21364      * @return {Node}
21365      */
21366     item : function(index){
21367         return this.childNodes[index];
21368     },
21369
21370     /**
21371      * Replaces one child node in this node with another.
21372      * @param {Node} newChild The replacement node
21373      * @param {Node} oldChild The node to replace
21374      * @return {Node} The replaced node
21375      */
21376     replaceChild : function(newChild, oldChild){
21377         this.insertBefore(newChild, oldChild);
21378         this.removeChild(oldChild);
21379         return oldChild;
21380     },
21381
21382     /**
21383      * Returns the index of a child node
21384      * @param {Node} node
21385      * @return {Number} The index of the node or -1 if it was not found
21386      */
21387     indexOf : function(child){
21388         return this.childNodes.indexOf(child);
21389     },
21390
21391     /**
21392      * Returns the tree this node is in.
21393      * @return {Tree}
21394      */
21395     getOwnerTree : function(){
21396         // if it doesn't have one, look for one
21397         if(!this.ownerTree){
21398             var p = this;
21399             while(p){
21400                 if(p.ownerTree){
21401                     this.ownerTree = p.ownerTree;
21402                     break;
21403                 }
21404                 p = p.parentNode;
21405             }
21406         }
21407         return this.ownerTree;
21408     },
21409
21410     /**
21411      * Returns depth of this node (the root node has a depth of 0)
21412      * @return {Number}
21413      */
21414     getDepth : function(){
21415         var depth = 0;
21416         var p = this;
21417         while(p.parentNode){
21418             ++depth;
21419             p = p.parentNode;
21420         }
21421         return depth;
21422     },
21423
21424     // private
21425     setOwnerTree : function(tree){
21426         // if it's move, we need to update everyone
21427         if(tree != this.ownerTree){
21428             if(this.ownerTree){
21429                 this.ownerTree.unregisterNode(this);
21430             }
21431             this.ownerTree = tree;
21432             var cs = this.childNodes;
21433             for(var i = 0, len = cs.length; i < len; i++) {
21434                 cs[i].setOwnerTree(tree);
21435             }
21436             if(tree){
21437                 tree.registerNode(this);
21438             }
21439         }
21440     },
21441
21442     /**
21443      * Returns the path for this node. The path can be used to expand or select this node programmatically.
21444      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
21445      * @return {String} The path
21446      */
21447     getPath : function(attr){
21448         attr = attr || "id";
21449         var p = this.parentNode;
21450         var b = [this.attributes[attr]];
21451         while(p){
21452             b.unshift(p.attributes[attr]);
21453             p = p.parentNode;
21454         }
21455         var sep = this.getOwnerTree().pathSeparator;
21456         return sep + b.join(sep);
21457     },
21458
21459     /**
21460      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21461      * function call will be the scope provided or the current node. The arguments to the function
21462      * will be the args provided or the current node. If the function returns false at any point,
21463      * the bubble is stopped.
21464      * @param {Function} fn The function to call
21465      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21466      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21467      */
21468     bubble : function(fn, scope, args){
21469         var p = this;
21470         while(p){
21471             if(fn.call(scope || p, args || p) === false){
21472                 break;
21473             }
21474             p = p.parentNode;
21475         }
21476     },
21477
21478     /**
21479      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
21480      * function call will be the scope provided or the current node. The arguments to the function
21481      * will be the args provided or the current node. If the function returns false at any point,
21482      * the cascade is stopped on that branch.
21483      * @param {Function} fn The function to call
21484      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21485      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21486      */
21487     cascade : function(fn, scope, args){
21488         if(fn.call(scope || this, args || this) !== false){
21489             var cs = this.childNodes;
21490             for(var i = 0, len = cs.length; i < len; i++) {
21491                 cs[i].cascade(fn, scope, args);
21492             }
21493         }
21494     },
21495
21496     /**
21497      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
21498      * function call will be the scope provided or the current node. The arguments to the function
21499      * will be the args provided or the current node. If the function returns false at any point,
21500      * the iteration stops.
21501      * @param {Function} fn The function to call
21502      * @param {Object} scope (optional) The scope of the function (defaults to current node)
21503      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
21504      */
21505     eachChild : function(fn, scope, args){
21506         var cs = this.childNodes;
21507         for(var i = 0, len = cs.length; i < len; i++) {
21508                 if(fn.call(scope || this, args || cs[i]) === false){
21509                     break;
21510                 }
21511         }
21512     },
21513
21514     /**
21515      * Finds the first child that has the attribute with the specified value.
21516      * @param {String} attribute The attribute name
21517      * @param {Mixed} value The value to search for
21518      * @return {Node} The found child or null if none was found
21519      */
21520     findChild : function(attribute, value){
21521         var cs = this.childNodes;
21522         for(var i = 0, len = cs.length; i < len; i++) {
21523                 if(cs[i].attributes[attribute] == value){
21524                     return cs[i];
21525                 }
21526         }
21527         return null;
21528     },
21529
21530     /**
21531      * Finds the first child by a custom function. The child matches if the function passed
21532      * returns true.
21533      * @param {Function} fn
21534      * @param {Object} scope (optional)
21535      * @return {Node} The found child or null if none was found
21536      */
21537     findChildBy : function(fn, scope){
21538         var cs = this.childNodes;
21539         for(var i = 0, len = cs.length; i < len; i++) {
21540                 if(fn.call(scope||cs[i], cs[i]) === true){
21541                     return cs[i];
21542                 }
21543         }
21544         return null;
21545     },
21546
21547     /**
21548      * Sorts this nodes children using the supplied sort function
21549      * @param {Function} fn
21550      * @param {Object} scope (optional)
21551      */
21552     sort : function(fn, scope){
21553         var cs = this.childNodes;
21554         var len = cs.length;
21555         if(len > 0){
21556             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
21557             cs.sort(sortFn);
21558             for(var i = 0; i < len; i++){
21559                 var n = cs[i];
21560                 n.previousSibling = cs[i-1];
21561                 n.nextSibling = cs[i+1];
21562                 if(i == 0){
21563                     this.setFirstChild(n);
21564                 }
21565                 if(i == len-1){
21566                     this.setLastChild(n);
21567                 }
21568             }
21569         }
21570     },
21571
21572     /**
21573      * Returns true if this node is an ancestor (at any point) of the passed node.
21574      * @param {Node} node
21575      * @return {Boolean}
21576      */
21577     contains : function(node){
21578         return node.isAncestor(this);
21579     },
21580
21581     /**
21582      * Returns true if the passed node is an ancestor (at any point) of this node.
21583      * @param {Node} node
21584      * @return {Boolean}
21585      */
21586     isAncestor : function(node){
21587         var p = this.parentNode;
21588         while(p){
21589             if(p == node){
21590                 return true;
21591             }
21592             p = p.parentNode;
21593         }
21594         return false;
21595     },
21596
21597     toString : function(){
21598         return "[Node"+(this.id?" "+this.id:"")+"]";
21599     }
21600 });/*
21601  * Based on:
21602  * Ext JS Library 1.1.1
21603  * Copyright(c) 2006-2007, Ext JS, LLC.
21604  *
21605  * Originally Released Under LGPL - original licence link has changed is not relivant.
21606  *
21607  * Fork - LGPL
21608  * <script type="text/javascript">
21609  */
21610  
21611
21612 /**
21613  * @class Roo.ComponentMgr
21614  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
21615  * @singleton
21616  */
21617 Roo.ComponentMgr = function(){
21618     var all = new Roo.util.MixedCollection();
21619
21620     return {
21621         /**
21622          * Registers a component.
21623          * @param {Roo.Component} c The component
21624          */
21625         register : function(c){
21626             all.add(c);
21627         },
21628
21629         /**
21630          * Unregisters a component.
21631          * @param {Roo.Component} c The component
21632          */
21633         unregister : function(c){
21634             all.remove(c);
21635         },
21636
21637         /**
21638          * Returns a component by id
21639          * @param {String} id The component id
21640          */
21641         get : function(id){
21642             return all.get(id);
21643         },
21644
21645         /**
21646          * Registers a function that will be called when a specified component is added to ComponentMgr
21647          * @param {String} id The component id
21648          * @param {Funtction} fn The callback function
21649          * @param {Object} scope The scope of the callback
21650          */
21651         onAvailable : function(id, fn, scope){
21652             all.on("add", function(index, o){
21653                 if(o.id == id){
21654                     fn.call(scope || o, o);
21655                     all.un("add", fn, scope);
21656                 }
21657             });
21658         }
21659     };
21660 }();/*
21661  * Based on:
21662  * Ext JS Library 1.1.1
21663  * Copyright(c) 2006-2007, Ext JS, LLC.
21664  *
21665  * Originally Released Under LGPL - original licence link has changed is not relivant.
21666  *
21667  * Fork - LGPL
21668  * <script type="text/javascript">
21669  */
21670  
21671 /**
21672  * @class Roo.Component
21673  * @extends Roo.util.Observable
21674  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
21675  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
21676  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
21677  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
21678  * All visual components (widgets) that require rendering into a layout should subclass Component.
21679  * @constructor
21680  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
21681  * 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
21682  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
21683  */
21684 Roo.Component = function(config){
21685     config = config || {};
21686     if(config.tagName || config.dom || typeof config == "string"){ // element object
21687         config = {el: config, id: config.id || config};
21688     }
21689     this.initialConfig = config;
21690
21691     Roo.apply(this, config);
21692     this.addEvents({
21693         /**
21694          * @event disable
21695          * Fires after the component is disabled.
21696              * @param {Roo.Component} this
21697              */
21698         disable : true,
21699         /**
21700          * @event enable
21701          * Fires after the component is enabled.
21702              * @param {Roo.Component} this
21703              */
21704         enable : true,
21705         /**
21706          * @event beforeshow
21707          * Fires before the component is shown.  Return false to stop the show.
21708              * @param {Roo.Component} this
21709              */
21710         beforeshow : true,
21711         /**
21712          * @event show
21713          * Fires after the component is shown.
21714              * @param {Roo.Component} this
21715              */
21716         show : true,
21717         /**
21718          * @event beforehide
21719          * Fires before the component is hidden. Return false to stop the hide.
21720              * @param {Roo.Component} this
21721              */
21722         beforehide : true,
21723         /**
21724          * @event hide
21725          * Fires after the component is hidden.
21726              * @param {Roo.Component} this
21727              */
21728         hide : true,
21729         /**
21730          * @event beforerender
21731          * Fires before the component is rendered. Return false to stop the render.
21732              * @param {Roo.Component} this
21733              */
21734         beforerender : true,
21735         /**
21736          * @event render
21737          * Fires after the component is rendered.
21738              * @param {Roo.Component} this
21739              */
21740         render : true,
21741         /**
21742          * @event beforedestroy
21743          * Fires before the component is destroyed. Return false to stop the destroy.
21744              * @param {Roo.Component} this
21745              */
21746         beforedestroy : true,
21747         /**
21748          * @event destroy
21749          * Fires after the component is destroyed.
21750              * @param {Roo.Component} this
21751              */
21752         destroy : true
21753     });
21754     if(!this.id){
21755         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
21756     }
21757     Roo.ComponentMgr.register(this);
21758     Roo.Component.superclass.constructor.call(this);
21759     this.initComponent();
21760     if(this.renderTo){ // not supported by all components yet. use at your own risk!
21761         this.render(this.renderTo);
21762         delete this.renderTo;
21763     }
21764 };
21765
21766 // private
21767 Roo.Component.AUTO_ID = 1000;
21768
21769 Roo.extend(Roo.Component, Roo.util.Observable, {
21770     /**
21771      * @property {Boolean} hidden
21772      * true if this component is hidden. Read-only.
21773      */
21774     hidden : false,
21775     /**
21776      * true if this component is disabled. Read-only.
21777      */
21778     disabled : false,
21779     /**
21780      * true if this component has been rendered. Read-only.
21781      */
21782     rendered : false,
21783     
21784     /** @cfg {String} disableClass
21785      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
21786      */
21787     disabledClass : "x-item-disabled",
21788         /** @cfg {Boolean} allowDomMove
21789          * Whether the component can move the Dom node when rendering (defaults to true).
21790          */
21791     allowDomMove : true,
21792     /** @cfg {String} hideMode
21793      * How this component should hidden. Supported values are
21794      * "visibility" (css visibility), "offsets" (negative offset position) and
21795      * "display" (css display) - defaults to "display".
21796      */
21797     hideMode: 'display',
21798
21799     // private
21800     ctype : "Roo.Component",
21801
21802     /** @cfg {String} actionMode 
21803      * which property holds the element that used for  hide() / show() / disable() / enable()
21804      * default is 'el' 
21805      */
21806     actionMode : "el",
21807
21808     // private
21809     getActionEl : function(){
21810         return this[this.actionMode];
21811     },
21812
21813     initComponent : Roo.emptyFn,
21814     /**
21815      * If this is a lazy rendering component, render it to its container element.
21816      * @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.
21817      */
21818     render : function(container, position){
21819         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
21820             if(!container && this.el){
21821                 this.el = Roo.get(this.el);
21822                 container = this.el.dom.parentNode;
21823                 this.allowDomMove = false;
21824             }
21825             this.container = Roo.get(container);
21826             this.rendered = true;
21827             if(position !== undefined){
21828                 if(typeof position == 'number'){
21829                     position = this.container.dom.childNodes[position];
21830                 }else{
21831                     position = Roo.getDom(position);
21832                 }
21833             }
21834             this.onRender(this.container, position || null);
21835             if(this.cls){
21836                 this.el.addClass(this.cls);
21837                 delete this.cls;
21838             }
21839             if(this.style){
21840                 this.el.applyStyles(this.style);
21841                 delete this.style;
21842             }
21843             this.fireEvent("render", this);
21844             this.afterRender(this.container);
21845             if(this.hidden){
21846                 this.hide();
21847             }
21848             if(this.disabled){
21849                 this.disable();
21850             }
21851         }
21852         return this;
21853     },
21854
21855     // private
21856     // default function is not really useful
21857     onRender : function(ct, position){
21858         if(this.el){
21859             this.el = Roo.get(this.el);
21860             if(this.allowDomMove !== false){
21861                 ct.dom.insertBefore(this.el.dom, position);
21862             }
21863         }
21864     },
21865
21866     // private
21867     getAutoCreate : function(){
21868         var cfg = typeof this.autoCreate == "object" ?
21869                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
21870         if(this.id && !cfg.id){
21871             cfg.id = this.id;
21872         }
21873         return cfg;
21874     },
21875
21876     // private
21877     afterRender : Roo.emptyFn,
21878
21879     /**
21880      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
21881      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
21882      */
21883     destroy : function(){
21884         if(this.fireEvent("beforedestroy", this) !== false){
21885             this.purgeListeners();
21886             this.beforeDestroy();
21887             if(this.rendered){
21888                 this.el.removeAllListeners();
21889                 this.el.remove();
21890                 if(this.actionMode == "container"){
21891                     this.container.remove();
21892                 }
21893             }
21894             this.onDestroy();
21895             Roo.ComponentMgr.unregister(this);
21896             this.fireEvent("destroy", this);
21897         }
21898     },
21899
21900         // private
21901     beforeDestroy : function(){
21902
21903     },
21904
21905         // private
21906         onDestroy : function(){
21907
21908     },
21909
21910     /**
21911      * Returns the underlying {@link Roo.Element}.
21912      * @return {Roo.Element} The element
21913      */
21914     getEl : function(){
21915         return this.el;
21916     },
21917
21918     /**
21919      * Returns the id of this component.
21920      * @return {String}
21921      */
21922     getId : function(){
21923         return this.id;
21924     },
21925
21926     /**
21927      * Try to focus this component.
21928      * @param {Boolean} selectText True to also select the text in this component (if applicable)
21929      * @return {Roo.Component} this
21930      */
21931     focus : function(selectText){
21932         if(this.rendered){
21933             this.el.focus();
21934             if(selectText === true){
21935                 this.el.dom.select();
21936             }
21937         }
21938         return this;
21939     },
21940
21941     // private
21942     blur : function(){
21943         if(this.rendered){
21944             this.el.blur();
21945         }
21946         return this;
21947     },
21948
21949     /**
21950      * Disable this component.
21951      * @return {Roo.Component} this
21952      */
21953     disable : function(){
21954         if(this.rendered){
21955             this.onDisable();
21956         }
21957         this.disabled = true;
21958         this.fireEvent("disable", this);
21959         return this;
21960     },
21961
21962         // private
21963     onDisable : function(){
21964         this.getActionEl().addClass(this.disabledClass);
21965         this.el.dom.disabled = true;
21966     },
21967
21968     /**
21969      * Enable this component.
21970      * @return {Roo.Component} this
21971      */
21972     enable : function(){
21973         if(this.rendered){
21974             this.onEnable();
21975         }
21976         this.disabled = false;
21977         this.fireEvent("enable", this);
21978         return this;
21979     },
21980
21981         // private
21982     onEnable : function(){
21983         this.getActionEl().removeClass(this.disabledClass);
21984         this.el.dom.disabled = false;
21985     },
21986
21987     /**
21988      * Convenience function for setting disabled/enabled by boolean.
21989      * @param {Boolean} disabled
21990      */
21991     setDisabled : function(disabled){
21992         this[disabled ? "disable" : "enable"]();
21993     },
21994
21995     /**
21996      * Show this component.
21997      * @return {Roo.Component} this
21998      */
21999     show: function(){
22000         if(this.fireEvent("beforeshow", this) !== false){
22001             this.hidden = false;
22002             if(this.rendered){
22003                 this.onShow();
22004             }
22005             this.fireEvent("show", this);
22006         }
22007         return this;
22008     },
22009
22010     // private
22011     onShow : function(){
22012         var ae = this.getActionEl();
22013         if(this.hideMode == 'visibility'){
22014             ae.dom.style.visibility = "visible";
22015         }else if(this.hideMode == 'offsets'){
22016             ae.removeClass('x-hidden');
22017         }else{
22018             ae.dom.style.display = "";
22019         }
22020     },
22021
22022     /**
22023      * Hide this component.
22024      * @return {Roo.Component} this
22025      */
22026     hide: function(){
22027         if(this.fireEvent("beforehide", this) !== false){
22028             this.hidden = true;
22029             if(this.rendered){
22030                 this.onHide();
22031             }
22032             this.fireEvent("hide", this);
22033         }
22034         return this;
22035     },
22036
22037     // private
22038     onHide : function(){
22039         var ae = this.getActionEl();
22040         if(this.hideMode == 'visibility'){
22041             ae.dom.style.visibility = "hidden";
22042         }else if(this.hideMode == 'offsets'){
22043             ae.addClass('x-hidden');
22044         }else{
22045             ae.dom.style.display = "none";
22046         }
22047     },
22048
22049     /**
22050      * Convenience function to hide or show this component by boolean.
22051      * @param {Boolean} visible True to show, false to hide
22052      * @return {Roo.Component} this
22053      */
22054     setVisible: function(visible){
22055         if(visible) {
22056             this.show();
22057         }else{
22058             this.hide();
22059         }
22060         return this;
22061     },
22062
22063     /**
22064      * Returns true if this component is visible.
22065      */
22066     isVisible : function(){
22067         return this.getActionEl().isVisible();
22068     },
22069
22070     cloneConfig : function(overrides){
22071         overrides = overrides || {};
22072         var id = overrides.id || Roo.id();
22073         var cfg = Roo.applyIf(overrides, this.initialConfig);
22074         cfg.id = id; // prevent dup id
22075         return new this.constructor(cfg);
22076     }
22077 });/*
22078  * Based on:
22079  * Ext JS Library 1.1.1
22080  * Copyright(c) 2006-2007, Ext JS, LLC.
22081  *
22082  * Originally Released Under LGPL - original licence link has changed is not relivant.
22083  *
22084  * Fork - LGPL
22085  * <script type="text/javascript">
22086  */
22087  (function(){ 
22088 /**
22089  * @class Roo.Layer
22090  * @extends Roo.Element
22091  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
22092  * automatic maintaining of shadow/shim positions.
22093  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
22094  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
22095  * you can pass a string with a CSS class name. False turns off the shadow.
22096  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
22097  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
22098  * @cfg {String} cls CSS class to add to the element
22099  * @cfg {Number} zindex Starting z-index (defaults to 11000)
22100  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
22101  * @constructor
22102  * @param {Object} config An object with config options.
22103  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
22104  */
22105
22106 Roo.Layer = function(config, existingEl){
22107     config = config || {};
22108     var dh = Roo.DomHelper;
22109     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
22110     if(existingEl){
22111         this.dom = Roo.getDom(existingEl);
22112     }
22113     if(!this.dom){
22114         var o = config.dh || {tag: "div", cls: "x-layer"};
22115         this.dom = dh.append(pel, o);
22116     }
22117     if(config.cls){
22118         this.addClass(config.cls);
22119     }
22120     this.constrain = config.constrain !== false;
22121     this.visibilityMode = Roo.Element.VISIBILITY;
22122     if(config.id){
22123         this.id = this.dom.id = config.id;
22124     }else{
22125         this.id = Roo.id(this.dom);
22126     }
22127     this.zindex = config.zindex || this.getZIndex();
22128     this.position("absolute", this.zindex);
22129     if(config.shadow){
22130         this.shadowOffset = config.shadowOffset || 4;
22131         this.shadow = new Roo.Shadow({
22132             offset : this.shadowOffset,
22133             mode : config.shadow
22134         });
22135     }else{
22136         this.shadowOffset = 0;
22137     }
22138     this.useShim = config.shim !== false && Roo.useShims;
22139     this.useDisplay = config.useDisplay;
22140     this.hide();
22141 };
22142
22143 var supr = Roo.Element.prototype;
22144
22145 // shims are shared among layer to keep from having 100 iframes
22146 var shims = [];
22147
22148 Roo.extend(Roo.Layer, Roo.Element, {
22149
22150     getZIndex : function(){
22151         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
22152     },
22153
22154     getShim : function(){
22155         if(!this.useShim){
22156             return null;
22157         }
22158         if(this.shim){
22159             return this.shim;
22160         }
22161         var shim = shims.shift();
22162         if(!shim){
22163             shim = this.createShim();
22164             shim.enableDisplayMode('block');
22165             shim.dom.style.display = 'none';
22166             shim.dom.style.visibility = 'visible';
22167         }
22168         var pn = this.dom.parentNode;
22169         if(shim.dom.parentNode != pn){
22170             pn.insertBefore(shim.dom, this.dom);
22171         }
22172         shim.setStyle('z-index', this.getZIndex()-2);
22173         this.shim = shim;
22174         return shim;
22175     },
22176
22177     hideShim : function(){
22178         if(this.shim){
22179             this.shim.setDisplayed(false);
22180             shims.push(this.shim);
22181             delete this.shim;
22182         }
22183     },
22184
22185     disableShadow : function(){
22186         if(this.shadow){
22187             this.shadowDisabled = true;
22188             this.shadow.hide();
22189             this.lastShadowOffset = this.shadowOffset;
22190             this.shadowOffset = 0;
22191         }
22192     },
22193
22194     enableShadow : function(show){
22195         if(this.shadow){
22196             this.shadowDisabled = false;
22197             this.shadowOffset = this.lastShadowOffset;
22198             delete this.lastShadowOffset;
22199             if(show){
22200                 this.sync(true);
22201             }
22202         }
22203     },
22204
22205     // private
22206     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
22207     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
22208     sync : function(doShow){
22209         var sw = this.shadow;
22210         if(!this.updating && this.isVisible() && (sw || this.useShim)){
22211             var sh = this.getShim();
22212
22213             var w = this.getWidth(),
22214                 h = this.getHeight();
22215
22216             var l = this.getLeft(true),
22217                 t = this.getTop(true);
22218
22219             if(sw && !this.shadowDisabled){
22220                 if(doShow && !sw.isVisible()){
22221                     sw.show(this);
22222                 }else{
22223                     sw.realign(l, t, w, h);
22224                 }
22225                 if(sh){
22226                     if(doShow){
22227                        sh.show();
22228                     }
22229                     // fit the shim behind the shadow, so it is shimmed too
22230                     var a = sw.adjusts, s = sh.dom.style;
22231                     s.left = (Math.min(l, l+a.l))+"px";
22232                     s.top = (Math.min(t, t+a.t))+"px";
22233                     s.width = (w+a.w)+"px";
22234                     s.height = (h+a.h)+"px";
22235                 }
22236             }else if(sh){
22237                 if(doShow){
22238                    sh.show();
22239                 }
22240                 sh.setSize(w, h);
22241                 sh.setLeftTop(l, t);
22242             }
22243             
22244         }
22245     },
22246
22247     // private
22248     destroy : function(){
22249         this.hideShim();
22250         if(this.shadow){
22251             this.shadow.hide();
22252         }
22253         this.removeAllListeners();
22254         var pn = this.dom.parentNode;
22255         if(pn){
22256             pn.removeChild(this.dom);
22257         }
22258         Roo.Element.uncache(this.id);
22259     },
22260
22261     remove : function(){
22262         this.destroy();
22263     },
22264
22265     // private
22266     beginUpdate : function(){
22267         this.updating = true;
22268     },
22269
22270     // private
22271     endUpdate : function(){
22272         this.updating = false;
22273         this.sync(true);
22274     },
22275
22276     // private
22277     hideUnders : function(negOffset){
22278         if(this.shadow){
22279             this.shadow.hide();
22280         }
22281         this.hideShim();
22282     },
22283
22284     // private
22285     constrainXY : function(){
22286         if(this.constrain){
22287             var vw = Roo.lib.Dom.getViewWidth(),
22288                 vh = Roo.lib.Dom.getViewHeight();
22289             var s = Roo.get(document).getScroll();
22290
22291             var xy = this.getXY();
22292             var x = xy[0], y = xy[1];   
22293             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
22294             // only move it if it needs it
22295             var moved = false;
22296             // first validate right/bottom
22297             if((x + w) > vw+s.left){
22298                 x = vw - w - this.shadowOffset;
22299                 moved = true;
22300             }
22301             if((y + h) > vh+s.top){
22302                 y = vh - h - this.shadowOffset;
22303                 moved = true;
22304             }
22305             // then make sure top/left isn't negative
22306             if(x < s.left){
22307                 x = s.left;
22308                 moved = true;
22309             }
22310             if(y < s.top){
22311                 y = s.top;
22312                 moved = true;
22313             }
22314             if(moved){
22315                 if(this.avoidY){
22316                     var ay = this.avoidY;
22317                     if(y <= ay && (y+h) >= ay){
22318                         y = ay-h-5;   
22319                     }
22320                 }
22321                 xy = [x, y];
22322                 this.storeXY(xy);
22323                 supr.setXY.call(this, xy);
22324                 this.sync();
22325             }
22326         }
22327     },
22328
22329     isVisible : function(){
22330         return this.visible;    
22331     },
22332
22333     // private
22334     showAction : function(){
22335         this.visible = true; // track visibility to prevent getStyle calls
22336         if(this.useDisplay === true){
22337             this.setDisplayed("");
22338         }else if(this.lastXY){
22339             supr.setXY.call(this, this.lastXY);
22340         }else if(this.lastLT){
22341             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
22342         }
22343     },
22344
22345     // private
22346     hideAction : function(){
22347         this.visible = false;
22348         if(this.useDisplay === true){
22349             this.setDisplayed(false);
22350         }else{
22351             this.setLeftTop(-10000,-10000);
22352         }
22353     },
22354
22355     // overridden Element method
22356     setVisible : function(v, a, d, c, e){
22357         if(v){
22358             this.showAction();
22359         }
22360         if(a && v){
22361             var cb = function(){
22362                 this.sync(true);
22363                 if(c){
22364                     c();
22365                 }
22366             }.createDelegate(this);
22367             supr.setVisible.call(this, true, true, d, cb, e);
22368         }else{
22369             if(!v){
22370                 this.hideUnders(true);
22371             }
22372             var cb = c;
22373             if(a){
22374                 cb = function(){
22375                     this.hideAction();
22376                     if(c){
22377                         c();
22378                     }
22379                 }.createDelegate(this);
22380             }
22381             supr.setVisible.call(this, v, a, d, cb, e);
22382             if(v){
22383                 this.sync(true);
22384             }else if(!a){
22385                 this.hideAction();
22386             }
22387         }
22388     },
22389
22390     storeXY : function(xy){
22391         delete this.lastLT;
22392         this.lastXY = xy;
22393     },
22394
22395     storeLeftTop : function(left, top){
22396         delete this.lastXY;
22397         this.lastLT = [left, top];
22398     },
22399
22400     // private
22401     beforeFx : function(){
22402         this.beforeAction();
22403         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
22404     },
22405
22406     // private
22407     afterFx : function(){
22408         Roo.Layer.superclass.afterFx.apply(this, arguments);
22409         this.sync(this.isVisible());
22410     },
22411
22412     // private
22413     beforeAction : function(){
22414         if(!this.updating && this.shadow){
22415             this.shadow.hide();
22416         }
22417     },
22418
22419     // overridden Element method
22420     setLeft : function(left){
22421         this.storeLeftTop(left, this.getTop(true));
22422         supr.setLeft.apply(this, arguments);
22423         this.sync();
22424     },
22425
22426     setTop : function(top){
22427         this.storeLeftTop(this.getLeft(true), top);
22428         supr.setTop.apply(this, arguments);
22429         this.sync();
22430     },
22431
22432     setLeftTop : function(left, top){
22433         this.storeLeftTop(left, top);
22434         supr.setLeftTop.apply(this, arguments);
22435         this.sync();
22436     },
22437
22438     setXY : function(xy, a, d, c, e){
22439         this.fixDisplay();
22440         this.beforeAction();
22441         this.storeXY(xy);
22442         var cb = this.createCB(c);
22443         supr.setXY.call(this, xy, a, d, cb, e);
22444         if(!a){
22445             cb();
22446         }
22447     },
22448
22449     // private
22450     createCB : function(c){
22451         var el = this;
22452         return function(){
22453             el.constrainXY();
22454             el.sync(true);
22455             if(c){
22456                 c();
22457             }
22458         };
22459     },
22460
22461     // overridden Element method
22462     setX : function(x, a, d, c, e){
22463         this.setXY([x, this.getY()], a, d, c, e);
22464     },
22465
22466     // overridden Element method
22467     setY : function(y, a, d, c, e){
22468         this.setXY([this.getX(), y], a, d, c, e);
22469     },
22470
22471     // overridden Element method
22472     setSize : function(w, h, a, d, c, e){
22473         this.beforeAction();
22474         var cb = this.createCB(c);
22475         supr.setSize.call(this, w, h, a, d, cb, e);
22476         if(!a){
22477             cb();
22478         }
22479     },
22480
22481     // overridden Element method
22482     setWidth : function(w, a, d, c, e){
22483         this.beforeAction();
22484         var cb = this.createCB(c);
22485         supr.setWidth.call(this, w, a, d, cb, e);
22486         if(!a){
22487             cb();
22488         }
22489     },
22490
22491     // overridden Element method
22492     setHeight : function(h, a, d, c, e){
22493         this.beforeAction();
22494         var cb = this.createCB(c);
22495         supr.setHeight.call(this, h, a, d, cb, e);
22496         if(!a){
22497             cb();
22498         }
22499     },
22500
22501     // overridden Element method
22502     setBounds : function(x, y, w, h, a, d, c, e){
22503         this.beforeAction();
22504         var cb = this.createCB(c);
22505         if(!a){
22506             this.storeXY([x, y]);
22507             supr.setXY.call(this, [x, y]);
22508             supr.setSize.call(this, w, h, a, d, cb, e);
22509             cb();
22510         }else{
22511             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
22512         }
22513         return this;
22514     },
22515     
22516     /**
22517      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
22518      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
22519      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
22520      * @param {Number} zindex The new z-index to set
22521      * @return {this} The Layer
22522      */
22523     setZIndex : function(zindex){
22524         this.zindex = zindex;
22525         this.setStyle("z-index", zindex + 2);
22526         if(this.shadow){
22527             this.shadow.setZIndex(zindex + 1);
22528         }
22529         if(this.shim){
22530             this.shim.setStyle("z-index", zindex);
22531         }
22532     }
22533 });
22534 })();/*
22535  * Based on:
22536  * Ext JS Library 1.1.1
22537  * Copyright(c) 2006-2007, Ext JS, LLC.
22538  *
22539  * Originally Released Under LGPL - original licence link has changed is not relivant.
22540  *
22541  * Fork - LGPL
22542  * <script type="text/javascript">
22543  */
22544
22545
22546 /**
22547  * @class Roo.Shadow
22548  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
22549  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
22550  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
22551  * @constructor
22552  * Create a new Shadow
22553  * @param {Object} config The config object
22554  */
22555 Roo.Shadow = function(config){
22556     Roo.apply(this, config);
22557     if(typeof this.mode != "string"){
22558         this.mode = this.defaultMode;
22559     }
22560     var o = this.offset, a = {h: 0};
22561     var rad = Math.floor(this.offset/2);
22562     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
22563         case "drop":
22564             a.w = 0;
22565             a.l = a.t = o;
22566             a.t -= 1;
22567             if(Roo.isIE){
22568                 a.l -= this.offset + rad;
22569                 a.t -= this.offset + rad;
22570                 a.w -= rad;
22571                 a.h -= rad;
22572                 a.t += 1;
22573             }
22574         break;
22575         case "sides":
22576             a.w = (o*2);
22577             a.l = -o;
22578             a.t = o-1;
22579             if(Roo.isIE){
22580                 a.l -= (this.offset - rad);
22581                 a.t -= this.offset + rad;
22582                 a.l += 1;
22583                 a.w -= (this.offset - rad)*2;
22584                 a.w -= rad + 1;
22585                 a.h -= 1;
22586             }
22587         break;
22588         case "frame":
22589             a.w = a.h = (o*2);
22590             a.l = a.t = -o;
22591             a.t += 1;
22592             a.h -= 2;
22593             if(Roo.isIE){
22594                 a.l -= (this.offset - rad);
22595                 a.t -= (this.offset - rad);
22596                 a.l += 1;
22597                 a.w -= (this.offset + rad + 1);
22598                 a.h -= (this.offset + rad);
22599                 a.h += 1;
22600             }
22601         break;
22602     };
22603
22604     this.adjusts = a;
22605 };
22606
22607 Roo.Shadow.prototype = {
22608     /**
22609      * @cfg {String} mode
22610      * The shadow display mode.  Supports the following options:<br />
22611      * sides: Shadow displays on both sides and bottom only<br />
22612      * frame: Shadow displays equally on all four sides<br />
22613      * drop: Traditional bottom-right drop shadow (default)
22614      */
22615     /**
22616      * @cfg {String} offset
22617      * The number of pixels to offset the shadow from the element (defaults to 4)
22618      */
22619     offset: 4,
22620
22621     // private
22622     defaultMode: "drop",
22623
22624     /**
22625      * Displays the shadow under the target element
22626      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
22627      */
22628     show : function(target){
22629         target = Roo.get(target);
22630         if(!this.el){
22631             this.el = Roo.Shadow.Pool.pull();
22632             if(this.el.dom.nextSibling != target.dom){
22633                 this.el.insertBefore(target);
22634             }
22635         }
22636         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
22637         if(Roo.isIE){
22638             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
22639         }
22640         this.realign(
22641             target.getLeft(true),
22642             target.getTop(true),
22643             target.getWidth(),
22644             target.getHeight()
22645         );
22646         this.el.dom.style.display = "block";
22647     },
22648
22649     /**
22650      * Returns true if the shadow is visible, else false
22651      */
22652     isVisible : function(){
22653         return this.el ? true : false;  
22654     },
22655
22656     /**
22657      * Direct alignment when values are already available. Show must be called at least once before
22658      * calling this method to ensure it is initialized.
22659      * @param {Number} left The target element left position
22660      * @param {Number} top The target element top position
22661      * @param {Number} width The target element width
22662      * @param {Number} height The target element height
22663      */
22664     realign : function(l, t, w, h){
22665         if(!this.el){
22666             return;
22667         }
22668         var a = this.adjusts, d = this.el.dom, s = d.style;
22669         var iea = 0;
22670         s.left = (l+a.l)+"px";
22671         s.top = (t+a.t)+"px";
22672         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
22673  
22674         if(s.width != sws || s.height != shs){
22675             s.width = sws;
22676             s.height = shs;
22677             if(!Roo.isIE){
22678                 var cn = d.childNodes;
22679                 var sww = Math.max(0, (sw-12))+"px";
22680                 cn[0].childNodes[1].style.width = sww;
22681                 cn[1].childNodes[1].style.width = sww;
22682                 cn[2].childNodes[1].style.width = sww;
22683                 cn[1].style.height = Math.max(0, (sh-12))+"px";
22684             }
22685         }
22686     },
22687
22688     /**
22689      * Hides this shadow
22690      */
22691     hide : function(){
22692         if(this.el){
22693             this.el.dom.style.display = "none";
22694             Roo.Shadow.Pool.push(this.el);
22695             delete this.el;
22696         }
22697     },
22698
22699     /**
22700      * Adjust the z-index of this shadow
22701      * @param {Number} zindex The new z-index
22702      */
22703     setZIndex : function(z){
22704         this.zIndex = z;
22705         if(this.el){
22706             this.el.setStyle("z-index", z);
22707         }
22708     }
22709 };
22710
22711 // Private utility class that manages the internal Shadow cache
22712 Roo.Shadow.Pool = function(){
22713     var p = [];
22714     var markup = Roo.isIE ?
22715                  '<div class="x-ie-shadow"></div>' :
22716                  '<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>';
22717     return {
22718         pull : function(){
22719             var sh = p.shift();
22720             if(!sh){
22721                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
22722                 sh.autoBoxAdjust = false;
22723             }
22724             return sh;
22725         },
22726
22727         push : function(sh){
22728             p.push(sh);
22729         }
22730     };
22731 }();/*
22732  * Based on:
22733  * Ext JS Library 1.1.1
22734  * Copyright(c) 2006-2007, Ext JS, LLC.
22735  *
22736  * Originally Released Under LGPL - original licence link has changed is not relivant.
22737  *
22738  * Fork - LGPL
22739  * <script type="text/javascript">
22740  */
22741
22742 /**
22743  * @class Roo.BoxComponent
22744  * @extends Roo.Component
22745  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
22746  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
22747  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
22748  * layout containers.
22749  * @constructor
22750  * @param {Roo.Element/String/Object} config The configuration options.
22751  */
22752 Roo.BoxComponent = function(config){
22753     Roo.Component.call(this, config);
22754     this.addEvents({
22755         /**
22756          * @event resize
22757          * Fires after the component is resized.
22758              * @param {Roo.Component} this
22759              * @param {Number} adjWidth The box-adjusted width that was set
22760              * @param {Number} adjHeight The box-adjusted height that was set
22761              * @param {Number} rawWidth The width that was originally specified
22762              * @param {Number} rawHeight The height that was originally specified
22763              */
22764         resize : true,
22765         /**
22766          * @event move
22767          * Fires after the component is moved.
22768              * @param {Roo.Component} this
22769              * @param {Number} x The new x position
22770              * @param {Number} y The new y position
22771              */
22772         move : true
22773     });
22774 };
22775
22776 Roo.extend(Roo.BoxComponent, Roo.Component, {
22777     // private, set in afterRender to signify that the component has been rendered
22778     boxReady : false,
22779     // private, used to defer height settings to subclasses
22780     deferHeight: false,
22781     /** @cfg {Number} width
22782      * width (optional) size of component
22783      */
22784      /** @cfg {Number} height
22785      * height (optional) size of component
22786      */
22787      
22788     /**
22789      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
22790      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
22791      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
22792      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
22793      * @return {Roo.BoxComponent} this
22794      */
22795     setSize : function(w, h){
22796         // support for standard size objects
22797         if(typeof w == 'object'){
22798             h = w.height;
22799             w = w.width;
22800         }
22801         // not rendered
22802         if(!this.boxReady){
22803             this.width = w;
22804             this.height = h;
22805             return this;
22806         }
22807
22808         // prevent recalcs when not needed
22809         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
22810             return this;
22811         }
22812         this.lastSize = {width: w, height: h};
22813
22814         var adj = this.adjustSize(w, h);
22815         var aw = adj.width, ah = adj.height;
22816         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
22817             var rz = this.getResizeEl();
22818             if(!this.deferHeight && aw !== undefined && ah !== undefined){
22819                 rz.setSize(aw, ah);
22820             }else if(!this.deferHeight && ah !== undefined){
22821                 rz.setHeight(ah);
22822             }else if(aw !== undefined){
22823                 rz.setWidth(aw);
22824             }
22825             this.onResize(aw, ah, w, h);
22826             this.fireEvent('resize', this, aw, ah, w, h);
22827         }
22828         return this;
22829     },
22830
22831     /**
22832      * Gets the current size of the component's underlying element.
22833      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
22834      */
22835     getSize : function(){
22836         return this.el.getSize();
22837     },
22838
22839     /**
22840      * Gets the current XY position of the component's underlying element.
22841      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22842      * @return {Array} The XY position of the element (e.g., [100, 200])
22843      */
22844     getPosition : function(local){
22845         if(local === true){
22846             return [this.el.getLeft(true), this.el.getTop(true)];
22847         }
22848         return this.xy || this.el.getXY();
22849     },
22850
22851     /**
22852      * Gets the current box measurements of the component's underlying element.
22853      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
22854      * @returns {Object} box An object in the format {x, y, width, height}
22855      */
22856     getBox : function(local){
22857         var s = this.el.getSize();
22858         if(local){
22859             s.x = this.el.getLeft(true);
22860             s.y = this.el.getTop(true);
22861         }else{
22862             var xy = this.xy || this.el.getXY();
22863             s.x = xy[0];
22864             s.y = xy[1];
22865         }
22866         return s;
22867     },
22868
22869     /**
22870      * Sets the current box measurements of the component's underlying element.
22871      * @param {Object} box An object in the format {x, y, width, height}
22872      * @returns {Roo.BoxComponent} this
22873      */
22874     updateBox : function(box){
22875         this.setSize(box.width, box.height);
22876         this.setPagePosition(box.x, box.y);
22877         return this;
22878     },
22879
22880     // protected
22881     getResizeEl : function(){
22882         return this.resizeEl || this.el;
22883     },
22884
22885     // protected
22886     getPositionEl : function(){
22887         return this.positionEl || this.el;
22888     },
22889
22890     /**
22891      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
22892      * This method fires the move event.
22893      * @param {Number} left The new left
22894      * @param {Number} top The new top
22895      * @returns {Roo.BoxComponent} this
22896      */
22897     setPosition : function(x, y){
22898         this.x = x;
22899         this.y = y;
22900         if(!this.boxReady){
22901             return this;
22902         }
22903         var adj = this.adjustPosition(x, y);
22904         var ax = adj.x, ay = adj.y;
22905
22906         var el = this.getPositionEl();
22907         if(ax !== undefined || ay !== undefined){
22908             if(ax !== undefined && ay !== undefined){
22909                 el.setLeftTop(ax, ay);
22910             }else if(ax !== undefined){
22911                 el.setLeft(ax);
22912             }else if(ay !== undefined){
22913                 el.setTop(ay);
22914             }
22915             this.onPosition(ax, ay);
22916             this.fireEvent('move', this, ax, ay);
22917         }
22918         return this;
22919     },
22920
22921     /**
22922      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
22923      * This method fires the move event.
22924      * @param {Number} x The new x position
22925      * @param {Number} y The new y position
22926      * @returns {Roo.BoxComponent} this
22927      */
22928     setPagePosition : function(x, y){
22929         this.pageX = x;
22930         this.pageY = y;
22931         if(!this.boxReady){
22932             return;
22933         }
22934         if(x === undefined || y === undefined){ // cannot translate undefined points
22935             return;
22936         }
22937         var p = this.el.translatePoints(x, y);
22938         this.setPosition(p.left, p.top);
22939         return this;
22940     },
22941
22942     // private
22943     onRender : function(ct, position){
22944         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
22945         if(this.resizeEl){
22946             this.resizeEl = Roo.get(this.resizeEl);
22947         }
22948         if(this.positionEl){
22949             this.positionEl = Roo.get(this.positionEl);
22950         }
22951     },
22952
22953     // private
22954     afterRender : function(){
22955         Roo.BoxComponent.superclass.afterRender.call(this);
22956         this.boxReady = true;
22957         this.setSize(this.width, this.height);
22958         if(this.x || this.y){
22959             this.setPosition(this.x, this.y);
22960         }
22961         if(this.pageX || this.pageY){
22962             this.setPagePosition(this.pageX, this.pageY);
22963         }
22964     },
22965
22966     /**
22967      * Force the component's size to recalculate based on the underlying element's current height and width.
22968      * @returns {Roo.BoxComponent} this
22969      */
22970     syncSize : function(){
22971         delete this.lastSize;
22972         this.setSize(this.el.getWidth(), this.el.getHeight());
22973         return this;
22974     },
22975
22976     /**
22977      * Called after the component is resized, this method is empty by default but can be implemented by any
22978      * subclass that needs to perform custom logic after a resize occurs.
22979      * @param {Number} adjWidth The box-adjusted width that was set
22980      * @param {Number} adjHeight The box-adjusted height that was set
22981      * @param {Number} rawWidth The width that was originally specified
22982      * @param {Number} rawHeight The height that was originally specified
22983      */
22984     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
22985
22986     },
22987
22988     /**
22989      * Called after the component is moved, this method is empty by default but can be implemented by any
22990      * subclass that needs to perform custom logic after a move occurs.
22991      * @param {Number} x The new x position
22992      * @param {Number} y The new y position
22993      */
22994     onPosition : function(x, y){
22995
22996     },
22997
22998     // private
22999     adjustSize : function(w, h){
23000         if(this.autoWidth){
23001             w = 'auto';
23002         }
23003         if(this.autoHeight){
23004             h = 'auto';
23005         }
23006         return {width : w, height: h};
23007     },
23008
23009     // private
23010     adjustPosition : function(x, y){
23011         return {x : x, y: y};
23012     }
23013 });/*
23014  * Based on:
23015  * Ext JS Library 1.1.1
23016  * Copyright(c) 2006-2007, Ext JS, LLC.
23017  *
23018  * Originally Released Under LGPL - original licence link has changed is not relivant.
23019  *
23020  * Fork - LGPL
23021  * <script type="text/javascript">
23022  */
23023
23024
23025 /**
23026  * @class Roo.SplitBar
23027  * @extends Roo.util.Observable
23028  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
23029  * <br><br>
23030  * Usage:
23031  * <pre><code>
23032 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
23033                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
23034 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
23035 split.minSize = 100;
23036 split.maxSize = 600;
23037 split.animate = true;
23038 split.on('moved', splitterMoved);
23039 </code></pre>
23040  * @constructor
23041  * Create a new SplitBar
23042  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
23043  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
23044  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23045  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
23046                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
23047                         position of the SplitBar).
23048  */
23049 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
23050     
23051     /** @private */
23052     this.el = Roo.get(dragElement, true);
23053     this.el.dom.unselectable = "on";
23054     /** @private */
23055     this.resizingEl = Roo.get(resizingElement, true);
23056
23057     /**
23058      * @private
23059      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
23060      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
23061      * @type Number
23062      */
23063     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
23064     
23065     /**
23066      * The minimum size of the resizing element. (Defaults to 0)
23067      * @type Number
23068      */
23069     this.minSize = 0;
23070     
23071     /**
23072      * The maximum size of the resizing element. (Defaults to 2000)
23073      * @type Number
23074      */
23075     this.maxSize = 2000;
23076     
23077     /**
23078      * Whether to animate the transition to the new size
23079      * @type Boolean
23080      */
23081     this.animate = false;
23082     
23083     /**
23084      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
23085      * @type Boolean
23086      */
23087     this.useShim = false;
23088     
23089     /** @private */
23090     this.shim = null;
23091     
23092     if(!existingProxy){
23093         /** @private */
23094         this.proxy = Roo.SplitBar.createProxy(this.orientation);
23095     }else{
23096         this.proxy = Roo.get(existingProxy).dom;
23097     }
23098     /** @private */
23099     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
23100     
23101     /** @private */
23102     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
23103     
23104     /** @private */
23105     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
23106     
23107     /** @private */
23108     this.dragSpecs = {};
23109     
23110     /**
23111      * @private The adapter to use to positon and resize elements
23112      */
23113     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
23114     this.adapter.init(this);
23115     
23116     if(this.orientation == Roo.SplitBar.HORIZONTAL){
23117         /** @private */
23118         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
23119         this.el.addClass("x-splitbar-h");
23120     }else{
23121         /** @private */
23122         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
23123         this.el.addClass("x-splitbar-v");
23124     }
23125     
23126     this.addEvents({
23127         /**
23128          * @event resize
23129          * Fires when the splitter is moved (alias for {@link #event-moved})
23130          * @param {Roo.SplitBar} this
23131          * @param {Number} newSize the new width or height
23132          */
23133         "resize" : true,
23134         /**
23135          * @event moved
23136          * Fires when the splitter is moved
23137          * @param {Roo.SplitBar} this
23138          * @param {Number} newSize the new width or height
23139          */
23140         "moved" : true,
23141         /**
23142          * @event beforeresize
23143          * Fires before the splitter is dragged
23144          * @param {Roo.SplitBar} this
23145          */
23146         "beforeresize" : true,
23147
23148         "beforeapply" : true
23149     });
23150
23151     Roo.util.Observable.call(this);
23152 };
23153
23154 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
23155     onStartProxyDrag : function(x, y){
23156         this.fireEvent("beforeresize", this);
23157         if(!this.overlay){
23158             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
23159             o.unselectable();
23160             o.enableDisplayMode("block");
23161             // all splitbars share the same overlay
23162             Roo.SplitBar.prototype.overlay = o;
23163         }
23164         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
23165         this.overlay.show();
23166         Roo.get(this.proxy).setDisplayed("block");
23167         var size = this.adapter.getElementSize(this);
23168         this.activeMinSize = this.getMinimumSize();;
23169         this.activeMaxSize = this.getMaximumSize();;
23170         var c1 = size - this.activeMinSize;
23171         var c2 = Math.max(this.activeMaxSize - size, 0);
23172         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23173             this.dd.resetConstraints();
23174             this.dd.setXConstraint(
23175                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
23176                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
23177             );
23178             this.dd.setYConstraint(0, 0);
23179         }else{
23180             this.dd.resetConstraints();
23181             this.dd.setXConstraint(0, 0);
23182             this.dd.setYConstraint(
23183                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
23184                 this.placement == Roo.SplitBar.TOP ? c2 : c1
23185             );
23186          }
23187         this.dragSpecs.startSize = size;
23188         this.dragSpecs.startPoint = [x, y];
23189         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
23190     },
23191     
23192     /** 
23193      * @private Called after the drag operation by the DDProxy
23194      */
23195     onEndProxyDrag : function(e){
23196         Roo.get(this.proxy).setDisplayed(false);
23197         var endPoint = Roo.lib.Event.getXY(e);
23198         if(this.overlay){
23199             this.overlay.hide();
23200         }
23201         var newSize;
23202         if(this.orientation == Roo.SplitBar.HORIZONTAL){
23203             newSize = this.dragSpecs.startSize + 
23204                 (this.placement == Roo.SplitBar.LEFT ?
23205                     endPoint[0] - this.dragSpecs.startPoint[0] :
23206                     this.dragSpecs.startPoint[0] - endPoint[0]
23207                 );
23208         }else{
23209             newSize = this.dragSpecs.startSize + 
23210                 (this.placement == Roo.SplitBar.TOP ?
23211                     endPoint[1] - this.dragSpecs.startPoint[1] :
23212                     this.dragSpecs.startPoint[1] - endPoint[1]
23213                 );
23214         }
23215         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
23216         if(newSize != this.dragSpecs.startSize){
23217             if(this.fireEvent('beforeapply', this, newSize) !== false){
23218                 this.adapter.setElementSize(this, newSize);
23219                 this.fireEvent("moved", this, newSize);
23220                 this.fireEvent("resize", this, newSize);
23221             }
23222         }
23223     },
23224     
23225     /**
23226      * Get the adapter this SplitBar uses
23227      * @return The adapter object
23228      */
23229     getAdapter : function(){
23230         return this.adapter;
23231     },
23232     
23233     /**
23234      * Set the adapter this SplitBar uses
23235      * @param {Object} adapter A SplitBar adapter object
23236      */
23237     setAdapter : function(adapter){
23238         this.adapter = adapter;
23239         this.adapter.init(this);
23240     },
23241     
23242     /**
23243      * Gets the minimum size for the resizing element
23244      * @return {Number} The minimum size
23245      */
23246     getMinimumSize : function(){
23247         return this.minSize;
23248     },
23249     
23250     /**
23251      * Sets the minimum size for the resizing element
23252      * @param {Number} minSize The minimum size
23253      */
23254     setMinimumSize : function(minSize){
23255         this.minSize = minSize;
23256     },
23257     
23258     /**
23259      * Gets the maximum size for the resizing element
23260      * @return {Number} The maximum size
23261      */
23262     getMaximumSize : function(){
23263         return this.maxSize;
23264     },
23265     
23266     /**
23267      * Sets the maximum size for the resizing element
23268      * @param {Number} maxSize The maximum size
23269      */
23270     setMaximumSize : function(maxSize){
23271         this.maxSize = maxSize;
23272     },
23273     
23274     /**
23275      * Sets the initialize size for the resizing element
23276      * @param {Number} size The initial size
23277      */
23278     setCurrentSize : function(size){
23279         var oldAnimate = this.animate;
23280         this.animate = false;
23281         this.adapter.setElementSize(this, size);
23282         this.animate = oldAnimate;
23283     },
23284     
23285     /**
23286      * Destroy this splitbar. 
23287      * @param {Boolean} removeEl True to remove the element
23288      */
23289     destroy : function(removeEl){
23290         if(this.shim){
23291             this.shim.remove();
23292         }
23293         this.dd.unreg();
23294         this.proxy.parentNode.removeChild(this.proxy);
23295         if(removeEl){
23296             this.el.remove();
23297         }
23298     }
23299 });
23300
23301 /**
23302  * @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.
23303  */
23304 Roo.SplitBar.createProxy = function(dir){
23305     var proxy = new Roo.Element(document.createElement("div"));
23306     proxy.unselectable();
23307     var cls = 'x-splitbar-proxy';
23308     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
23309     document.body.appendChild(proxy.dom);
23310     return proxy.dom;
23311 };
23312
23313 /** 
23314  * @class Roo.SplitBar.BasicLayoutAdapter
23315  * Default Adapter. It assumes the splitter and resizing element are not positioned
23316  * elements and only gets/sets the width of the element. Generally used for table based layouts.
23317  */
23318 Roo.SplitBar.BasicLayoutAdapter = function(){
23319 };
23320
23321 Roo.SplitBar.BasicLayoutAdapter.prototype = {
23322     // do nothing for now
23323     init : function(s){
23324     
23325     },
23326     /**
23327      * Called before drag operations to get the current size of the resizing element. 
23328      * @param {Roo.SplitBar} s The SplitBar using this adapter
23329      */
23330      getElementSize : function(s){
23331         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23332             return s.resizingEl.getWidth();
23333         }else{
23334             return s.resizingEl.getHeight();
23335         }
23336     },
23337     
23338     /**
23339      * Called after drag operations to set the size of the resizing element.
23340      * @param {Roo.SplitBar} s The SplitBar using this adapter
23341      * @param {Number} newSize The new size to set
23342      * @param {Function} onComplete A function to be invoked when resizing is complete
23343      */
23344     setElementSize : function(s, newSize, onComplete){
23345         if(s.orientation == Roo.SplitBar.HORIZONTAL){
23346             if(!s.animate){
23347                 s.resizingEl.setWidth(newSize);
23348                 if(onComplete){
23349                     onComplete(s, newSize);
23350                 }
23351             }else{
23352                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
23353             }
23354         }else{
23355             
23356             if(!s.animate){
23357                 s.resizingEl.setHeight(newSize);
23358                 if(onComplete){
23359                     onComplete(s, newSize);
23360                 }
23361             }else{
23362                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
23363             }
23364         }
23365     }
23366 };
23367
23368 /** 
23369  *@class Roo.SplitBar.AbsoluteLayoutAdapter
23370  * @extends Roo.SplitBar.BasicLayoutAdapter
23371  * Adapter that  moves the splitter element to align with the resized sizing element. 
23372  * Used with an absolute positioned SplitBar.
23373  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
23374  * document.body, make sure you assign an id to the body element.
23375  */
23376 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
23377     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
23378     this.container = Roo.get(container);
23379 };
23380
23381 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
23382     init : function(s){
23383         this.basic.init(s);
23384     },
23385     
23386     getElementSize : function(s){
23387         return this.basic.getElementSize(s);
23388     },
23389     
23390     setElementSize : function(s, newSize, onComplete){
23391         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
23392     },
23393     
23394     moveSplitter : function(s){
23395         var yes = Roo.SplitBar;
23396         switch(s.placement){
23397             case yes.LEFT:
23398                 s.el.setX(s.resizingEl.getRight());
23399                 break;
23400             case yes.RIGHT:
23401                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
23402                 break;
23403             case yes.TOP:
23404                 s.el.setY(s.resizingEl.getBottom());
23405                 break;
23406             case yes.BOTTOM:
23407                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
23408                 break;
23409         }
23410     }
23411 };
23412
23413 /**
23414  * Orientation constant - Create a vertical SplitBar
23415  * @static
23416  * @type Number
23417  */
23418 Roo.SplitBar.VERTICAL = 1;
23419
23420 /**
23421  * Orientation constant - Create a horizontal SplitBar
23422  * @static
23423  * @type Number
23424  */
23425 Roo.SplitBar.HORIZONTAL = 2;
23426
23427 /**
23428  * Placement constant - The resizing element is to the left of the splitter element
23429  * @static
23430  * @type Number
23431  */
23432 Roo.SplitBar.LEFT = 1;
23433
23434 /**
23435  * Placement constant - The resizing element is to the right of the splitter element
23436  * @static
23437  * @type Number
23438  */
23439 Roo.SplitBar.RIGHT = 2;
23440
23441 /**
23442  * Placement constant - The resizing element is positioned above the splitter element
23443  * @static
23444  * @type Number
23445  */
23446 Roo.SplitBar.TOP = 3;
23447
23448 /**
23449  * Placement constant - The resizing element is positioned under splitter element
23450  * @static
23451  * @type Number
23452  */
23453 Roo.SplitBar.BOTTOM = 4;
23454 /*
23455  * Based on:
23456  * Ext JS Library 1.1.1
23457  * Copyright(c) 2006-2007, Ext JS, LLC.
23458  *
23459  * Originally Released Under LGPL - original licence link has changed is not relivant.
23460  *
23461  * Fork - LGPL
23462  * <script type="text/javascript">
23463  */
23464
23465 /**
23466  * @class Roo.View
23467  * @extends Roo.util.Observable
23468  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
23469  * This class also supports single and multi selection modes. <br>
23470  * Create a data model bound view:
23471  <pre><code>
23472  var store = new Roo.data.Store(...);
23473
23474  var view = new Roo.View({
23475     el : "my-element",
23476     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
23477  
23478     singleSelect: true,
23479     selectedClass: "ydataview-selected",
23480     store: store
23481  });
23482
23483  // listen for node click?
23484  view.on("click", function(vw, index, node, e){
23485  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23486  });
23487
23488  // load XML data
23489  dataModel.load("foobar.xml");
23490  </code></pre>
23491  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
23492  * <br><br>
23493  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
23494  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
23495  * 
23496  * Note: old style constructor is still suported (container, template, config)
23497  * 
23498  * @constructor
23499  * Create a new View
23500  * @param {Object} config The config object
23501  * 
23502  */
23503 Roo.View = function(config, depreciated_tpl, depreciated_config){
23504     
23505     if (typeof(depreciated_tpl) == 'undefined') {
23506         // new way.. - universal constructor.
23507         Roo.apply(this, config);
23508         this.el  = Roo.get(this.el);
23509     } else {
23510         // old format..
23511         this.el  = Roo.get(config);
23512         this.tpl = depreciated_tpl;
23513         Roo.apply(this, depreciated_config);
23514     }
23515      
23516     
23517     if(typeof(this.tpl) == "string"){
23518         this.tpl = new Roo.Template(this.tpl);
23519     } else {
23520         // support xtype ctors..
23521         this.tpl = new Roo.factory(this.tpl, Roo);
23522     }
23523     
23524     
23525     this.tpl.compile();
23526    
23527
23528      
23529     /** @private */
23530     this.addEvents({
23531     /**
23532      * @event beforeclick
23533      * Fires before a click is processed. Returns false to cancel the default action.
23534      * @param {Roo.View} this
23535      * @param {Number} index The index of the target node
23536      * @param {HTMLElement} node The target node
23537      * @param {Roo.EventObject} e The raw event object
23538      */
23539         "beforeclick" : true,
23540     /**
23541      * @event click
23542      * Fires when a template node is clicked.
23543      * @param {Roo.View} this
23544      * @param {Number} index The index of the target node
23545      * @param {HTMLElement} node The target node
23546      * @param {Roo.EventObject} e The raw event object
23547      */
23548         "click" : true,
23549     /**
23550      * @event dblclick
23551      * Fires when a template node is double clicked.
23552      * @param {Roo.View} this
23553      * @param {Number} index The index of the target node
23554      * @param {HTMLElement} node The target node
23555      * @param {Roo.EventObject} e The raw event object
23556      */
23557         "dblclick" : true,
23558     /**
23559      * @event contextmenu
23560      * Fires when a template node is right clicked.
23561      * @param {Roo.View} this
23562      * @param {Number} index The index of the target node
23563      * @param {HTMLElement} node The target node
23564      * @param {Roo.EventObject} e The raw event object
23565      */
23566         "contextmenu" : true,
23567     /**
23568      * @event selectionchange
23569      * Fires when the selected nodes change.
23570      * @param {Roo.View} this
23571      * @param {Array} selections Array of the selected nodes
23572      */
23573         "selectionchange" : true,
23574
23575     /**
23576      * @event beforeselect
23577      * Fires before a selection is made. If any handlers return false, the selection is cancelled.
23578      * @param {Roo.View} this
23579      * @param {HTMLElement} node The node to be selected
23580      * @param {Array} selections Array of currently selected nodes
23581      */
23582         "beforeselect" : true
23583     });
23584
23585     this.el.on({
23586         "click": this.onClick,
23587         "dblclick": this.onDblClick,
23588         "contextmenu": this.onContextMenu,
23589         scope:this
23590     });
23591
23592     this.selections = [];
23593     this.nodes = [];
23594     this.cmp = new Roo.CompositeElementLite([]);
23595     if(this.store){
23596         this.store = Roo.factory(this.store, Roo.data);
23597         this.setStore(this.store, true);
23598     }
23599     Roo.View.superclass.constructor.call(this);
23600 };
23601
23602 Roo.extend(Roo.View, Roo.util.Observable, {
23603     
23604      /**
23605      * @cfg {Roo.data.Store} store Data store to load data from.
23606      */
23607     store : false,
23608     
23609     /**
23610      * @cfg {String|Roo.Element} el The container element.
23611      */
23612     el : '',
23613     
23614     /**
23615      * @cfg {String|Roo.Template} tpl The template used by this View 
23616      */
23617     tpl : false,
23618     
23619     /**
23620      * @cfg {String} selectedClass The css class to add to selected nodes
23621      */
23622     selectedClass : "x-view-selected",
23623      /**
23624      * @cfg {String} emptyText The empty text to show when nothing is loaded.
23625      */
23626     emptyText : "",
23627     /**
23628      * @cfg {Boolean} multiSelect Allow multiple selection
23629      */
23630     
23631     multiSelect : false,
23632     /**
23633      * @cfg {Boolean} singleSelect Allow single selection
23634      */
23635     singleSelect:  false,
23636     
23637     /**
23638      * Returns the element this view is bound to.
23639      * @return {Roo.Element}
23640      */
23641     getEl : function(){
23642         return this.el;
23643     },
23644
23645     /**
23646      * Refreshes the view.
23647      */
23648     refresh : function(){
23649         var t = this.tpl;
23650         this.clearSelections();
23651         this.el.update("");
23652         var html = [];
23653         var records = this.store.getRange();
23654         if(records.length < 1){
23655             this.el.update(this.emptyText);
23656             return;
23657         }
23658         for(var i = 0, len = records.length; i < len; i++){
23659             var data = this.prepareData(records[i].data, i, records[i]);
23660             html[html.length] = t.apply(data);
23661         }
23662         this.el.update(html.join(""));
23663         this.nodes = this.el.dom.childNodes;
23664         this.updateIndexes(0);
23665     },
23666
23667     /**
23668      * Function to override to reformat the data that is sent to
23669      * the template for each node.
23670      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
23671      * a JSON object for an UpdateManager bound view).
23672      */
23673     prepareData : function(data){
23674         return data;
23675     },
23676
23677     onUpdate : function(ds, record){
23678         this.clearSelections();
23679         var index = this.store.indexOf(record);
23680         var n = this.nodes[index];
23681         this.tpl.insertBefore(n, this.prepareData(record.data));
23682         n.parentNode.removeChild(n);
23683         this.updateIndexes(index, index);
23684     },
23685
23686     onAdd : function(ds, records, index){
23687         this.clearSelections();
23688         if(this.nodes.length == 0){
23689             this.refresh();
23690             return;
23691         }
23692         var n = this.nodes[index];
23693         for(var i = 0, len = records.length; i < len; i++){
23694             var d = this.prepareData(records[i].data);
23695             if(n){
23696                 this.tpl.insertBefore(n, d);
23697             }else{
23698                 this.tpl.append(this.el, d);
23699             }
23700         }
23701         this.updateIndexes(index);
23702     },
23703
23704     onRemove : function(ds, record, index){
23705         this.clearSelections();
23706         this.el.dom.removeChild(this.nodes[index]);
23707         this.updateIndexes(index);
23708     },
23709
23710     /**
23711      * Refresh an individual node.
23712      * @param {Number} index
23713      */
23714     refreshNode : function(index){
23715         this.onUpdate(this.store, this.store.getAt(index));
23716     },
23717
23718     updateIndexes : function(startIndex, endIndex){
23719         var ns = this.nodes;
23720         startIndex = startIndex || 0;
23721         endIndex = endIndex || ns.length - 1;
23722         for(var i = startIndex; i <= endIndex; i++){
23723             ns[i].nodeIndex = i;
23724         }
23725     },
23726
23727     /**
23728      * Changes the data store this view uses and refresh the view.
23729      * @param {Store} store
23730      */
23731     setStore : function(store, initial){
23732         if(!initial && this.store){
23733             this.store.un("datachanged", this.refresh);
23734             this.store.un("add", this.onAdd);
23735             this.store.un("remove", this.onRemove);
23736             this.store.un("update", this.onUpdate);
23737             this.store.un("clear", this.refresh);
23738         }
23739         if(store){
23740           
23741             store.on("datachanged", this.refresh, this);
23742             store.on("add", this.onAdd, this);
23743             store.on("remove", this.onRemove, this);
23744             store.on("update", this.onUpdate, this);
23745             store.on("clear", this.refresh, this);
23746         }
23747         
23748         if(store){
23749             this.refresh();
23750         }
23751     },
23752
23753     /**
23754      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
23755      * @param {HTMLElement} node
23756      * @return {HTMLElement} The template node
23757      */
23758     findItemFromChild : function(node){
23759         var el = this.el.dom;
23760         if(!node || node.parentNode == el){
23761                     return node;
23762             }
23763             var p = node.parentNode;
23764             while(p && p != el){
23765             if(p.parentNode == el){
23766                 return p;
23767             }
23768             p = p.parentNode;
23769         }
23770             return null;
23771     },
23772
23773     /** @ignore */
23774     onClick : function(e){
23775         var item = this.findItemFromChild(e.getTarget());
23776         if(item){
23777             var index = this.indexOf(item);
23778             if(this.onItemClick(item, index, e) !== false){
23779                 this.fireEvent("click", this, index, item, e);
23780             }
23781         }else{
23782             this.clearSelections();
23783         }
23784     },
23785
23786     /** @ignore */
23787     onContextMenu : function(e){
23788         var item = this.findItemFromChild(e.getTarget());
23789         if(item){
23790             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
23791         }
23792     },
23793
23794     /** @ignore */
23795     onDblClick : function(e){
23796         var item = this.findItemFromChild(e.getTarget());
23797         if(item){
23798             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
23799         }
23800     },
23801
23802     onItemClick : function(item, index, e){
23803         if(this.fireEvent("beforeclick", this, index, item, e) === false){
23804             return false;
23805         }
23806         if(this.multiSelect || this.singleSelect){
23807             if(this.multiSelect && e.shiftKey && this.lastSelection){
23808                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
23809             }else{
23810                 this.select(item, this.multiSelect && e.ctrlKey);
23811                 this.lastSelection = item;
23812             }
23813             e.preventDefault();
23814         }
23815         return true;
23816     },
23817
23818     /**
23819      * Get the number of selected nodes.
23820      * @return {Number}
23821      */
23822     getSelectionCount : function(){
23823         return this.selections.length;
23824     },
23825
23826     /**
23827      * Get the currently selected nodes.
23828      * @return {Array} An array of HTMLElements
23829      */
23830     getSelectedNodes : function(){
23831         return this.selections;
23832     },
23833
23834     /**
23835      * Get the indexes of the selected nodes.
23836      * @return {Array}
23837      */
23838     getSelectedIndexes : function(){
23839         var indexes = [], s = this.selections;
23840         for(var i = 0, len = s.length; i < len; i++){
23841             indexes.push(s[i].nodeIndex);
23842         }
23843         return indexes;
23844     },
23845
23846     /**
23847      * Clear all selections
23848      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
23849      */
23850     clearSelections : function(suppressEvent){
23851         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
23852             this.cmp.elements = this.selections;
23853             this.cmp.removeClass(this.selectedClass);
23854             this.selections = [];
23855             if(!suppressEvent){
23856                 this.fireEvent("selectionchange", this, this.selections);
23857             }
23858         }
23859     },
23860
23861     /**
23862      * Returns true if the passed node is selected
23863      * @param {HTMLElement/Number} node The node or node index
23864      * @return {Boolean}
23865      */
23866     isSelected : function(node){
23867         var s = this.selections;
23868         if(s.length < 1){
23869             return false;
23870         }
23871         node = this.getNode(node);
23872         return s.indexOf(node) !== -1;
23873     },
23874
23875     /**
23876      * Selects nodes.
23877      * @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
23878      * @param {Boolean} keepExisting (optional) true to keep existing selections
23879      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
23880      */
23881     select : function(nodeInfo, keepExisting, suppressEvent){
23882         if(nodeInfo instanceof Array){
23883             if(!keepExisting){
23884                 this.clearSelections(true);
23885             }
23886             for(var i = 0, len = nodeInfo.length; i < len; i++){
23887                 this.select(nodeInfo[i], true, true);
23888             }
23889         } else{
23890             var node = this.getNode(nodeInfo);
23891             if(node && !this.isSelected(node)){
23892                 if(!keepExisting){
23893                     this.clearSelections(true);
23894                 }
23895                 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
23896                     Roo.fly(node).addClass(this.selectedClass);
23897                     this.selections.push(node);
23898                     if(!suppressEvent){
23899                         this.fireEvent("selectionchange", this, this.selections);
23900                     }
23901                 }
23902             }
23903         }
23904     },
23905
23906     /**
23907      * Gets a template node.
23908      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23909      * @return {HTMLElement} The node or null if it wasn't found
23910      */
23911     getNode : function(nodeInfo){
23912         if(typeof nodeInfo == "string"){
23913             return document.getElementById(nodeInfo);
23914         }else if(typeof nodeInfo == "number"){
23915             return this.nodes[nodeInfo];
23916         }
23917         return nodeInfo;
23918     },
23919
23920     /**
23921      * Gets a range template nodes.
23922      * @param {Number} startIndex
23923      * @param {Number} endIndex
23924      * @return {Array} An array of nodes
23925      */
23926     getNodes : function(start, end){
23927         var ns = this.nodes;
23928         start = start || 0;
23929         end = typeof end == "undefined" ? ns.length - 1 : end;
23930         var nodes = [];
23931         if(start <= end){
23932             for(var i = start; i <= end; i++){
23933                 nodes.push(ns[i]);
23934             }
23935         } else{
23936             for(var i = start; i >= end; i--){
23937                 nodes.push(ns[i]);
23938             }
23939         }
23940         return nodes;
23941     },
23942
23943     /**
23944      * Finds the index of the passed node
23945      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
23946      * @return {Number} The index of the node or -1
23947      */
23948     indexOf : function(node){
23949         node = this.getNode(node);
23950         if(typeof node.nodeIndex == "number"){
23951             return node.nodeIndex;
23952         }
23953         var ns = this.nodes;
23954         for(var i = 0, len = ns.length; i < len; i++){
23955             if(ns[i] == node){
23956                 return i;
23957             }
23958         }
23959         return -1;
23960     }
23961 });
23962 /*
23963  * Based on:
23964  * Ext JS Library 1.1.1
23965  * Copyright(c) 2006-2007, Ext JS, LLC.
23966  *
23967  * Originally Released Under LGPL - original licence link has changed is not relivant.
23968  *
23969  * Fork - LGPL
23970  * <script type="text/javascript">
23971  */
23972
23973 /**
23974  * @class Roo.JsonView
23975  * @extends Roo.View
23976  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
23977 <pre><code>
23978 var view = new Roo.JsonView({
23979     container: "my-element",
23980     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
23981     multiSelect: true, 
23982     jsonRoot: "data" 
23983 });
23984
23985 // listen for node click?
23986 view.on("click", function(vw, index, node, e){
23987     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
23988 });
23989
23990 // direct load of JSON data
23991 view.load("foobar.php");
23992
23993 // Example from my blog list
23994 var tpl = new Roo.Template(
23995     '&lt;div class="entry"&gt;' +
23996     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
23997     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
23998     "&lt;/div&gt;&lt;hr /&gt;"
23999 );
24000
24001 var moreView = new Roo.JsonView({
24002     container :  "entry-list", 
24003     template : tpl,
24004     jsonRoot: "posts"
24005 });
24006 moreView.on("beforerender", this.sortEntries, this);
24007 moreView.load({
24008     url: "/blog/get-posts.php",
24009     params: "allposts=true",
24010     text: "Loading Blog Entries..."
24011 });
24012 </code></pre>
24013
24014 * Note: old code is supported with arguments : (container, template, config)
24015
24016
24017  * @constructor
24018  * Create a new JsonView
24019  * 
24020  * @param {Object} config The config object
24021  * 
24022  */
24023 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
24024     
24025     
24026     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
24027
24028     var um = this.el.getUpdateManager();
24029     um.setRenderer(this);
24030     um.on("update", this.onLoad, this);
24031     um.on("failure", this.onLoadException, this);
24032
24033     /**
24034      * @event beforerender
24035      * Fires before rendering of the downloaded JSON data.
24036      * @param {Roo.JsonView} this
24037      * @param {Object} data The JSON data loaded
24038      */
24039     /**
24040      * @event load
24041      * Fires when data is loaded.
24042      * @param {Roo.JsonView} this
24043      * @param {Object} data The JSON data loaded
24044      * @param {Object} response The raw Connect response object
24045      */
24046     /**
24047      * @event loadexception
24048      * Fires when loading fails.
24049      * @param {Roo.JsonView} this
24050      * @param {Object} response The raw Connect response object
24051      */
24052     this.addEvents({
24053         'beforerender' : true,
24054         'load' : true,
24055         'loadexception' : true
24056     });
24057 };
24058 Roo.extend(Roo.JsonView, Roo.View, {
24059     /**
24060      * @type {String} The root property in the loaded JSON object that contains the data
24061      */
24062     jsonRoot : "",
24063
24064     /**
24065      * Refreshes the view.
24066      */
24067     refresh : function(){
24068         this.clearSelections();
24069         this.el.update("");
24070         var html = [];
24071         var o = this.jsonData;
24072         if(o && o.length > 0){
24073             for(var i = 0, len = o.length; i < len; i++){
24074                 var data = this.prepareData(o[i], i, o);
24075                 html[html.length] = this.tpl.apply(data);
24076             }
24077         }else{
24078             html.push(this.emptyText);
24079         }
24080         this.el.update(html.join(""));
24081         this.nodes = this.el.dom.childNodes;
24082         this.updateIndexes(0);
24083     },
24084
24085     /**
24086      * 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.
24087      * @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:
24088      <pre><code>
24089      view.load({
24090          url: "your-url.php",
24091          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
24092          callback: yourFunction,
24093          scope: yourObject, //(optional scope)
24094          discardUrl: false,
24095          nocache: false,
24096          text: "Loading...",
24097          timeout: 30,
24098          scripts: false
24099      });
24100      </code></pre>
24101      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
24102      * 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.
24103      * @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}
24104      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
24105      * @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.
24106      */
24107     load : function(){
24108         var um = this.el.getUpdateManager();
24109         um.update.apply(um, arguments);
24110     },
24111
24112     render : function(el, response){
24113         this.clearSelections();
24114         this.el.update("");
24115         var o;
24116         try{
24117             o = Roo.util.JSON.decode(response.responseText);
24118             if(this.jsonRoot){
24119                 
24120                 o = o[this.jsonRoot];
24121             }
24122         } catch(e){
24123         }
24124         /**
24125          * The current JSON data or null
24126          */
24127         this.jsonData = o;
24128         this.beforeRender();
24129         this.refresh();
24130     },
24131
24132 /**
24133  * Get the number of records in the current JSON dataset
24134  * @return {Number}
24135  */
24136     getCount : function(){
24137         return this.jsonData ? this.jsonData.length : 0;
24138     },
24139
24140 /**
24141  * Returns the JSON object for the specified node(s)
24142  * @param {HTMLElement/Array} node The node or an array of nodes
24143  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
24144  * you get the JSON object for the node
24145  */
24146     getNodeData : function(node){
24147         if(node instanceof Array){
24148             var data = [];
24149             for(var i = 0, len = node.length; i < len; i++){
24150                 data.push(this.getNodeData(node[i]));
24151             }
24152             return data;
24153         }
24154         return this.jsonData[this.indexOf(node)] || null;
24155     },
24156
24157     beforeRender : function(){
24158         this.snapshot = this.jsonData;
24159         if(this.sortInfo){
24160             this.sort.apply(this, this.sortInfo);
24161         }
24162         this.fireEvent("beforerender", this, this.jsonData);
24163     },
24164
24165     onLoad : function(el, o){
24166         this.fireEvent("load", this, this.jsonData, o);
24167     },
24168
24169     onLoadException : function(el, o){
24170         this.fireEvent("loadexception", this, o);
24171     },
24172
24173 /**
24174  * Filter the data by a specific property.
24175  * @param {String} property A property on your JSON objects
24176  * @param {String/RegExp} value Either string that the property values
24177  * should start with, or a RegExp to test against the property
24178  */
24179     filter : function(property, value){
24180         if(this.jsonData){
24181             var data = [];
24182             var ss = this.snapshot;
24183             if(typeof value == "string"){
24184                 var vlen = value.length;
24185                 if(vlen == 0){
24186                     this.clearFilter();
24187                     return;
24188                 }
24189                 value = value.toLowerCase();
24190                 for(var i = 0, len = ss.length; i < len; i++){
24191                     var o = ss[i];
24192                     if(o[property].substr(0, vlen).toLowerCase() == value){
24193                         data.push(o);
24194                     }
24195                 }
24196             } else if(value.exec){ // regex?
24197                 for(var i = 0, len = ss.length; i < len; i++){
24198                     var o = ss[i];
24199                     if(value.test(o[property])){
24200                         data.push(o);
24201                     }
24202                 }
24203             } else{
24204                 return;
24205             }
24206             this.jsonData = data;
24207             this.refresh();
24208         }
24209     },
24210
24211 /**
24212  * Filter by a function. The passed function will be called with each
24213  * object in the current dataset. If the function returns true the value is kept,
24214  * otherwise it is filtered.
24215  * @param {Function} fn
24216  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
24217  */
24218     filterBy : function(fn, scope){
24219         if(this.jsonData){
24220             var data = [];
24221             var ss = this.snapshot;
24222             for(var i = 0, len = ss.length; i < len; i++){
24223                 var o = ss[i];
24224                 if(fn.call(scope || this, o)){
24225                     data.push(o);
24226                 }
24227             }
24228             this.jsonData = data;
24229             this.refresh();
24230         }
24231     },
24232
24233 /**
24234  * Clears the current filter.
24235  */
24236     clearFilter : function(){
24237         if(this.snapshot && this.jsonData != this.snapshot){
24238             this.jsonData = this.snapshot;
24239             this.refresh();
24240         }
24241     },
24242
24243
24244 /**
24245  * Sorts the data for this view and refreshes it.
24246  * @param {String} property A property on your JSON objects to sort on
24247  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
24248  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
24249  */
24250     sort : function(property, dir, sortType){
24251         this.sortInfo = Array.prototype.slice.call(arguments, 0);
24252         if(this.jsonData){
24253             var p = property;
24254             var dsc = dir && dir.toLowerCase() == "desc";
24255             var f = function(o1, o2){
24256                 var v1 = sortType ? sortType(o1[p]) : o1[p];
24257                 var v2 = sortType ? sortType(o2[p]) : o2[p];
24258                 ;
24259                 if(v1 < v2){
24260                     return dsc ? +1 : -1;
24261                 } else if(v1 > v2){
24262                     return dsc ? -1 : +1;
24263                 } else{
24264                     return 0;
24265                 }
24266             };
24267             this.jsonData.sort(f);
24268             this.refresh();
24269             if(this.jsonData != this.snapshot){
24270                 this.snapshot.sort(f);
24271             }
24272         }
24273     }
24274 });/*
24275  * Based on:
24276  * Ext JS Library 1.1.1
24277  * Copyright(c) 2006-2007, Ext JS, LLC.
24278  *
24279  * Originally Released Under LGPL - original licence link has changed is not relivant.
24280  *
24281  * Fork - LGPL
24282  * <script type="text/javascript">
24283  */
24284  
24285
24286 /**
24287  * @class Roo.ColorPalette
24288  * @extends Roo.Component
24289  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
24290  * Here's an example of typical usage:
24291  * <pre><code>
24292 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
24293 cp.render('my-div');
24294
24295 cp.on('select', function(palette, selColor){
24296     // do something with selColor
24297 });
24298 </code></pre>
24299  * @constructor
24300  * Create a new ColorPalette
24301  * @param {Object} config The config object
24302  */
24303 Roo.ColorPalette = function(config){
24304     Roo.ColorPalette.superclass.constructor.call(this, config);
24305     this.addEvents({
24306         /**
24307              * @event select
24308              * Fires when a color is selected
24309              * @param {ColorPalette} this
24310              * @param {String} color The 6-digit color hex code (without the # symbol)
24311              */
24312         select: true
24313     });
24314
24315     if(this.handler){
24316         this.on("select", this.handler, this.scope, true);
24317     }
24318 };
24319 Roo.extend(Roo.ColorPalette, Roo.Component, {
24320     /**
24321      * @cfg {String} itemCls
24322      * The CSS class to apply to the containing element (defaults to "x-color-palette")
24323      */
24324     itemCls : "x-color-palette",
24325     /**
24326      * @cfg {String} value
24327      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
24328      * the hex codes are case-sensitive.
24329      */
24330     value : null,
24331     clickEvent:'click',
24332     // private
24333     ctype: "Roo.ColorPalette",
24334
24335     /**
24336      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
24337      */
24338     allowReselect : false,
24339
24340     /**
24341      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
24342      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
24343      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
24344      * of colors with the width setting until the box is symmetrical.</p>
24345      * <p>You can override individual colors if needed:</p>
24346      * <pre><code>
24347 var cp = new Roo.ColorPalette();
24348 cp.colors[0] = "FF0000";  // change the first box to red
24349 </code></pre>
24350
24351 Or you can provide a custom array of your own for complete control:
24352 <pre><code>
24353 var cp = new Roo.ColorPalette();
24354 cp.colors = ["000000", "993300", "333300"];
24355 </code></pre>
24356      * @type Array
24357      */
24358     colors : [
24359         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
24360         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
24361         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
24362         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
24363         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
24364     ],
24365
24366     // private
24367     onRender : function(container, position){
24368         var t = new Roo.MasterTemplate(
24369             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
24370         );
24371         var c = this.colors;
24372         for(var i = 0, len = c.length; i < len; i++){
24373             t.add([c[i]]);
24374         }
24375         var el = document.createElement("div");
24376         el.className = this.itemCls;
24377         t.overwrite(el);
24378         container.dom.insertBefore(el, position);
24379         this.el = Roo.get(el);
24380         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
24381         if(this.clickEvent != 'click'){
24382             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
24383         }
24384     },
24385
24386     // private
24387     afterRender : function(){
24388         Roo.ColorPalette.superclass.afterRender.call(this);
24389         if(this.value){
24390             var s = this.value;
24391             this.value = null;
24392             this.select(s);
24393         }
24394     },
24395
24396     // private
24397     handleClick : function(e, t){
24398         e.preventDefault();
24399         if(!this.disabled){
24400             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
24401             this.select(c.toUpperCase());
24402         }
24403     },
24404
24405     /**
24406      * Selects the specified color in the palette (fires the select event)
24407      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
24408      */
24409     select : function(color){
24410         color = color.replace("#", "");
24411         if(color != this.value || this.allowReselect){
24412             var el = this.el;
24413             if(this.value){
24414                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
24415             }
24416             el.child("a.color-"+color).addClass("x-color-palette-sel");
24417             this.value = color;
24418             this.fireEvent("select", this, color);
24419         }
24420     }
24421 });/*
24422  * Based on:
24423  * Ext JS Library 1.1.1
24424  * Copyright(c) 2006-2007, Ext JS, LLC.
24425  *
24426  * Originally Released Under LGPL - original licence link has changed is not relivant.
24427  *
24428  * Fork - LGPL
24429  * <script type="text/javascript">
24430  */
24431  
24432 /**
24433  * @class Roo.DatePicker
24434  * @extends Roo.Component
24435  * Simple date picker class.
24436  * @constructor
24437  * Create a new DatePicker
24438  * @param {Object} config The config object
24439  */
24440 Roo.DatePicker = function(config){
24441     Roo.DatePicker.superclass.constructor.call(this, config);
24442
24443     this.value = config && config.value ?
24444                  config.value.clearTime() : new Date().clearTime();
24445
24446     this.addEvents({
24447         /**
24448              * @event select
24449              * Fires when a date is selected
24450              * @param {DatePicker} this
24451              * @param {Date} date The selected date
24452              */
24453         select: true
24454     });
24455
24456     if(this.handler){
24457         this.on("select", this.handler,  this.scope || this);
24458     }
24459     // build the disabledDatesRE
24460     if(!this.disabledDatesRE && this.disabledDates){
24461         var dd = this.disabledDates;
24462         var re = "(?:";
24463         for(var i = 0; i < dd.length; i++){
24464             re += dd[i];
24465             if(i != dd.length-1) re += "|";
24466         }
24467         this.disabledDatesRE = new RegExp(re + ")");
24468     }
24469 };
24470
24471 Roo.extend(Roo.DatePicker, Roo.Component, {
24472     /**
24473      * @cfg {String} todayText
24474      * The text to display on the button that selects the current date (defaults to "Today")
24475      */
24476     todayText : "Today",
24477     /**
24478      * @cfg {String} okText
24479      * The text to display on the ok button
24480      */
24481     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
24482     /**
24483      * @cfg {String} cancelText
24484      * The text to display on the cancel button
24485      */
24486     cancelText : "Cancel",
24487     /**
24488      * @cfg {String} todayTip
24489      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
24490      */
24491     todayTip : "{0} (Spacebar)",
24492     /**
24493      * @cfg {Date} minDate
24494      * Minimum allowable date (JavaScript date object, defaults to null)
24495      */
24496     minDate : null,
24497     /**
24498      * @cfg {Date} maxDate
24499      * Maximum allowable date (JavaScript date object, defaults to null)
24500      */
24501     maxDate : null,
24502     /**
24503      * @cfg {String} minText
24504      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
24505      */
24506     minText : "This date is before the minimum date",
24507     /**
24508      * @cfg {String} maxText
24509      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
24510      */
24511     maxText : "This date is after the maximum date",
24512     /**
24513      * @cfg {String} format
24514      * The default date format string which can be overriden for localization support.  The format must be
24515      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
24516      */
24517     format : "m/d/y",
24518     /**
24519      * @cfg {Array} disabledDays
24520      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
24521      */
24522     disabledDays : null,
24523     /**
24524      * @cfg {String} disabledDaysText
24525      * The tooltip to display when the date falls on a disabled day (defaults to "")
24526      */
24527     disabledDaysText : "",
24528     /**
24529      * @cfg {RegExp} disabledDatesRE
24530      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
24531      */
24532     disabledDatesRE : null,
24533     /**
24534      * @cfg {String} disabledDatesText
24535      * The tooltip text to display when the date falls on a disabled date (defaults to "")
24536      */
24537     disabledDatesText : "",
24538     /**
24539      * @cfg {Boolean} constrainToViewport
24540      * True to constrain the date picker to the viewport (defaults to true)
24541      */
24542     constrainToViewport : true,
24543     /**
24544      * @cfg {Array} monthNames
24545      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
24546      */
24547     monthNames : Date.monthNames,
24548     /**
24549      * @cfg {Array} dayNames
24550      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
24551      */
24552     dayNames : Date.dayNames,
24553     /**
24554      * @cfg {String} nextText
24555      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
24556      */
24557     nextText: 'Next Month (Control+Right)',
24558     /**
24559      * @cfg {String} prevText
24560      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
24561      */
24562     prevText: 'Previous Month (Control+Left)',
24563     /**
24564      * @cfg {String} monthYearText
24565      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
24566      */
24567     monthYearText: 'Choose a month (Control+Up/Down to move years)',
24568     /**
24569      * @cfg {Number} startDay
24570      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
24571      */
24572     startDay : 0,
24573     /**
24574      * @cfg {Bool} showClear
24575      * Show a clear button (usefull for date form elements that can be blank.)
24576      */
24577     
24578     showClear: false,
24579     
24580     /**
24581      * Sets the value of the date field
24582      * @param {Date} value The date to set
24583      */
24584     setValue : function(value){
24585         var old = this.value;
24586         this.value = value.clearTime(true);
24587         if(this.el){
24588             this.update(this.value);
24589         }
24590     },
24591
24592     /**
24593      * Gets the current selected value of the date field
24594      * @return {Date} The selected date
24595      */
24596     getValue : function(){
24597         return this.value;
24598     },
24599
24600     // private
24601     focus : function(){
24602         if(this.el){
24603             this.update(this.activeDate);
24604         }
24605     },
24606
24607     // private
24608     onRender : function(container, position){
24609         var m = [
24610              '<table cellspacing="0">',
24611                 '<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>',
24612                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
24613         var dn = this.dayNames;
24614         for(var i = 0; i < 7; i++){
24615             var d = this.startDay+i;
24616             if(d > 6){
24617                 d = d-7;
24618             }
24619             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
24620         }
24621         m[m.length] = "</tr></thead><tbody><tr>";
24622         for(var i = 0; i < 42; i++) {
24623             if(i % 7 == 0 && i != 0){
24624                 m[m.length] = "</tr><tr>";
24625             }
24626             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
24627         }
24628         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
24629             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
24630
24631         var el = document.createElement("div");
24632         el.className = "x-date-picker";
24633         el.innerHTML = m.join("");
24634
24635         container.dom.insertBefore(el, position);
24636
24637         this.el = Roo.get(el);
24638         this.eventEl = Roo.get(el.firstChild);
24639
24640         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
24641             handler: this.showPrevMonth,
24642             scope: this,
24643             preventDefault:true,
24644             stopDefault:true
24645         });
24646
24647         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
24648             handler: this.showNextMonth,
24649             scope: this,
24650             preventDefault:true,
24651             stopDefault:true
24652         });
24653
24654         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
24655
24656         this.monthPicker = this.el.down('div.x-date-mp');
24657         this.monthPicker.enableDisplayMode('block');
24658         
24659         var kn = new Roo.KeyNav(this.eventEl, {
24660             "left" : function(e){
24661                 e.ctrlKey ?
24662                     this.showPrevMonth() :
24663                     this.update(this.activeDate.add("d", -1));
24664             },
24665
24666             "right" : function(e){
24667                 e.ctrlKey ?
24668                     this.showNextMonth() :
24669                     this.update(this.activeDate.add("d", 1));
24670             },
24671
24672             "up" : function(e){
24673                 e.ctrlKey ?
24674                     this.showNextYear() :
24675                     this.update(this.activeDate.add("d", -7));
24676             },
24677
24678             "down" : function(e){
24679                 e.ctrlKey ?
24680                     this.showPrevYear() :
24681                     this.update(this.activeDate.add("d", 7));
24682             },
24683
24684             "pageUp" : function(e){
24685                 this.showNextMonth();
24686             },
24687
24688             "pageDown" : function(e){
24689                 this.showPrevMonth();
24690             },
24691
24692             "enter" : function(e){
24693                 e.stopPropagation();
24694                 return true;
24695             },
24696
24697             scope : this
24698         });
24699
24700         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
24701
24702         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
24703
24704         this.el.unselectable();
24705         
24706         this.cells = this.el.select("table.x-date-inner tbody td");
24707         this.textNodes = this.el.query("table.x-date-inner tbody span");
24708
24709         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
24710             text: "&#160;",
24711             tooltip: this.monthYearText
24712         });
24713
24714         this.mbtn.on('click', this.showMonthPicker, this);
24715         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
24716
24717
24718         var today = (new Date()).dateFormat(this.format);
24719         
24720         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
24721         if (this.showClear) {
24722             baseTb.add( new Roo.Toolbar.Fill());
24723         }
24724         baseTb.add({
24725             text: String.format(this.todayText, today),
24726             tooltip: String.format(this.todayTip, today),
24727             handler: this.selectToday,
24728             scope: this
24729         });
24730         
24731         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
24732             
24733         //});
24734         if (this.showClear) {
24735             
24736             baseTb.add( new Roo.Toolbar.Fill());
24737             baseTb.add({
24738                 text: '&#160;',
24739                 cls: 'x-btn-icon x-btn-clear',
24740                 handler: function() {
24741                     //this.value = '';
24742                     this.fireEvent("select", this, '');
24743                 },
24744                 scope: this
24745             });
24746         }
24747         
24748         
24749         if(Roo.isIE){
24750             this.el.repaint();
24751         }
24752         this.update(this.value);
24753     },
24754
24755     createMonthPicker : function(){
24756         if(!this.monthPicker.dom.firstChild){
24757             var buf = ['<table border="0" cellspacing="0">'];
24758             for(var i = 0; i < 6; i++){
24759                 buf.push(
24760                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
24761                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
24762                     i == 0 ?
24763                     '<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>' :
24764                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
24765                 );
24766             }
24767             buf.push(
24768                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
24769                     this.okText,
24770                     '</button><button type="button" class="x-date-mp-cancel">',
24771                     this.cancelText,
24772                     '</button></td></tr>',
24773                 '</table>'
24774             );
24775             this.monthPicker.update(buf.join(''));
24776             this.monthPicker.on('click', this.onMonthClick, this);
24777             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
24778
24779             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
24780             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
24781
24782             this.mpMonths.each(function(m, a, i){
24783                 i += 1;
24784                 if((i%2) == 0){
24785                     m.dom.xmonth = 5 + Math.round(i * .5);
24786                 }else{
24787                     m.dom.xmonth = Math.round((i-1) * .5);
24788                 }
24789             });
24790         }
24791     },
24792
24793     showMonthPicker : function(){
24794         this.createMonthPicker();
24795         var size = this.el.getSize();
24796         this.monthPicker.setSize(size);
24797         this.monthPicker.child('table').setSize(size);
24798
24799         this.mpSelMonth = (this.activeDate || this.value).getMonth();
24800         this.updateMPMonth(this.mpSelMonth);
24801         this.mpSelYear = (this.activeDate || this.value).getFullYear();
24802         this.updateMPYear(this.mpSelYear);
24803
24804         this.monthPicker.slideIn('t', {duration:.2});
24805     },
24806
24807     updateMPYear : function(y){
24808         this.mpyear = y;
24809         var ys = this.mpYears.elements;
24810         for(var i = 1; i <= 10; i++){
24811             var td = ys[i-1], y2;
24812             if((i%2) == 0){
24813                 y2 = y + Math.round(i * .5);
24814                 td.firstChild.innerHTML = y2;
24815                 td.xyear = y2;
24816             }else{
24817                 y2 = y - (5-Math.round(i * .5));
24818                 td.firstChild.innerHTML = y2;
24819                 td.xyear = y2;
24820             }
24821             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
24822         }
24823     },
24824
24825     updateMPMonth : function(sm){
24826         this.mpMonths.each(function(m, a, i){
24827             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
24828         });
24829     },
24830
24831     selectMPMonth: function(m){
24832         
24833     },
24834
24835     onMonthClick : function(e, t){
24836         e.stopEvent();
24837         var el = new Roo.Element(t), pn;
24838         if(el.is('button.x-date-mp-cancel')){
24839             this.hideMonthPicker();
24840         }
24841         else if(el.is('button.x-date-mp-ok')){
24842             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24843             this.hideMonthPicker();
24844         }
24845         else if(pn = el.up('td.x-date-mp-month', 2)){
24846             this.mpMonths.removeClass('x-date-mp-sel');
24847             pn.addClass('x-date-mp-sel');
24848             this.mpSelMonth = pn.dom.xmonth;
24849         }
24850         else if(pn = el.up('td.x-date-mp-year', 2)){
24851             this.mpYears.removeClass('x-date-mp-sel');
24852             pn.addClass('x-date-mp-sel');
24853             this.mpSelYear = pn.dom.xyear;
24854         }
24855         else if(el.is('a.x-date-mp-prev')){
24856             this.updateMPYear(this.mpyear-10);
24857         }
24858         else if(el.is('a.x-date-mp-next')){
24859             this.updateMPYear(this.mpyear+10);
24860         }
24861     },
24862
24863     onMonthDblClick : function(e, t){
24864         e.stopEvent();
24865         var el = new Roo.Element(t), pn;
24866         if(pn = el.up('td.x-date-mp-month', 2)){
24867             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
24868             this.hideMonthPicker();
24869         }
24870         else if(pn = el.up('td.x-date-mp-year', 2)){
24871             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
24872             this.hideMonthPicker();
24873         }
24874     },
24875
24876     hideMonthPicker : function(disableAnim){
24877         if(this.monthPicker){
24878             if(disableAnim === true){
24879                 this.monthPicker.hide();
24880             }else{
24881                 this.monthPicker.slideOut('t', {duration:.2});
24882             }
24883         }
24884     },
24885
24886     // private
24887     showPrevMonth : function(e){
24888         this.update(this.activeDate.add("mo", -1));
24889     },
24890
24891     // private
24892     showNextMonth : function(e){
24893         this.update(this.activeDate.add("mo", 1));
24894     },
24895
24896     // private
24897     showPrevYear : function(){
24898         this.update(this.activeDate.add("y", -1));
24899     },
24900
24901     // private
24902     showNextYear : function(){
24903         this.update(this.activeDate.add("y", 1));
24904     },
24905
24906     // private
24907     handleMouseWheel : function(e){
24908         var delta = e.getWheelDelta();
24909         if(delta > 0){
24910             this.showPrevMonth();
24911             e.stopEvent();
24912         } else if(delta < 0){
24913             this.showNextMonth();
24914             e.stopEvent();
24915         }
24916     },
24917
24918     // private
24919     handleDateClick : function(e, t){
24920         e.stopEvent();
24921         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
24922             this.setValue(new Date(t.dateValue));
24923             this.fireEvent("select", this, this.value);
24924         }
24925     },
24926
24927     // private
24928     selectToday : function(){
24929         this.setValue(new Date().clearTime());
24930         this.fireEvent("select", this, this.value);
24931     },
24932
24933     // private
24934     update : function(date){
24935         var vd = this.activeDate;
24936         this.activeDate = date;
24937         if(vd && this.el){
24938             var t = date.getTime();
24939             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
24940                 this.cells.removeClass("x-date-selected");
24941                 this.cells.each(function(c){
24942                    if(c.dom.firstChild.dateValue == t){
24943                        c.addClass("x-date-selected");
24944                        setTimeout(function(){
24945                             try{c.dom.firstChild.focus();}catch(e){}
24946                        }, 50);
24947                        return false;
24948                    }
24949                 });
24950                 return;
24951             }
24952         }
24953         var days = date.getDaysInMonth();
24954         var firstOfMonth = date.getFirstDateOfMonth();
24955         var startingPos = firstOfMonth.getDay()-this.startDay;
24956
24957         if(startingPos <= this.startDay){
24958             startingPos += 7;
24959         }
24960
24961         var pm = date.add("mo", -1);
24962         var prevStart = pm.getDaysInMonth()-startingPos;
24963
24964         var cells = this.cells.elements;
24965         var textEls = this.textNodes;
24966         days += startingPos;
24967
24968         // convert everything to numbers so it's fast
24969         var day = 86400000;
24970         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
24971         var today = new Date().clearTime().getTime();
24972         var sel = date.clearTime().getTime();
24973         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
24974         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
24975         var ddMatch = this.disabledDatesRE;
24976         var ddText = this.disabledDatesText;
24977         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
24978         var ddaysText = this.disabledDaysText;
24979         var format = this.format;
24980
24981         var setCellClass = function(cal, cell){
24982             cell.title = "";
24983             var t = d.getTime();
24984             cell.firstChild.dateValue = t;
24985             if(t == today){
24986                 cell.className += " x-date-today";
24987                 cell.title = cal.todayText;
24988             }
24989             if(t == sel){
24990                 cell.className += " x-date-selected";
24991                 setTimeout(function(){
24992                     try{cell.firstChild.focus();}catch(e){}
24993                 }, 50);
24994             }
24995             // disabling
24996             if(t < min) {
24997                 cell.className = " x-date-disabled";
24998                 cell.title = cal.minText;
24999                 return;
25000             }
25001             if(t > max) {
25002                 cell.className = " x-date-disabled";
25003                 cell.title = cal.maxText;
25004                 return;
25005             }
25006             if(ddays){
25007                 if(ddays.indexOf(d.getDay()) != -1){
25008                     cell.title = ddaysText;
25009                     cell.className = " x-date-disabled";
25010                 }
25011             }
25012             if(ddMatch && format){
25013                 var fvalue = d.dateFormat(format);
25014                 if(ddMatch.test(fvalue)){
25015                     cell.title = ddText.replace("%0", fvalue);
25016                     cell.className = " x-date-disabled";
25017                 }
25018             }
25019         };
25020
25021         var i = 0;
25022         for(; i < startingPos; i++) {
25023             textEls[i].innerHTML = (++prevStart);
25024             d.setDate(d.getDate()+1);
25025             cells[i].className = "x-date-prevday";
25026             setCellClass(this, cells[i]);
25027         }
25028         for(; i < days; i++){
25029             intDay = i - startingPos + 1;
25030             textEls[i].innerHTML = (intDay);
25031             d.setDate(d.getDate()+1);
25032             cells[i].className = "x-date-active";
25033             setCellClass(this, cells[i]);
25034         }
25035         var extraDays = 0;
25036         for(; i < 42; i++) {
25037              textEls[i].innerHTML = (++extraDays);
25038              d.setDate(d.getDate()+1);
25039              cells[i].className = "x-date-nextday";
25040              setCellClass(this, cells[i]);
25041         }
25042
25043         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
25044
25045         if(!this.internalRender){
25046             var main = this.el.dom.firstChild;
25047             var w = main.offsetWidth;
25048             this.el.setWidth(w + this.el.getBorderWidth("lr"));
25049             Roo.fly(main).setWidth(w);
25050             this.internalRender = true;
25051             // opera does not respect the auto grow header center column
25052             // then, after it gets a width opera refuses to recalculate
25053             // without a second pass
25054             if(Roo.isOpera && !this.secondPass){
25055                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
25056                 this.secondPass = true;
25057                 this.update.defer(10, this, [date]);
25058             }
25059         }
25060     }
25061 });/*
25062  * Based on:
25063  * Ext JS Library 1.1.1
25064  * Copyright(c) 2006-2007, Ext JS, LLC.
25065  *
25066  * Originally Released Under LGPL - original licence link has changed is not relivant.
25067  *
25068  * Fork - LGPL
25069  * <script type="text/javascript">
25070  */
25071 /**
25072  * @class Roo.TabPanel
25073  * @extends Roo.util.Observable
25074  * A lightweight tab container.
25075  * <br><br>
25076  * Usage:
25077  * <pre><code>
25078 // basic tabs 1, built from existing content
25079 var tabs = new Roo.TabPanel("tabs1");
25080 tabs.addTab("script", "View Script");
25081 tabs.addTab("markup", "View Markup");
25082 tabs.activate("script");
25083
25084 // more advanced tabs, built from javascript
25085 var jtabs = new Roo.TabPanel("jtabs");
25086 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
25087
25088 // set up the UpdateManager
25089 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
25090 var updater = tab2.getUpdateManager();
25091 updater.setDefaultUrl("ajax1.htm");
25092 tab2.on('activate', updater.refresh, updater, true);
25093
25094 // Use setUrl for Ajax loading
25095 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
25096 tab3.setUrl("ajax2.htm", null, true);
25097
25098 // Disabled tab
25099 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
25100 tab4.disable();
25101
25102 jtabs.activate("jtabs-1");
25103  * </code></pre>
25104  * @constructor
25105  * Create a new TabPanel.
25106  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
25107  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
25108  */
25109 Roo.TabPanel = function(container, config){
25110     /**
25111     * The container element for this TabPanel.
25112     * @type Roo.Element
25113     */
25114     this.el = Roo.get(container, true);
25115     if(config){
25116         if(typeof config == "boolean"){
25117             this.tabPosition = config ? "bottom" : "top";
25118         }else{
25119             Roo.apply(this, config);
25120         }
25121     }
25122     if(this.tabPosition == "bottom"){
25123         this.bodyEl = Roo.get(this.createBody(this.el.dom));
25124         this.el.addClass("x-tabs-bottom");
25125     }
25126     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
25127     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
25128     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
25129     if(Roo.isIE){
25130         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
25131     }
25132     if(this.tabPosition != "bottom"){
25133     /** The body element that contains {@link Roo.TabPanelItem} bodies.
25134      * @type Roo.Element
25135      */
25136       this.bodyEl = Roo.get(this.createBody(this.el.dom));
25137       this.el.addClass("x-tabs-top");
25138     }
25139     this.items = [];
25140
25141     this.bodyEl.setStyle("position", "relative");
25142
25143     this.active = null;
25144     this.activateDelegate = this.activate.createDelegate(this);
25145
25146     this.addEvents({
25147         /**
25148          * @event tabchange
25149          * Fires when the active tab changes
25150          * @param {Roo.TabPanel} this
25151          * @param {Roo.TabPanelItem} activePanel The new active tab
25152          */
25153         "tabchange": true,
25154         /**
25155          * @event beforetabchange
25156          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
25157          * @param {Roo.TabPanel} this
25158          * @param {Object} e Set cancel to true on this object to cancel the tab change
25159          * @param {Roo.TabPanelItem} tab The tab being changed to
25160          */
25161         "beforetabchange" : true
25162     });
25163
25164     Roo.EventManager.onWindowResize(this.onResize, this);
25165     this.cpad = this.el.getPadding("lr");
25166     this.hiddenCount = 0;
25167
25168     Roo.TabPanel.superclass.constructor.call(this);
25169 };
25170
25171 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
25172         /*
25173          *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
25174          */
25175     tabPosition : "top",
25176         /*
25177          *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
25178          */
25179     currentTabWidth : 0,
25180         /*
25181          *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
25182          */
25183     minTabWidth : 40,
25184         /*
25185          *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
25186          */
25187     maxTabWidth : 250,
25188         /*
25189          *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
25190          */
25191     preferredTabWidth : 175,
25192         /*
25193          *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
25194          */
25195     resizeTabs : false,
25196         /*
25197          *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
25198          */
25199     monitorResize : true,
25200
25201     /**
25202      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
25203      * @param {String} id The id of the div to use <b>or create</b>
25204      * @param {String} text The text for the tab
25205      * @param {String} content (optional) Content to put in the TabPanelItem body
25206      * @param {Boolean} closable (optional) True to create a close icon on the tab
25207      * @return {Roo.TabPanelItem} The created TabPanelItem
25208      */
25209     addTab : function(id, text, content, closable){
25210         var item = new Roo.TabPanelItem(this, id, text, closable);
25211         this.addTabItem(item);
25212         if(content){
25213             item.setContent(content);
25214         }
25215         return item;
25216     },
25217
25218     /**
25219      * Returns the {@link Roo.TabPanelItem} with the specified id/index
25220      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
25221      * @return {Roo.TabPanelItem}
25222      */
25223     getTab : function(id){
25224         return this.items[id];
25225     },
25226
25227     /**
25228      * Hides the {@link Roo.TabPanelItem} with the specified id/index
25229      * @param {String/Number} id The id or index of the TabPanelItem to hide.
25230      */
25231     hideTab : function(id){
25232         var t = this.items[id];
25233         if(!t.isHidden()){
25234            t.setHidden(true);
25235            this.hiddenCount++;
25236            this.autoSizeTabs();
25237         }
25238     },
25239
25240     /**
25241      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
25242      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
25243      */
25244     unhideTab : function(id){
25245         var t = this.items[id];
25246         if(t.isHidden()){
25247            t.setHidden(false);
25248            this.hiddenCount--;
25249            this.autoSizeTabs();
25250         }
25251     },
25252
25253     /**
25254      * Adds an existing {@link Roo.TabPanelItem}.
25255      * @param {Roo.TabPanelItem} item The TabPanelItem to add
25256      */
25257     addTabItem : function(item){
25258         this.items[item.id] = item;
25259         this.items.push(item);
25260         if(this.resizeTabs){
25261            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
25262            this.autoSizeTabs();
25263         }else{
25264             item.autoSize();
25265         }
25266     },
25267
25268     /**
25269      * Removes a {@link Roo.TabPanelItem}.
25270      * @param {String/Number} id The id or index of the TabPanelItem to remove.
25271      */
25272     removeTab : function(id){
25273         var items = this.items;
25274         var tab = items[id];
25275         if(!tab) { return; }
25276         var index = items.indexOf(tab);
25277         if(this.active == tab && items.length > 1){
25278             var newTab = this.getNextAvailable(index);
25279             if(newTab) {
25280                 newTab.activate();
25281             }
25282         }
25283         this.stripEl.dom.removeChild(tab.pnode.dom);
25284         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
25285             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
25286         }
25287         items.splice(index, 1);
25288         delete this.items[tab.id];
25289         tab.fireEvent("close", tab);
25290         tab.purgeListeners();
25291         this.autoSizeTabs();
25292     },
25293
25294     getNextAvailable : function(start){
25295         var items = this.items;
25296         var index = start;
25297         // look for a next tab that will slide over to
25298         // replace the one being removed
25299         while(index < items.length){
25300             var item = items[++index];
25301             if(item && !item.isHidden()){
25302                 return item;
25303             }
25304         }
25305         // if one isn't found select the previous tab (on the left)
25306         index = start;
25307         while(index >= 0){
25308             var item = items[--index];
25309             if(item && !item.isHidden()){
25310                 return item;
25311             }
25312         }
25313         return null;
25314     },
25315
25316     /**
25317      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
25318      * @param {String/Number} id The id or index of the TabPanelItem to disable.
25319      */
25320     disableTab : function(id){
25321         var tab = this.items[id];
25322         if(tab && this.active != tab){
25323             tab.disable();
25324         }
25325     },
25326
25327     /**
25328      * Enables a {@link Roo.TabPanelItem} that is disabled.
25329      * @param {String/Number} id The id or index of the TabPanelItem to enable.
25330      */
25331     enableTab : function(id){
25332         var tab = this.items[id];
25333         tab.enable();
25334     },
25335
25336     /**
25337      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
25338      * @param {String/Number} id The id or index of the TabPanelItem to activate.
25339      * @return {Roo.TabPanelItem} The TabPanelItem.
25340      */
25341     activate : function(id){
25342         var tab = this.items[id];
25343         if(!tab){
25344             return null;
25345         }
25346         if(tab == this.active || tab.disabled){
25347             return tab;
25348         }
25349         var e = {};
25350         this.fireEvent("beforetabchange", this, e, tab);
25351         if(e.cancel !== true && !tab.disabled){
25352             if(this.active){
25353                 this.active.hide();
25354             }
25355             this.active = this.items[id];
25356             this.active.show();
25357             this.fireEvent("tabchange", this, this.active);
25358         }
25359         return tab;
25360     },
25361
25362     /**
25363      * Gets the active {@link Roo.TabPanelItem}.
25364      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
25365      */
25366     getActiveTab : function(){
25367         return this.active;
25368     },
25369
25370     /**
25371      * Updates the tab body element to fit the height of the container element
25372      * for overflow scrolling
25373      * @param {Number} targetHeight (optional) Override the starting height from the elements height
25374      */
25375     syncHeight : function(targetHeight){
25376         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
25377         var bm = this.bodyEl.getMargins();
25378         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
25379         this.bodyEl.setHeight(newHeight);
25380         return newHeight;
25381     },
25382
25383     onResize : function(){
25384         if(this.monitorResize){
25385             this.autoSizeTabs();
25386         }
25387     },
25388
25389     /**
25390      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
25391      */
25392     beginUpdate : function(){
25393         this.updating = true;
25394     },
25395
25396     /**
25397      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
25398      */
25399     endUpdate : function(){
25400         this.updating = false;
25401         this.autoSizeTabs();
25402     },
25403
25404     /**
25405      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
25406      */
25407     autoSizeTabs : function(){
25408         var count = this.items.length;
25409         var vcount = count - this.hiddenCount;
25410         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
25411         var w = Math.max(this.el.getWidth() - this.cpad, 10);
25412         var availWidth = Math.floor(w / vcount);
25413         var b = this.stripBody;
25414         if(b.getWidth() > w){
25415             var tabs = this.items;
25416             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
25417             if(availWidth < this.minTabWidth){
25418                 /*if(!this.sleft){    // incomplete scrolling code
25419                     this.createScrollButtons();
25420                 }
25421                 this.showScroll();
25422                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
25423             }
25424         }else{
25425             if(this.currentTabWidth < this.preferredTabWidth){
25426                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
25427             }
25428         }
25429     },
25430
25431     /**
25432      * Returns the number of tabs in this TabPanel.
25433      * @return {Number}
25434      */
25435      getCount : function(){
25436          return this.items.length;
25437      },
25438
25439     /**
25440      * Resizes all the tabs to the passed width
25441      * @param {Number} The new width
25442      */
25443     setTabWidth : function(width){
25444         this.currentTabWidth = width;
25445         for(var i = 0, len = this.items.length; i < len; i++) {
25446                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
25447         }
25448     },
25449
25450     /**
25451      * Destroys this TabPanel
25452      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
25453      */
25454     destroy : function(removeEl){
25455         Roo.EventManager.removeResizeListener(this.onResize, this);
25456         for(var i = 0, len = this.items.length; i < len; i++){
25457             this.items[i].purgeListeners();
25458         }
25459         if(removeEl === true){
25460             this.el.update("");
25461             this.el.remove();
25462         }
25463     }
25464 });
25465
25466 /**
25467  * @class Roo.TabPanelItem
25468  * @extends Roo.util.Observable
25469  * Represents an individual item (tab plus body) in a TabPanel.
25470  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
25471  * @param {String} id The id of this TabPanelItem
25472  * @param {String} text The text for the tab of this TabPanelItem
25473  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
25474  */
25475 Roo.TabPanelItem = function(tabPanel, id, text, closable){
25476     /**
25477      * The {@link Roo.TabPanel} this TabPanelItem belongs to
25478      * @type Roo.TabPanel
25479      */
25480     this.tabPanel = tabPanel;
25481     /**
25482      * The id for this TabPanelItem
25483      * @type String
25484      */
25485     this.id = id;
25486     /** @private */
25487     this.disabled = false;
25488     /** @private */
25489     this.text = text;
25490     /** @private */
25491     this.loaded = false;
25492     this.closable = closable;
25493
25494     /**
25495      * The body element for this TabPanelItem.
25496      * @type Roo.Element
25497      */
25498     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
25499     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
25500     this.bodyEl.setStyle("display", "block");
25501     this.bodyEl.setStyle("zoom", "1");
25502     this.hideAction();
25503
25504     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
25505     /** @private */
25506     this.el = Roo.get(els.el, true);
25507     this.inner = Roo.get(els.inner, true);
25508     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
25509     this.pnode = Roo.get(els.el.parentNode, true);
25510     this.el.on("mousedown", this.onTabMouseDown, this);
25511     this.el.on("click", this.onTabClick, this);
25512     /** @private */
25513     if(closable){
25514         var c = Roo.get(els.close, true);
25515         c.dom.title = this.closeText;
25516         c.addClassOnOver("close-over");
25517         c.on("click", this.closeClick, this);
25518      }
25519
25520     this.addEvents({
25521          /**
25522          * @event activate
25523          * Fires when this tab becomes the active tab.
25524          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25525          * @param {Roo.TabPanelItem} this
25526          */
25527         "activate": true,
25528         /**
25529          * @event beforeclose
25530          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
25531          * @param {Roo.TabPanelItem} this
25532          * @param {Object} e Set cancel to true on this object to cancel the close.
25533          */
25534         "beforeclose": true,
25535         /**
25536          * @event close
25537          * Fires when this tab is closed.
25538          * @param {Roo.TabPanelItem} this
25539          */
25540          "close": true,
25541         /**
25542          * @event deactivate
25543          * Fires when this tab is no longer the active tab.
25544          * @param {Roo.TabPanel} tabPanel The parent TabPanel
25545          * @param {Roo.TabPanelItem} this
25546          */
25547          "deactivate" : true
25548     });
25549     this.hidden = false;
25550
25551     Roo.TabPanelItem.superclass.constructor.call(this);
25552 };
25553
25554 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
25555     purgeListeners : function(){
25556        Roo.util.Observable.prototype.purgeListeners.call(this);
25557        this.el.removeAllListeners();
25558     },
25559     /**
25560      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
25561      */
25562     show : function(){
25563         this.pnode.addClass("on");
25564         this.showAction();
25565         if(Roo.isOpera){
25566             this.tabPanel.stripWrap.repaint();
25567         }
25568         this.fireEvent("activate", this.tabPanel, this);
25569     },
25570
25571     /**
25572      * Returns true if this tab is the active tab.
25573      * @return {Boolean}
25574      */
25575     isActive : function(){
25576         return this.tabPanel.getActiveTab() == this;
25577     },
25578
25579     /**
25580      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
25581      */
25582     hide : function(){
25583         this.pnode.removeClass("on");
25584         this.hideAction();
25585         this.fireEvent("deactivate", this.tabPanel, this);
25586     },
25587
25588     hideAction : function(){
25589         this.bodyEl.hide();
25590         this.bodyEl.setStyle("position", "absolute");
25591         this.bodyEl.setLeft("-20000px");
25592         this.bodyEl.setTop("-20000px");
25593     },
25594
25595     showAction : function(){
25596         this.bodyEl.setStyle("position", "relative");
25597         this.bodyEl.setTop("");
25598         this.bodyEl.setLeft("");
25599         this.bodyEl.show();
25600     },
25601
25602     /**
25603      * Set the tooltip for the tab.
25604      * @param {String} tooltip The tab's tooltip
25605      */
25606     setTooltip : function(text){
25607         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
25608             this.textEl.dom.qtip = text;
25609             this.textEl.dom.removeAttribute('title');
25610         }else{
25611             this.textEl.dom.title = text;
25612         }
25613     },
25614
25615     onTabClick : function(e){
25616         e.preventDefault();
25617         this.tabPanel.activate(this.id);
25618     },
25619
25620     onTabMouseDown : function(e){
25621         e.preventDefault();
25622         this.tabPanel.activate(this.id);
25623     },
25624
25625     getWidth : function(){
25626         return this.inner.getWidth();
25627     },
25628
25629     setWidth : function(width){
25630         var iwidth = width - this.pnode.getPadding("lr");
25631         this.inner.setWidth(iwidth);
25632         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
25633         this.pnode.setWidth(width);
25634     },
25635
25636     /**
25637      * Show or hide the tab
25638      * @param {Boolean} hidden True to hide or false to show.
25639      */
25640     setHidden : function(hidden){
25641         this.hidden = hidden;
25642         this.pnode.setStyle("display", hidden ? "none" : "");
25643     },
25644
25645     /**
25646      * Returns true if this tab is "hidden"
25647      * @return {Boolean}
25648      */
25649     isHidden : function(){
25650         return this.hidden;
25651     },
25652
25653     /**
25654      * Returns the text for this tab
25655      * @return {String}
25656      */
25657     getText : function(){
25658         return this.text;
25659     },
25660
25661     autoSize : function(){
25662         //this.el.beginMeasure();
25663         this.textEl.setWidth(1);
25664         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
25665         //this.el.endMeasure();
25666     },
25667
25668     /**
25669      * Sets the text for the tab (Note: this also sets the tooltip text)
25670      * @param {String} text The tab's text and tooltip
25671      */
25672     setText : function(text){
25673         this.text = text;
25674         this.textEl.update(text);
25675         this.setTooltip(text);
25676         if(!this.tabPanel.resizeTabs){
25677             this.autoSize();
25678         }
25679     },
25680     /**
25681      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
25682      */
25683     activate : function(){
25684         this.tabPanel.activate(this.id);
25685     },
25686
25687     /**
25688      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
25689      */
25690     disable : function(){
25691         if(this.tabPanel.active != this){
25692             this.disabled = true;
25693             this.pnode.addClass("disabled");
25694         }
25695     },
25696
25697     /**
25698      * Enables this TabPanelItem if it was previously disabled.
25699      */
25700     enable : function(){
25701         this.disabled = false;
25702         this.pnode.removeClass("disabled");
25703     },
25704
25705     /**
25706      * Sets the content for this TabPanelItem.
25707      * @param {String} content The content
25708      * @param {Boolean} loadScripts true to look for and load scripts
25709      */
25710     setContent : function(content, loadScripts){
25711         this.bodyEl.update(content, loadScripts);
25712     },
25713
25714     /**
25715      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
25716      * @return {Roo.UpdateManager} The UpdateManager
25717      */
25718     getUpdateManager : function(){
25719         return this.bodyEl.getUpdateManager();
25720     },
25721
25722     /**
25723      * Set a URL to be used to load the content for this TabPanelItem.
25724      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
25725      * @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)
25726      * @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)
25727      * @return {Roo.UpdateManager} The UpdateManager
25728      */
25729     setUrl : function(url, params, loadOnce){
25730         if(this.refreshDelegate){
25731             this.un('activate', this.refreshDelegate);
25732         }
25733         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
25734         this.on("activate", this.refreshDelegate);
25735         return this.bodyEl.getUpdateManager();
25736     },
25737
25738     /** @private */
25739     _handleRefresh : function(url, params, loadOnce){
25740         if(!loadOnce || !this.loaded){
25741             var updater = this.bodyEl.getUpdateManager();
25742             updater.update(url, params, this._setLoaded.createDelegate(this));
25743         }
25744     },
25745
25746     /**
25747      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
25748      *   Will fail silently if the setUrl method has not been called.
25749      *   This does not activate the panel, just updates its content.
25750      */
25751     refresh : function(){
25752         if(this.refreshDelegate){
25753            this.loaded = false;
25754            this.refreshDelegate();
25755         }
25756     },
25757
25758     /** @private */
25759     _setLoaded : function(){
25760         this.loaded = true;
25761     },
25762
25763     /** @private */
25764     closeClick : function(e){
25765         var o = {};
25766         e.stopEvent();
25767         this.fireEvent("beforeclose", this, o);
25768         if(o.cancel !== true){
25769             this.tabPanel.removeTab(this.id);
25770         }
25771     },
25772     /**
25773      * The text displayed in the tooltip for the close icon.
25774      * @type String
25775      */
25776     closeText : "Close this tab"
25777 });
25778
25779 /** @private */
25780 Roo.TabPanel.prototype.createStrip = function(container){
25781     var strip = document.createElement("div");
25782     strip.className = "x-tabs-wrap";
25783     container.appendChild(strip);
25784     return strip;
25785 };
25786 /** @private */
25787 Roo.TabPanel.prototype.createStripList = function(strip){
25788     // div wrapper for retard IE
25789     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>';
25790     return strip.firstChild.firstChild.firstChild.firstChild;
25791 };
25792 /** @private */
25793 Roo.TabPanel.prototype.createBody = function(container){
25794     var body = document.createElement("div");
25795     Roo.id(body, "tab-body");
25796     Roo.fly(body).addClass("x-tabs-body");
25797     container.appendChild(body);
25798     return body;
25799 };
25800 /** @private */
25801 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
25802     var body = Roo.getDom(id);
25803     if(!body){
25804         body = document.createElement("div");
25805         body.id = id;
25806     }
25807     Roo.fly(body).addClass("x-tabs-item-body");
25808     bodyEl.insertBefore(body, bodyEl.firstChild);
25809     return body;
25810 };
25811 /** @private */
25812 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
25813     var td = document.createElement("td");
25814     stripEl.appendChild(td);
25815     if(closable){
25816         td.className = "x-tabs-closable";
25817         if(!this.closeTpl){
25818             this.closeTpl = new Roo.Template(
25819                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25820                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
25821                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
25822             );
25823         }
25824         var el = this.closeTpl.overwrite(td, {"text": text});
25825         var close = el.getElementsByTagName("div")[0];
25826         var inner = el.getElementsByTagName("em")[0];
25827         return {"el": el, "close": close, "inner": inner};
25828     } else {
25829         if(!this.tabTpl){
25830             this.tabTpl = new Roo.Template(
25831                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
25832                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
25833             );
25834         }
25835         var el = this.tabTpl.overwrite(td, {"text": text});
25836         var inner = el.getElementsByTagName("em")[0];
25837         return {"el": el, "inner": inner};
25838     }
25839 };/*
25840  * Based on:
25841  * Ext JS Library 1.1.1
25842  * Copyright(c) 2006-2007, Ext JS, LLC.
25843  *
25844  * Originally Released Under LGPL - original licence link has changed is not relivant.
25845  *
25846  * Fork - LGPL
25847  * <script type="text/javascript">
25848  */
25849
25850 /**
25851  * @class Roo.Button
25852  * @extends Roo.util.Observable
25853  * Simple Button class
25854  * @cfg {String} text The button text
25855  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
25856  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
25857  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
25858  * @cfg {Object} scope The scope of the handler
25859  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
25860  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
25861  * @cfg {Boolean} hidden True to start hidden (defaults to false)
25862  * @cfg {Boolean} disabled True to start disabled (defaults to false)
25863  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
25864  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
25865    applies if enableToggle = true)
25866  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
25867  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
25868   an {@link Roo.util.ClickRepeater} config object (defaults to false).
25869  * @constructor
25870  * Create a new button
25871  * @param {Object} config The config object
25872  */
25873 Roo.Button = function(renderTo, config)
25874 {
25875     if (!config) {
25876         config = renderTo;
25877         renderTo = config.renderTo || false;
25878     }
25879     
25880     Roo.apply(this, config);
25881     this.addEvents({
25882         /**
25883              * @event click
25884              * Fires when this button is clicked
25885              * @param {Button} this
25886              * @param {EventObject} e The click event
25887              */
25888             "click" : true,
25889         /**
25890              * @event toggle
25891              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
25892              * @param {Button} this
25893              * @param {Boolean} pressed
25894              */
25895             "toggle" : true,
25896         /**
25897              * @event mouseover
25898              * Fires when the mouse hovers over the button
25899              * @param {Button} this
25900              * @param {Event} e The event object
25901              */
25902         'mouseover' : true,
25903         /**
25904              * @event mouseout
25905              * Fires when the mouse exits the button
25906              * @param {Button} this
25907              * @param {Event} e The event object
25908              */
25909         'mouseout': true,
25910          /**
25911              * @event render
25912              * Fires when the button is rendered
25913              * @param {Button} this
25914              */
25915         'render': true
25916     });
25917     if(this.menu){
25918         this.menu = Roo.menu.MenuMgr.get(this.menu);
25919     }
25920     // register listeners first!!  - so render can be captured..
25921     Roo.util.Observable.call(this);
25922     if(renderTo){
25923         this.render(renderTo);
25924     }
25925     
25926   
25927 };
25928
25929 Roo.extend(Roo.Button, Roo.util.Observable, {
25930     /**
25931      * 
25932      */
25933     
25934     /**
25935      * Read-only. True if this button is hidden
25936      * @type Boolean
25937      */
25938     hidden : false,
25939     /**
25940      * Read-only. True if this button is disabled
25941      * @type Boolean
25942      */
25943     disabled : false,
25944     /**
25945      * Read-only. True if this button is pressed (only if enableToggle = true)
25946      * @type Boolean
25947      */
25948     pressed : false,
25949
25950     /**
25951      * @cfg {Number} tabIndex 
25952      * The DOM tabIndex for this button (defaults to undefined)
25953      */
25954     tabIndex : undefined,
25955
25956     /**
25957      * @cfg {Boolean} enableToggle
25958      * True to enable pressed/not pressed toggling (defaults to false)
25959      */
25960     enableToggle: false,
25961     /**
25962      * @cfg {Mixed} menu
25963      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
25964      */
25965     menu : undefined,
25966     /**
25967      * @cfg {String} menuAlign
25968      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
25969      */
25970     menuAlign : "tl-bl?",
25971
25972     /**
25973      * @cfg {String} iconCls
25974      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
25975      */
25976     iconCls : undefined,
25977     /**
25978      * @cfg {String} type
25979      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
25980      */
25981     type : 'button',
25982
25983     // private
25984     menuClassTarget: 'tr',
25985
25986     /**
25987      * @cfg {String} clickEvent
25988      * The type of event to map to the button's event handler (defaults to 'click')
25989      */
25990     clickEvent : 'click',
25991
25992     /**
25993      * @cfg {Boolean} handleMouseEvents
25994      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
25995      */
25996     handleMouseEvents : true,
25997
25998     /**
25999      * @cfg {String} tooltipType
26000      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
26001      */
26002     tooltipType : 'qtip',
26003
26004     /**
26005      * @cfg {String} cls
26006      * A CSS class to apply to the button's main element.
26007      */
26008     
26009     /**
26010      * @cfg {Roo.Template} template (Optional)
26011      * An {@link Roo.Template} with which to create the Button's main element. This Template must
26012      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
26013      * require code modifications if required elements (e.g. a button) aren't present.
26014      */
26015
26016     // private
26017     render : function(renderTo){
26018         var btn;
26019         if(this.hideParent){
26020             this.parentEl = Roo.get(renderTo);
26021         }
26022         if(!this.dhconfig){
26023             if(!this.template){
26024                 if(!Roo.Button.buttonTemplate){
26025                     // hideous table template
26026                     Roo.Button.buttonTemplate = new Roo.Template(
26027                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
26028                         '<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>',
26029                         "</tr></tbody></table>");
26030                 }
26031                 this.template = Roo.Button.buttonTemplate;
26032             }
26033             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
26034             var btnEl = btn.child("button:first");
26035             btnEl.on('focus', this.onFocus, this);
26036             btnEl.on('blur', this.onBlur, this);
26037             if(this.cls){
26038                 btn.addClass(this.cls);
26039             }
26040             if(this.icon){
26041                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
26042             }
26043             if(this.iconCls){
26044                 btnEl.addClass(this.iconCls);
26045                 if(!this.cls){
26046                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26047                 }
26048             }
26049             if(this.tabIndex !== undefined){
26050                 btnEl.dom.tabIndex = this.tabIndex;
26051             }
26052             if(this.tooltip){
26053                 if(typeof this.tooltip == 'object'){
26054                     Roo.QuickTips.tips(Roo.apply({
26055                           target: btnEl.id
26056                     }, this.tooltip));
26057                 } else {
26058                     btnEl.dom[this.tooltipType] = this.tooltip;
26059                 }
26060             }
26061         }else{
26062             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
26063         }
26064         this.el = btn;
26065         if(this.id){
26066             this.el.dom.id = this.el.id = this.id;
26067         }
26068         if(this.menu){
26069             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
26070             this.menu.on("show", this.onMenuShow, this);
26071             this.menu.on("hide", this.onMenuHide, this);
26072         }
26073         btn.addClass("x-btn");
26074         if(Roo.isIE && !Roo.isIE7){
26075             this.autoWidth.defer(1, this);
26076         }else{
26077             this.autoWidth();
26078         }
26079         if(this.handleMouseEvents){
26080             btn.on("mouseover", this.onMouseOver, this);
26081             btn.on("mouseout", this.onMouseOut, this);
26082             btn.on("mousedown", this.onMouseDown, this);
26083         }
26084         btn.on(this.clickEvent, this.onClick, this);
26085         //btn.on("mouseup", this.onMouseUp, this);
26086         if(this.hidden){
26087             this.hide();
26088         }
26089         if(this.disabled){
26090             this.disable();
26091         }
26092         Roo.ButtonToggleMgr.register(this);
26093         if(this.pressed){
26094             this.el.addClass("x-btn-pressed");
26095         }
26096         if(this.repeat){
26097             var repeater = new Roo.util.ClickRepeater(btn,
26098                 typeof this.repeat == "object" ? this.repeat : {}
26099             );
26100             repeater.on("click", this.onClick,  this);
26101         }
26102         
26103         this.fireEvent('render', this);
26104         
26105     },
26106     /**
26107      * Returns the button's underlying element
26108      * @return {Roo.Element} The element
26109      */
26110     getEl : function(){
26111         return this.el;  
26112     },
26113     
26114     /**
26115      * Destroys this Button and removes any listeners.
26116      */
26117     destroy : function(){
26118         Roo.ButtonToggleMgr.unregister(this);
26119         this.el.removeAllListeners();
26120         this.purgeListeners();
26121         this.el.remove();
26122     },
26123
26124     // private
26125     autoWidth : function(){
26126         if(this.el){
26127             this.el.setWidth("auto");
26128             if(Roo.isIE7 && Roo.isStrict){
26129                 var ib = this.el.child('button');
26130                 if(ib && ib.getWidth() > 20){
26131                     ib.clip();
26132                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26133                 }
26134             }
26135             if(this.minWidth){
26136                 if(this.hidden){
26137                     this.el.beginMeasure();
26138                 }
26139                 if(this.el.getWidth() < this.minWidth){
26140                     this.el.setWidth(this.minWidth);
26141                 }
26142                 if(this.hidden){
26143                     this.el.endMeasure();
26144                 }
26145             }
26146         }
26147     },
26148
26149     /**
26150      * Assigns this button's click handler
26151      * @param {Function} handler The function to call when the button is clicked
26152      * @param {Object} scope (optional) Scope for the function passed in
26153      */
26154     setHandler : function(handler, scope){
26155         this.handler = handler;
26156         this.scope = scope;  
26157     },
26158     
26159     /**
26160      * Sets this button's text
26161      * @param {String} text The button text
26162      */
26163     setText : function(text){
26164         this.text = text;
26165         if(this.el){
26166             this.el.child("td.x-btn-center button.x-btn-text").update(text);
26167         }
26168         this.autoWidth();
26169     },
26170     
26171     /**
26172      * Gets the text for this button
26173      * @return {String} The button text
26174      */
26175     getText : function(){
26176         return this.text;  
26177     },
26178     
26179     /**
26180      * Show this button
26181      */
26182     show: function(){
26183         this.hidden = false;
26184         if(this.el){
26185             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
26186         }
26187     },
26188     
26189     /**
26190      * Hide this button
26191      */
26192     hide: function(){
26193         this.hidden = true;
26194         if(this.el){
26195             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
26196         }
26197     },
26198     
26199     /**
26200      * Convenience function for boolean show/hide
26201      * @param {Boolean} visible True to show, false to hide
26202      */
26203     setVisible: function(visible){
26204         if(visible) {
26205             this.show();
26206         }else{
26207             this.hide();
26208         }
26209     },
26210     
26211     /**
26212      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
26213      * @param {Boolean} state (optional) Force a particular state
26214      */
26215     toggle : function(state){
26216         state = state === undefined ? !this.pressed : state;
26217         if(state != this.pressed){
26218             if(state){
26219                 this.el.addClass("x-btn-pressed");
26220                 this.pressed = true;
26221                 this.fireEvent("toggle", this, true);
26222             }else{
26223                 this.el.removeClass("x-btn-pressed");
26224                 this.pressed = false;
26225                 this.fireEvent("toggle", this, false);
26226             }
26227             if(this.toggleHandler){
26228                 this.toggleHandler.call(this.scope || this, this, state);
26229             }
26230         }
26231     },
26232     
26233     /**
26234      * Focus the button
26235      */
26236     focus : function(){
26237         this.el.child('button:first').focus();
26238     },
26239     
26240     /**
26241      * Disable this button
26242      */
26243     disable : function(){
26244         if(this.el){
26245             this.el.addClass("x-btn-disabled");
26246         }
26247         this.disabled = true;
26248     },
26249     
26250     /**
26251      * Enable this button
26252      */
26253     enable : function(){
26254         if(this.el){
26255             this.el.removeClass("x-btn-disabled");
26256         }
26257         this.disabled = false;
26258     },
26259
26260     /**
26261      * Convenience function for boolean enable/disable
26262      * @param {Boolean} enabled True to enable, false to disable
26263      */
26264     setDisabled : function(v){
26265         this[v !== true ? "enable" : "disable"]();
26266     },
26267
26268     // private
26269     onClick : function(e){
26270         if(e){
26271             e.preventDefault();
26272         }
26273         if(e.button != 0){
26274             return;
26275         }
26276         if(!this.disabled){
26277             if(this.enableToggle){
26278                 this.toggle();
26279             }
26280             if(this.menu && !this.menu.isVisible()){
26281                 this.menu.show(this.el, this.menuAlign);
26282             }
26283             this.fireEvent("click", this, e);
26284             if(this.handler){
26285                 this.el.removeClass("x-btn-over");
26286                 this.handler.call(this.scope || this, this, e);
26287             }
26288         }
26289     },
26290     // private
26291     onMouseOver : function(e){
26292         if(!this.disabled){
26293             this.el.addClass("x-btn-over");
26294             this.fireEvent('mouseover', this, e);
26295         }
26296     },
26297     // private
26298     onMouseOut : function(e){
26299         if(!e.within(this.el,  true)){
26300             this.el.removeClass("x-btn-over");
26301             this.fireEvent('mouseout', this, e);
26302         }
26303     },
26304     // private
26305     onFocus : function(e){
26306         if(!this.disabled){
26307             this.el.addClass("x-btn-focus");
26308         }
26309     },
26310     // private
26311     onBlur : function(e){
26312         this.el.removeClass("x-btn-focus");
26313     },
26314     // private
26315     onMouseDown : function(e){
26316         if(!this.disabled && e.button == 0){
26317             this.el.addClass("x-btn-click");
26318             Roo.get(document).on('mouseup', this.onMouseUp, this);
26319         }
26320     },
26321     // private
26322     onMouseUp : function(e){
26323         if(e.button == 0){
26324             this.el.removeClass("x-btn-click");
26325             Roo.get(document).un('mouseup', this.onMouseUp, this);
26326         }
26327     },
26328     // private
26329     onMenuShow : function(e){
26330         this.el.addClass("x-btn-menu-active");
26331     },
26332     // private
26333     onMenuHide : function(e){
26334         this.el.removeClass("x-btn-menu-active");
26335     }   
26336 });
26337
26338 // Private utility class used by Button
26339 Roo.ButtonToggleMgr = function(){
26340    var groups = {};
26341    
26342    function toggleGroup(btn, state){
26343        if(state){
26344            var g = groups[btn.toggleGroup];
26345            for(var i = 0, l = g.length; i < l; i++){
26346                if(g[i] != btn){
26347                    g[i].toggle(false);
26348                }
26349            }
26350        }
26351    }
26352    
26353    return {
26354        register : function(btn){
26355            if(!btn.toggleGroup){
26356                return;
26357            }
26358            var g = groups[btn.toggleGroup];
26359            if(!g){
26360                g = groups[btn.toggleGroup] = [];
26361            }
26362            g.push(btn);
26363            btn.on("toggle", toggleGroup);
26364        },
26365        
26366        unregister : function(btn){
26367            if(!btn.toggleGroup){
26368                return;
26369            }
26370            var g = groups[btn.toggleGroup];
26371            if(g){
26372                g.remove(btn);
26373                btn.un("toggle", toggleGroup);
26374            }
26375        }
26376    };
26377 }();/*
26378  * Based on:
26379  * Ext JS Library 1.1.1
26380  * Copyright(c) 2006-2007, Ext JS, LLC.
26381  *
26382  * Originally Released Under LGPL - original licence link has changed is not relivant.
26383  *
26384  * Fork - LGPL
26385  * <script type="text/javascript">
26386  */
26387  
26388 /**
26389  * @class Roo.SplitButton
26390  * @extends Roo.Button
26391  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
26392  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
26393  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
26394  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
26395  * @cfg {String} arrowTooltip The title attribute of the arrow
26396  * @constructor
26397  * Create a new menu button
26398  * @param {String/HTMLElement/Element} renderTo The element to append the button to
26399  * @param {Object} config The config object
26400  */
26401 Roo.SplitButton = function(renderTo, config){
26402     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
26403     /**
26404      * @event arrowclick
26405      * Fires when this button's arrow is clicked
26406      * @param {SplitButton} this
26407      * @param {EventObject} e The click event
26408      */
26409     this.addEvents({"arrowclick":true});
26410 };
26411
26412 Roo.extend(Roo.SplitButton, Roo.Button, {
26413     render : function(renderTo){
26414         // this is one sweet looking template!
26415         var tpl = new Roo.Template(
26416             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
26417             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
26418             '<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>',
26419             "</tbody></table></td><td>",
26420             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
26421             '<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>',
26422             "</tbody></table></td></tr></table>"
26423         );
26424         var btn = tpl.append(renderTo, [this.text, this.type], true);
26425         var btnEl = btn.child("button");
26426         if(this.cls){
26427             btn.addClass(this.cls);
26428         }
26429         if(this.icon){
26430             btnEl.setStyle('background-image', 'url(' +this.icon +')');
26431         }
26432         if(this.iconCls){
26433             btnEl.addClass(this.iconCls);
26434             if(!this.cls){
26435                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
26436             }
26437         }
26438         this.el = btn;
26439         if(this.handleMouseEvents){
26440             btn.on("mouseover", this.onMouseOver, this);
26441             btn.on("mouseout", this.onMouseOut, this);
26442             btn.on("mousedown", this.onMouseDown, this);
26443             btn.on("mouseup", this.onMouseUp, this);
26444         }
26445         btn.on(this.clickEvent, this.onClick, this);
26446         if(this.tooltip){
26447             if(typeof this.tooltip == 'object'){
26448                 Roo.QuickTips.tips(Roo.apply({
26449                       target: btnEl.id
26450                 }, this.tooltip));
26451             } else {
26452                 btnEl.dom[this.tooltipType] = this.tooltip;
26453             }
26454         }
26455         if(this.arrowTooltip){
26456             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
26457         }
26458         if(this.hidden){
26459             this.hide();
26460         }
26461         if(this.disabled){
26462             this.disable();
26463         }
26464         if(this.pressed){
26465             this.el.addClass("x-btn-pressed");
26466         }
26467         if(Roo.isIE && !Roo.isIE7){
26468             this.autoWidth.defer(1, this);
26469         }else{
26470             this.autoWidth();
26471         }
26472         if(this.menu){
26473             this.menu.on("show", this.onMenuShow, this);
26474             this.menu.on("hide", this.onMenuHide, this);
26475         }
26476         this.fireEvent('render', this);
26477     },
26478
26479     // private
26480     autoWidth : function(){
26481         if(this.el){
26482             var tbl = this.el.child("table:first");
26483             var tbl2 = this.el.child("table:last");
26484             this.el.setWidth("auto");
26485             tbl.setWidth("auto");
26486             if(Roo.isIE7 && Roo.isStrict){
26487                 var ib = this.el.child('button:first');
26488                 if(ib && ib.getWidth() > 20){
26489                     ib.clip();
26490                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
26491                 }
26492             }
26493             if(this.minWidth){
26494                 if(this.hidden){
26495                     this.el.beginMeasure();
26496                 }
26497                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
26498                     tbl.setWidth(this.minWidth-tbl2.getWidth());
26499                 }
26500                 if(this.hidden){
26501                     this.el.endMeasure();
26502                 }
26503             }
26504             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
26505         } 
26506     },
26507     /**
26508      * Sets this button's click handler
26509      * @param {Function} handler The function to call when the button is clicked
26510      * @param {Object} scope (optional) Scope for the function passed above
26511      */
26512     setHandler : function(handler, scope){
26513         this.handler = handler;
26514         this.scope = scope;  
26515     },
26516     
26517     /**
26518      * Sets this button's arrow click handler
26519      * @param {Function} handler The function to call when the arrow is clicked
26520      * @param {Object} scope (optional) Scope for the function passed above
26521      */
26522     setArrowHandler : function(handler, scope){
26523         this.arrowHandler = handler;
26524         this.scope = scope;  
26525     },
26526     
26527     /**
26528      * Focus the button
26529      */
26530     focus : function(){
26531         if(this.el){
26532             this.el.child("button:first").focus();
26533         }
26534     },
26535
26536     // private
26537     onClick : function(e){
26538         e.preventDefault();
26539         if(!this.disabled){
26540             if(e.getTarget(".x-btn-menu-arrow-wrap")){
26541                 if(this.menu && !this.menu.isVisible()){
26542                     this.menu.show(this.el, this.menuAlign);
26543                 }
26544                 this.fireEvent("arrowclick", this, e);
26545                 if(this.arrowHandler){
26546                     this.arrowHandler.call(this.scope || this, this, e);
26547                 }
26548             }else{
26549                 this.fireEvent("click", this, e);
26550                 if(this.handler){
26551                     this.handler.call(this.scope || this, this, e);
26552                 }
26553             }
26554         }
26555     },
26556     // private
26557     onMouseDown : function(e){
26558         if(!this.disabled){
26559             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
26560         }
26561     },
26562     // private
26563     onMouseUp : function(e){
26564         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
26565     }   
26566 });
26567
26568
26569 // backwards compat
26570 Roo.MenuButton = Roo.SplitButton;/*
26571  * Based on:
26572  * Ext JS Library 1.1.1
26573  * Copyright(c) 2006-2007, Ext JS, LLC.
26574  *
26575  * Originally Released Under LGPL - original licence link has changed is not relivant.
26576  *
26577  * Fork - LGPL
26578  * <script type="text/javascript">
26579  */
26580
26581 /**
26582  * @class Roo.Toolbar
26583  * Basic Toolbar class.
26584  * @constructor
26585  * Creates a new Toolbar
26586  * @param {Object} config The config object
26587  */ 
26588 Roo.Toolbar = function(container, buttons, config)
26589 {
26590     /// old consturctor format still supported..
26591     if(container instanceof Array){ // omit the container for later rendering
26592         buttons = container;
26593         config = buttons;
26594         container = null;
26595     }
26596     if (typeof(container) == 'object' && container.xtype) {
26597         config = container;
26598         container = config.container;
26599         buttons = config.buttons; // not really - use items!!
26600     }
26601     var xitems = [];
26602     if (config && config.items) {
26603         xitems = config.items;
26604         delete config.items;
26605     }
26606     Roo.apply(this, config);
26607     this.buttons = buttons;
26608     
26609     if(container){
26610         this.render(container);
26611     }
26612     Roo.each(xitems, function(b) {
26613         this.add(b);
26614     }, this);
26615     
26616 };
26617
26618 Roo.Toolbar.prototype = {
26619     /**
26620      * @cfg {Roo.data.Store} items
26621      * array of button configs or elements to add
26622      */
26623     
26624     /**
26625      * @cfg {String/HTMLElement/Element} container
26626      * The id or element that will contain the toolbar
26627      */
26628     // private
26629     render : function(ct){
26630         this.el = Roo.get(ct);
26631         if(this.cls){
26632             this.el.addClass(this.cls);
26633         }
26634         // using a table allows for vertical alignment
26635         // 100% width is needed by Safari...
26636         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
26637         this.tr = this.el.child("tr", true);
26638         var autoId = 0;
26639         this.items = new Roo.util.MixedCollection(false, function(o){
26640             return o.id || ("item" + (++autoId));
26641         });
26642         if(this.buttons){
26643             this.add.apply(this, this.buttons);
26644             delete this.buttons;
26645         }
26646     },
26647
26648     /**
26649      * Adds element(s) to the toolbar -- this function takes a variable number of 
26650      * arguments of mixed type and adds them to the toolbar.
26651      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
26652      * <ul>
26653      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
26654      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
26655      * <li>Field: Any form field (equivalent to {@link #addField})</li>
26656      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
26657      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
26658      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
26659      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
26660      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
26661      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
26662      * </ul>
26663      * @param {Mixed} arg2
26664      * @param {Mixed} etc.
26665      */
26666     add : function(){
26667         var a = arguments, l = a.length;
26668         for(var i = 0; i < l; i++){
26669             this._add(a[i]);
26670         }
26671     },
26672     // private..
26673     _add : function(el) {
26674         
26675         if (el.xtype) {
26676             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
26677         }
26678         
26679         if (el.applyTo){ // some kind of form field
26680             return this.addField(el);
26681         } 
26682         if (el.render){ // some kind of Toolbar.Item
26683             return this.addItem(el);
26684         }
26685         if (typeof el == "string"){ // string
26686             if(el == "separator" || el == "-"){
26687                 return this.addSeparator();
26688             }
26689             if (el == " "){
26690                 return this.addSpacer();
26691             }
26692             if(el == "->"){
26693                 return this.addFill();
26694             }
26695             return this.addText(el);
26696             
26697         }
26698         if(el.tagName){ // element
26699             return this.addElement(el);
26700         }
26701         if(typeof el == "object"){ // must be button config?
26702             return this.addButton(el);
26703         }
26704         // and now what?!?!
26705         return false;
26706         
26707     },
26708     
26709     /**
26710      * Add an Xtype element
26711      * @param {Object} xtype Xtype Object
26712      * @return {Object} created Object
26713      */
26714     addxtype : function(e){
26715         return this.add(e);  
26716     },
26717     
26718     /**
26719      * Returns the Element for this toolbar.
26720      * @return {Roo.Element}
26721      */
26722     getEl : function(){
26723         return this.el;  
26724     },
26725     
26726     /**
26727      * Adds a separator
26728      * @return {Roo.Toolbar.Item} The separator item
26729      */
26730     addSeparator : function(){
26731         return this.addItem(new Roo.Toolbar.Separator());
26732     },
26733
26734     /**
26735      * Adds a spacer element
26736      * @return {Roo.Toolbar.Spacer} The spacer item
26737      */
26738     addSpacer : function(){
26739         return this.addItem(new Roo.Toolbar.Spacer());
26740     },
26741
26742     /**
26743      * Adds a fill element that forces subsequent additions to the right side of the toolbar
26744      * @return {Roo.Toolbar.Fill} The fill item
26745      */
26746     addFill : function(){
26747         return this.addItem(new Roo.Toolbar.Fill());
26748     },
26749
26750     /**
26751      * Adds any standard HTML element to the toolbar
26752      * @param {String/HTMLElement/Element} el The element or id of the element to add
26753      * @return {Roo.Toolbar.Item} The element's item
26754      */
26755     addElement : function(el){
26756         return this.addItem(new Roo.Toolbar.Item(el));
26757     },
26758     /**
26759      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
26760      * @type Roo.util.MixedCollection  
26761      */
26762     items : false,
26763      
26764     /**
26765      * Adds any Toolbar.Item or subclass
26766      * @param {Roo.Toolbar.Item} item
26767      * @return {Roo.Toolbar.Item} The item
26768      */
26769     addItem : function(item){
26770         var td = this.nextBlock();
26771         item.render(td);
26772         this.items.add(item);
26773         return item;
26774     },
26775     
26776     /**
26777      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
26778      * @param {Object/Array} config A button config or array of configs
26779      * @return {Roo.Toolbar.Button/Array}
26780      */
26781     addButton : function(config){
26782         if(config instanceof Array){
26783             var buttons = [];
26784             for(var i = 0, len = config.length; i < len; i++) {
26785                 buttons.push(this.addButton(config[i]));
26786             }
26787             return buttons;
26788         }
26789         var b = config;
26790         if(!(config instanceof Roo.Toolbar.Button)){
26791             b = config.split ?
26792                 new Roo.Toolbar.SplitButton(config) :
26793                 new Roo.Toolbar.Button(config);
26794         }
26795         var td = this.nextBlock();
26796         b.render(td);
26797         this.items.add(b);
26798         return b;
26799     },
26800     
26801     /**
26802      * Adds text to the toolbar
26803      * @param {String} text The text to add
26804      * @return {Roo.Toolbar.Item} The element's item
26805      */
26806     addText : function(text){
26807         return this.addItem(new Roo.Toolbar.TextItem(text));
26808     },
26809     
26810     /**
26811      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
26812      * @param {Number} index The index where the item is to be inserted
26813      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
26814      * @return {Roo.Toolbar.Button/Item}
26815      */
26816     insertButton : function(index, item){
26817         if(item instanceof Array){
26818             var buttons = [];
26819             for(var i = 0, len = item.length; i < len; i++) {
26820                buttons.push(this.insertButton(index + i, item[i]));
26821             }
26822             return buttons;
26823         }
26824         if (!(item instanceof Roo.Toolbar.Button)){
26825            item = new Roo.Toolbar.Button(item);
26826         }
26827         var td = document.createElement("td");
26828         this.tr.insertBefore(td, this.tr.childNodes[index]);
26829         item.render(td);
26830         this.items.insert(index, item);
26831         return item;
26832     },
26833     
26834     /**
26835      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
26836      * @param {Object} config
26837      * @return {Roo.Toolbar.Item} The element's item
26838      */
26839     addDom : function(config, returnEl){
26840         var td = this.nextBlock();
26841         Roo.DomHelper.overwrite(td, config);
26842         var ti = new Roo.Toolbar.Item(td.firstChild);
26843         ti.render(td);
26844         this.items.add(ti);
26845         return ti;
26846     },
26847
26848     /**
26849      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
26850      * @type Roo.util.MixedCollection  
26851      */
26852     fields : false,
26853     
26854     /**
26855      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc). Note: the field should not have
26856      * been rendered yet. For a field that has already been rendered, use {@link #addElement}.
26857      * @param {Roo.form.Field} field
26858      * @return {Roo.ToolbarItem}
26859      */
26860      
26861       
26862     addField : function(field) {
26863         if (!this.fields) {
26864             var autoId = 0;
26865             this.fields = new Roo.util.MixedCollection(false, function(o){
26866                 return o.id || ("item" + (++autoId));
26867             });
26868
26869         }
26870         
26871         var td = this.nextBlock();
26872         field.render(td);
26873         var ti = new Roo.Toolbar.Item(td.firstChild);
26874         ti.render(td);
26875         this.items.add(ti);
26876         this.fields.add(field);
26877         return ti;
26878     },
26879     /**
26880      * Hide the toolbar
26881      * @method hide
26882      */
26883      
26884       
26885     hide : function()
26886     {
26887         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
26888         this.el.child('div').hide();
26889     },
26890     /**
26891      * Show the toolbar
26892      * @method show
26893      */
26894     show : function()
26895     {
26896         this.el.child('div').show();
26897     },
26898       
26899     // private
26900     nextBlock : function(){
26901         var td = document.createElement("td");
26902         this.tr.appendChild(td);
26903         return td;
26904     },
26905
26906     // private
26907     destroy : function(){
26908         if(this.items){ // rendered?
26909             Roo.destroy.apply(Roo, this.items.items);
26910         }
26911         if(this.fields){ // rendered?
26912             Roo.destroy.apply(Roo, this.fields.items);
26913         }
26914         Roo.Element.uncache(this.el, this.tr);
26915     }
26916 };
26917
26918 /**
26919  * @class Roo.Toolbar.Item
26920  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
26921  * @constructor
26922  * Creates a new Item
26923  * @param {HTMLElement} el 
26924  */
26925 Roo.Toolbar.Item = function(el){
26926     this.el = Roo.getDom(el);
26927     this.id = Roo.id(this.el);
26928     this.hidden = false;
26929 };
26930
26931 Roo.Toolbar.Item.prototype = {
26932     
26933     /**
26934      * Get this item's HTML Element
26935      * @return {HTMLElement}
26936      */
26937     getEl : function(){
26938        return this.el;  
26939     },
26940
26941     // private
26942     render : function(td){
26943         this.td = td;
26944         td.appendChild(this.el);
26945     },
26946     
26947     /**
26948      * Removes and destroys this item.
26949      */
26950     destroy : function(){
26951         this.td.parentNode.removeChild(this.td);
26952     },
26953     
26954     /**
26955      * Shows this item.
26956      */
26957     show: function(){
26958         this.hidden = false;
26959         this.td.style.display = "";
26960     },
26961     
26962     /**
26963      * Hides this item.
26964      */
26965     hide: function(){
26966         this.hidden = true;
26967         this.td.style.display = "none";
26968     },
26969     
26970     /**
26971      * Convenience function for boolean show/hide.
26972      * @param {Boolean} visible true to show/false to hide
26973      */
26974     setVisible: function(visible){
26975         if(visible) {
26976             this.show();
26977         }else{
26978             this.hide();
26979         }
26980     },
26981     
26982     /**
26983      * Try to focus this item.
26984      */
26985     focus : function(){
26986         Roo.fly(this.el).focus();
26987     },
26988     
26989     /**
26990      * Disables this item.
26991      */
26992     disable : function(){
26993         Roo.fly(this.td).addClass("x-item-disabled");
26994         this.disabled = true;
26995         this.el.disabled = true;
26996     },
26997     
26998     /**
26999      * Enables this item.
27000      */
27001     enable : function(){
27002         Roo.fly(this.td).removeClass("x-item-disabled");
27003         this.disabled = false;
27004         this.el.disabled = false;
27005     }
27006 };
27007
27008
27009 /**
27010  * @class Roo.Toolbar.Separator
27011  * @extends Roo.Toolbar.Item
27012  * A simple toolbar separator class
27013  * @constructor
27014  * Creates a new Separator
27015  */
27016 Roo.Toolbar.Separator = function(){
27017     var s = document.createElement("span");
27018     s.className = "ytb-sep";
27019     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
27020 };
27021 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
27022     enable:Roo.emptyFn,
27023     disable:Roo.emptyFn,
27024     focus:Roo.emptyFn
27025 });
27026
27027 /**
27028  * @class Roo.Toolbar.Spacer
27029  * @extends Roo.Toolbar.Item
27030  * A simple element that adds extra horizontal space to a toolbar.
27031  * @constructor
27032  * Creates a new Spacer
27033  */
27034 Roo.Toolbar.Spacer = function(){
27035     var s = document.createElement("div");
27036     s.className = "ytb-spacer";
27037     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
27038 };
27039 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
27040     enable:Roo.emptyFn,
27041     disable:Roo.emptyFn,
27042     focus:Roo.emptyFn
27043 });
27044
27045 /**
27046  * @class Roo.Toolbar.Fill
27047  * @extends Roo.Toolbar.Spacer
27048  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
27049  * @constructor
27050  * Creates a new Spacer
27051  */
27052 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
27053     // private
27054     render : function(td){
27055         td.style.width = '100%';
27056         Roo.Toolbar.Fill.superclass.render.call(this, td);
27057     }
27058 });
27059
27060 /**
27061  * @class Roo.Toolbar.TextItem
27062  * @extends Roo.Toolbar.Item
27063  * A simple class that renders text directly into a toolbar.
27064  * @constructor
27065  * Creates a new TextItem
27066  * @param {String} text
27067  */
27068 Roo.Toolbar.TextItem = function(text){
27069     if (typeof(text) == 'object') {
27070         text = text.text;
27071     }
27072     var s = document.createElement("span");
27073     s.className = "ytb-text";
27074     s.innerHTML = text;
27075     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
27076 };
27077 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
27078     enable:Roo.emptyFn,
27079     disable:Roo.emptyFn,
27080     focus:Roo.emptyFn
27081 });
27082
27083 /**
27084  * @class Roo.Toolbar.Button
27085  * @extends Roo.Button
27086  * A button that renders into a toolbar.
27087  * @constructor
27088  * Creates a new Button
27089  * @param {Object} config A standard {@link Roo.Button} config object
27090  */
27091 Roo.Toolbar.Button = function(config){
27092     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
27093 };
27094 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
27095     render : function(td){
27096         this.td = td;
27097         Roo.Toolbar.Button.superclass.render.call(this, td);
27098     },
27099     
27100     /**
27101      * Removes and destroys this button
27102      */
27103     destroy : function(){
27104         Roo.Toolbar.Button.superclass.destroy.call(this);
27105         this.td.parentNode.removeChild(this.td);
27106     },
27107     
27108     /**
27109      * Shows this button
27110      */
27111     show: function(){
27112         this.hidden = false;
27113         this.td.style.display = "";
27114     },
27115     
27116     /**
27117      * Hides this button
27118      */
27119     hide: function(){
27120         this.hidden = true;
27121         this.td.style.display = "none";
27122     },
27123
27124     /**
27125      * Disables this item
27126      */
27127     disable : function(){
27128         Roo.fly(this.td).addClass("x-item-disabled");
27129         this.disabled = true;
27130     },
27131
27132     /**
27133      * Enables this item
27134      */
27135     enable : function(){
27136         Roo.fly(this.td).removeClass("x-item-disabled");
27137         this.disabled = false;
27138     }
27139 });
27140 // backwards compat
27141 Roo.ToolbarButton = Roo.Toolbar.Button;
27142
27143 /**
27144  * @class Roo.Toolbar.SplitButton
27145  * @extends Roo.SplitButton
27146  * A menu button that renders into a toolbar.
27147  * @constructor
27148  * Creates a new SplitButton
27149  * @param {Object} config A standard {@link Roo.SplitButton} config object
27150  */
27151 Roo.Toolbar.SplitButton = function(config){
27152     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
27153 };
27154 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
27155     render : function(td){
27156         this.td = td;
27157         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
27158     },
27159     
27160     /**
27161      * Removes and destroys this button
27162      */
27163     destroy : function(){
27164         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
27165         this.td.parentNode.removeChild(this.td);
27166     },
27167     
27168     /**
27169      * Shows this button
27170      */
27171     show: function(){
27172         this.hidden = false;
27173         this.td.style.display = "";
27174     },
27175     
27176     /**
27177      * Hides this button
27178      */
27179     hide: function(){
27180         this.hidden = true;
27181         this.td.style.display = "none";
27182     }
27183 });
27184
27185 // backwards compat
27186 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
27187  * Based on:
27188  * Ext JS Library 1.1.1
27189  * Copyright(c) 2006-2007, Ext JS, LLC.
27190  *
27191  * Originally Released Under LGPL - original licence link has changed is not relivant.
27192  *
27193  * Fork - LGPL
27194  * <script type="text/javascript">
27195  */
27196  
27197 /**
27198  * @class Roo.PagingToolbar
27199  * @extends Roo.Toolbar
27200  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27201  * @constructor
27202  * Create a new PagingToolbar
27203  * @param {Object} config The config object
27204  */
27205 Roo.PagingToolbar = function(el, ds, config)
27206 {
27207     // old args format still supported... - xtype is prefered..
27208     if (typeof(el) == 'object' && el.xtype) {
27209         // created from xtype...
27210         config = el;
27211         ds = el.dataSource;
27212         el = config.container;
27213     }
27214     var items = [];
27215     if (config.items) {
27216         items = config.items;
27217         config.items = [];
27218     }
27219     
27220     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
27221     this.ds = ds;
27222     this.cursor = 0;
27223     this.renderButtons(this.el);
27224     this.bind(ds);
27225     
27226     // supprot items array.
27227    
27228     Roo.each(items, function(e) {
27229         this.add(Roo.factory(e));
27230     },this);
27231     
27232 };
27233
27234 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
27235     /**
27236      * @cfg {Roo.data.Store} dataSource
27237      * The underlying data store providing the paged data
27238      */
27239     /**
27240      * @cfg {String/HTMLElement/Element} container
27241      * container The id or element that will contain the toolbar
27242      */
27243     /**
27244      * @cfg {Boolean} displayInfo
27245      * True to display the displayMsg (defaults to false)
27246      */
27247     /**
27248      * @cfg {Number} pageSize
27249      * The number of records to display per page (defaults to 20)
27250      */
27251     pageSize: 20,
27252     /**
27253      * @cfg {String} displayMsg
27254      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27255      */
27256     displayMsg : 'Displaying {0} - {1} of {2}',
27257     /**
27258      * @cfg {String} emptyMsg
27259      * The message to display when no records are found (defaults to "No data to display")
27260      */
27261     emptyMsg : 'No data to display',
27262     /**
27263      * Customizable piece of the default paging text (defaults to "Page")
27264      * @type String
27265      */
27266     beforePageText : "Page",
27267     /**
27268      * Customizable piece of the default paging text (defaults to "of %0")
27269      * @type String
27270      */
27271     afterPageText : "of {0}",
27272     /**
27273      * Customizable piece of the default paging text (defaults to "First Page")
27274      * @type String
27275      */
27276     firstText : "First Page",
27277     /**
27278      * Customizable piece of the default paging text (defaults to "Previous Page")
27279      * @type String
27280      */
27281     prevText : "Previous Page",
27282     /**
27283      * Customizable piece of the default paging text (defaults to "Next Page")
27284      * @type String
27285      */
27286     nextText : "Next Page",
27287     /**
27288      * Customizable piece of the default paging text (defaults to "Last Page")
27289      * @type String
27290      */
27291     lastText : "Last Page",
27292     /**
27293      * Customizable piece of the default paging text (defaults to "Refresh")
27294      * @type String
27295      */
27296     refreshText : "Refresh",
27297
27298     // private
27299     renderButtons : function(el){
27300         Roo.PagingToolbar.superclass.render.call(this, el);
27301         this.first = this.addButton({
27302             tooltip: this.firstText,
27303             cls: "x-btn-icon x-grid-page-first",
27304             disabled: true,
27305             handler: this.onClick.createDelegate(this, ["first"])
27306         });
27307         this.prev = this.addButton({
27308             tooltip: this.prevText,
27309             cls: "x-btn-icon x-grid-page-prev",
27310             disabled: true,
27311             handler: this.onClick.createDelegate(this, ["prev"])
27312         });
27313         //this.addSeparator();
27314         this.add(this.beforePageText);
27315         this.field = Roo.get(this.addDom({
27316            tag: "input",
27317            type: "text",
27318            size: "3",
27319            value: "1",
27320            cls: "x-grid-page-number"
27321         }).el);
27322         this.field.on("keydown", this.onPagingKeydown, this);
27323         this.field.on("focus", function(){this.dom.select();});
27324         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
27325         this.field.setHeight(18);
27326         //this.addSeparator();
27327         this.next = this.addButton({
27328             tooltip: this.nextText,
27329             cls: "x-btn-icon x-grid-page-next",
27330             disabled: true,
27331             handler: this.onClick.createDelegate(this, ["next"])
27332         });
27333         this.last = this.addButton({
27334             tooltip: this.lastText,
27335             cls: "x-btn-icon x-grid-page-last",
27336             disabled: true,
27337             handler: this.onClick.createDelegate(this, ["last"])
27338         });
27339         //this.addSeparator();
27340         this.loading = this.addButton({
27341             tooltip: this.refreshText,
27342             cls: "x-btn-icon x-grid-loading",
27343             handler: this.onClick.createDelegate(this, ["refresh"])
27344         });
27345
27346         if(this.displayInfo){
27347             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
27348         }
27349     },
27350
27351     // private
27352     updateInfo : function(){
27353         if(this.displayEl){
27354             var count = this.ds.getCount();
27355             var msg = count == 0 ?
27356                 this.emptyMsg :
27357                 String.format(
27358                     this.displayMsg,
27359                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27360                 );
27361             this.displayEl.update(msg);
27362         }
27363     },
27364
27365     // private
27366     onLoad : function(ds, r, o){
27367        this.cursor = o.params ? o.params.start : 0;
27368        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
27369
27370        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
27371        this.field.dom.value = ap;
27372        this.first.setDisabled(ap == 1);
27373        this.prev.setDisabled(ap == 1);
27374        this.next.setDisabled(ap == ps);
27375        this.last.setDisabled(ap == ps);
27376        this.loading.enable();
27377        this.updateInfo();
27378     },
27379
27380     // private
27381     getPageData : function(){
27382         var total = this.ds.getTotalCount();
27383         return {
27384             total : total,
27385             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27386             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27387         };
27388     },
27389
27390     // private
27391     onLoadError : function(){
27392         this.loading.enable();
27393     },
27394
27395     // private
27396     onPagingKeydown : function(e){
27397         var k = e.getKey();
27398         var d = this.getPageData();
27399         if(k == e.RETURN){
27400             var v = this.field.dom.value, pageNum;
27401             if(!v || isNaN(pageNum = parseInt(v, 10))){
27402                 this.field.dom.value = d.activePage;
27403                 return;
27404             }
27405             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27406             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27407             e.stopEvent();
27408         }
27409         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))
27410         {
27411           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27412           this.field.dom.value = pageNum;
27413           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27414           e.stopEvent();
27415         }
27416         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27417         {
27418           var v = this.field.dom.value, pageNum; 
27419           var increment = (e.shiftKey) ? 10 : 1;
27420           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27421             increment *= -1;
27422           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27423             this.field.dom.value = d.activePage;
27424             return;
27425           }
27426           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27427           {
27428             this.field.dom.value = parseInt(v, 10) + increment;
27429             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27430             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27431           }
27432           e.stopEvent();
27433         }
27434     },
27435
27436     // private
27437     beforeLoad : function(){
27438         if(this.loading){
27439             this.loading.disable();
27440         }
27441     },
27442
27443     // private
27444     onClick : function(which){
27445         var ds = this.ds;
27446         switch(which){
27447             case "first":
27448                 ds.load({params:{start: 0, limit: this.pageSize}});
27449             break;
27450             case "prev":
27451                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27452             break;
27453             case "next":
27454                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27455             break;
27456             case "last":
27457                 var total = ds.getTotalCount();
27458                 var extra = total % this.pageSize;
27459                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27460                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27461             break;
27462             case "refresh":
27463                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27464             break;
27465         }
27466     },
27467
27468     /**
27469      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27470      * @param {Roo.data.Store} store The data store to unbind
27471      */
27472     unbind : function(ds){
27473         ds.un("beforeload", this.beforeLoad, this);
27474         ds.un("load", this.onLoad, this);
27475         ds.un("loadexception", this.onLoadError, this);
27476         ds.un("remove", this.updateInfo, this);
27477         ds.un("add", this.updateInfo, this);
27478         this.ds = undefined;
27479     },
27480
27481     /**
27482      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27483      * @param {Roo.data.Store} store The data store to bind
27484      */
27485     bind : function(ds){
27486         ds.on("beforeload", this.beforeLoad, this);
27487         ds.on("load", this.onLoad, this);
27488         ds.on("loadexception", this.onLoadError, this);
27489         ds.on("remove", this.updateInfo, this);
27490         ds.on("add", this.updateInfo, this);
27491         this.ds = ds;
27492     }
27493 });/*
27494  * Based on:
27495  * Ext JS Library 1.1.1
27496  * Copyright(c) 2006-2007, Ext JS, LLC.
27497  *
27498  * Originally Released Under LGPL - original licence link has changed is not relivant.
27499  *
27500  * Fork - LGPL
27501  * <script type="text/javascript">
27502  */
27503
27504 /**
27505  * @class Roo.Resizable
27506  * @extends Roo.util.Observable
27507  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
27508  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
27509  * 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
27510  * the element will be wrapped for you automatically.</p>
27511  * <p>Here is the list of valid resize handles:</p>
27512  * <pre>
27513 Value   Description
27514 ------  -------------------
27515  'n'     north
27516  's'     south
27517  'e'     east
27518  'w'     west
27519  'nw'    northwest
27520  'sw'    southwest
27521  'se'    southeast
27522  'ne'    northeast
27523  'hd'    horizontal drag
27524  'all'   all
27525 </pre>
27526  * <p>Here's an example showing the creation of a typical Resizable:</p>
27527  * <pre><code>
27528 var resizer = new Roo.Resizable("element-id", {
27529     handles: 'all',
27530     minWidth: 200,
27531     minHeight: 100,
27532     maxWidth: 500,
27533     maxHeight: 400,
27534     pinned: true
27535 });
27536 resizer.on("resize", myHandler);
27537 </code></pre>
27538  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
27539  * resizer.east.setDisplayed(false);</p>
27540  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
27541  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
27542  * resize operation's new size (defaults to [0, 0])
27543  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
27544  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
27545  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
27546  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
27547  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
27548  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
27549  * @cfg {Number} width The width of the element in pixels (defaults to null)
27550  * @cfg {Number} height The height of the element in pixels (defaults to null)
27551  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
27552  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
27553  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
27554  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
27555  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
27556  * in favor of the handles config option (defaults to false)
27557  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
27558  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
27559  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
27560  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
27561  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
27562  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
27563  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
27564  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
27565  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
27566  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
27567  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
27568  * @constructor
27569  * Create a new resizable component
27570  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
27571  * @param {Object} config configuration options
27572   */
27573 Roo.Resizable = function(el, config)
27574 {
27575     this.el = Roo.get(el);
27576
27577     if(config && config.wrap){
27578         config.resizeChild = this.el;
27579         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
27580         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
27581         this.el.setStyle("overflow", "hidden");
27582         this.el.setPositioning(config.resizeChild.getPositioning());
27583         config.resizeChild.clearPositioning();
27584         if(!config.width || !config.height){
27585             var csize = config.resizeChild.getSize();
27586             this.el.setSize(csize.width, csize.height);
27587         }
27588         if(config.pinned && !config.adjustments){
27589             config.adjustments = "auto";
27590         }
27591     }
27592
27593     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
27594     this.proxy.unselectable();
27595     this.proxy.enableDisplayMode('block');
27596
27597     Roo.apply(this, config);
27598
27599     if(this.pinned){
27600         this.disableTrackOver = true;
27601         this.el.addClass("x-resizable-pinned");
27602     }
27603     // if the element isn't positioned, make it relative
27604     var position = this.el.getStyle("position");
27605     if(position != "absolute" && position != "fixed"){
27606         this.el.setStyle("position", "relative");
27607     }
27608     if(!this.handles){ // no handles passed, must be legacy style
27609         this.handles = 's,e,se';
27610         if(this.multiDirectional){
27611             this.handles += ',n,w';
27612         }
27613     }
27614     if(this.handles == "all"){
27615         this.handles = "n s e w ne nw se sw";
27616     }
27617     var hs = this.handles.split(/\s*?[,;]\s*?| /);
27618     var ps = Roo.Resizable.positions;
27619     for(var i = 0, len = hs.length; i < len; i++){
27620         if(hs[i] && ps[hs[i]]){
27621             var pos = ps[hs[i]];
27622             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
27623         }
27624     }
27625     // legacy
27626     this.corner = this.southeast;
27627     
27628     // updateBox = the box can move..
27629     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
27630         this.updateBox = true;
27631     }
27632
27633     this.activeHandle = null;
27634
27635     if(this.resizeChild){
27636         if(typeof this.resizeChild == "boolean"){
27637             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
27638         }else{
27639             this.resizeChild = Roo.get(this.resizeChild, true);
27640         }
27641     }
27642     
27643     if(this.adjustments == "auto"){
27644         var rc = this.resizeChild;
27645         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
27646         if(rc && (hw || hn)){
27647             rc.position("relative");
27648             rc.setLeft(hw ? hw.el.getWidth() : 0);
27649             rc.setTop(hn ? hn.el.getHeight() : 0);
27650         }
27651         this.adjustments = [
27652             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
27653             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
27654         ];
27655     }
27656
27657     if(this.draggable){
27658         this.dd = this.dynamic ?
27659             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
27660         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
27661     }
27662
27663     // public events
27664     this.addEvents({
27665         /**
27666          * @event beforeresize
27667          * Fired before resize is allowed. Set enabled to false to cancel resize.
27668          * @param {Roo.Resizable} this
27669          * @param {Roo.EventObject} e The mousedown event
27670          */
27671         "beforeresize" : true,
27672         /**
27673          * @event resize
27674          * Fired after a resize.
27675          * @param {Roo.Resizable} this
27676          * @param {Number} width The new width
27677          * @param {Number} height The new height
27678          * @param {Roo.EventObject} e The mouseup event
27679          */
27680         "resize" : true
27681     });
27682
27683     if(this.width !== null && this.height !== null){
27684         this.resizeTo(this.width, this.height);
27685     }else{
27686         this.updateChildSize();
27687     }
27688     if(Roo.isIE){
27689         this.el.dom.style.zoom = 1;
27690     }
27691     Roo.Resizable.superclass.constructor.call(this);
27692 };
27693
27694 Roo.extend(Roo.Resizable, Roo.util.Observable, {
27695         resizeChild : false,
27696         adjustments : [0, 0],
27697         minWidth : 5,
27698         minHeight : 5,
27699         maxWidth : 10000,
27700         maxHeight : 10000,
27701         enabled : true,
27702         animate : false,
27703         duration : .35,
27704         dynamic : false,
27705         handles : false,
27706         multiDirectional : false,
27707         disableTrackOver : false,
27708         easing : 'easeOutStrong',
27709         widthIncrement : 0,
27710         heightIncrement : 0,
27711         pinned : false,
27712         width : null,
27713         height : null,
27714         preserveRatio : false,
27715         transparent: false,
27716         minX: 0,
27717         minY: 0,
27718         draggable: false,
27719
27720         /**
27721          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
27722          */
27723         constrainTo: undefined,
27724         /**
27725          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
27726          */
27727         resizeRegion: undefined,
27728
27729
27730     /**
27731      * Perform a manual resize
27732      * @param {Number} width
27733      * @param {Number} height
27734      */
27735     resizeTo : function(width, height){
27736         this.el.setSize(width, height);
27737         this.updateChildSize();
27738         this.fireEvent("resize", this, width, height, null);
27739     },
27740
27741     // private
27742     startSizing : function(e, handle){
27743         this.fireEvent("beforeresize", this, e);
27744         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
27745
27746             if(!this.overlay){
27747                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
27748                 this.overlay.unselectable();
27749                 this.overlay.enableDisplayMode("block");
27750                 this.overlay.on("mousemove", this.onMouseMove, this);
27751                 this.overlay.on("mouseup", this.onMouseUp, this);
27752             }
27753             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
27754
27755             this.resizing = true;
27756             this.startBox = this.el.getBox();
27757             this.startPoint = e.getXY();
27758             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
27759                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
27760
27761             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27762             this.overlay.show();
27763
27764             if(this.constrainTo) {
27765                 var ct = Roo.get(this.constrainTo);
27766                 this.resizeRegion = ct.getRegion().adjust(
27767                     ct.getFrameWidth('t'),
27768                     ct.getFrameWidth('l'),
27769                     -ct.getFrameWidth('b'),
27770                     -ct.getFrameWidth('r')
27771                 );
27772             }
27773
27774             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
27775             this.proxy.show();
27776             this.proxy.setBox(this.startBox);
27777             if(!this.dynamic){
27778                 this.proxy.setStyle('visibility', 'visible');
27779             }
27780         }
27781     },
27782
27783     // private
27784     onMouseDown : function(handle, e){
27785         if(this.enabled){
27786             e.stopEvent();
27787             this.activeHandle = handle;
27788             this.startSizing(e, handle);
27789         }
27790     },
27791
27792     // private
27793     onMouseUp : function(e){
27794         var size = this.resizeElement();
27795         this.resizing = false;
27796         this.handleOut();
27797         this.overlay.hide();
27798         this.proxy.hide();
27799         this.fireEvent("resize", this, size.width, size.height, e);
27800     },
27801
27802     // private
27803     updateChildSize : function(){
27804         if(this.resizeChild){
27805             var el = this.el;
27806             var child = this.resizeChild;
27807             var adj = this.adjustments;
27808             if(el.dom.offsetWidth){
27809                 var b = el.getSize(true);
27810                 child.setSize(b.width+adj[0], b.height+adj[1]);
27811             }
27812             // Second call here for IE
27813             // The first call enables instant resizing and
27814             // the second call corrects scroll bars if they
27815             // exist
27816             if(Roo.isIE){
27817                 setTimeout(function(){
27818                     if(el.dom.offsetWidth){
27819                         var b = el.getSize(true);
27820                         child.setSize(b.width+adj[0], b.height+adj[1]);
27821                     }
27822                 }, 10);
27823             }
27824         }
27825     },
27826
27827     // private
27828     snap : function(value, inc, min){
27829         if(!inc || !value) return value;
27830         var newValue = value;
27831         var m = value % inc;
27832         if(m > 0){
27833             if(m > (inc/2)){
27834                 newValue = value + (inc-m);
27835             }else{
27836                 newValue = value - m;
27837             }
27838         }
27839         return Math.max(min, newValue);
27840     },
27841
27842     // private
27843     resizeElement : function(){
27844         var box = this.proxy.getBox();
27845         if(this.updateBox){
27846             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
27847         }else{
27848             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
27849         }
27850         this.updateChildSize();
27851         if(!this.dynamic){
27852             this.proxy.hide();
27853         }
27854         return box;
27855     },
27856
27857     // private
27858     constrain : function(v, diff, m, mx){
27859         if(v - diff < m){
27860             diff = v - m;
27861         }else if(v - diff > mx){
27862             diff = mx - v;
27863         }
27864         return diff;
27865     },
27866
27867     // private
27868     onMouseMove : function(e){
27869         if(this.enabled){
27870             try{// try catch so if something goes wrong the user doesn't get hung
27871
27872             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
27873                 return;
27874             }
27875
27876             //var curXY = this.startPoint;
27877             var curSize = this.curSize || this.startBox;
27878             var x = this.startBox.x, y = this.startBox.y;
27879             var ox = x, oy = y;
27880             var w = curSize.width, h = curSize.height;
27881             var ow = w, oh = h;
27882             var mw = this.minWidth, mh = this.minHeight;
27883             var mxw = this.maxWidth, mxh = this.maxHeight;
27884             var wi = this.widthIncrement;
27885             var hi = this.heightIncrement;
27886
27887             var eventXY = e.getXY();
27888             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
27889             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
27890
27891             var pos = this.activeHandle.position;
27892
27893             switch(pos){
27894                 case "east":
27895                     w += diffX;
27896                     w = Math.min(Math.max(mw, w), mxw);
27897                     break;
27898              
27899                 case "south":
27900                     h += diffY;
27901                     h = Math.min(Math.max(mh, h), mxh);
27902                     break;
27903                 case "southeast":
27904                     w += diffX;
27905                     h += diffY;
27906                     w = Math.min(Math.max(mw, w), mxw);
27907                     h = Math.min(Math.max(mh, h), mxh);
27908                     break;
27909                 case "north":
27910                     diffY = this.constrain(h, diffY, mh, mxh);
27911                     y += diffY;
27912                     h -= diffY;
27913                     break;
27914                 case "hdrag":
27915                     
27916                     if (wi) {
27917                         var adiffX = Math.abs(diffX);
27918                         var sub = (adiffX % wi); // how much 
27919                         if (sub > (wi/2)) { // far enough to snap
27920                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
27921                         } else {
27922                             // remove difference.. 
27923                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
27924                         }
27925                     }
27926                     x += diffX;
27927                     x = Math.max(this.minX, x);
27928                     break;
27929                 case "west":
27930                     diffX = this.constrain(w, diffX, mw, mxw);
27931                     x += diffX;
27932                     w -= diffX;
27933                     break;
27934                 case "northeast":
27935                     w += diffX;
27936                     w = Math.min(Math.max(mw, w), mxw);
27937                     diffY = this.constrain(h, diffY, mh, mxh);
27938                     y += diffY;
27939                     h -= diffY;
27940                     break;
27941                 case "northwest":
27942                     diffX = this.constrain(w, diffX, mw, mxw);
27943                     diffY = this.constrain(h, diffY, mh, mxh);
27944                     y += diffY;
27945                     h -= diffY;
27946                     x += diffX;
27947                     w -= diffX;
27948                     break;
27949                case "southwest":
27950                     diffX = this.constrain(w, diffX, mw, mxw);
27951                     h += diffY;
27952                     h = Math.min(Math.max(mh, h), mxh);
27953                     x += diffX;
27954                     w -= diffX;
27955                     break;
27956             }
27957
27958             var sw = this.snap(w, wi, mw);
27959             var sh = this.snap(h, hi, mh);
27960             if(sw != w || sh != h){
27961                 switch(pos){
27962                     case "northeast":
27963                         y -= sh - h;
27964                     break;
27965                     case "north":
27966                         y -= sh - h;
27967                         break;
27968                     case "southwest":
27969                         x -= sw - w;
27970                     break;
27971                     case "west":
27972                         x -= sw - w;
27973                         break;
27974                     case "northwest":
27975                         x -= sw - w;
27976                         y -= sh - h;
27977                     break;
27978                 }
27979                 w = sw;
27980                 h = sh;
27981             }
27982
27983             if(this.preserveRatio){
27984                 switch(pos){
27985                     case "southeast":
27986                     case "east":
27987                         h = oh * (w/ow);
27988                         h = Math.min(Math.max(mh, h), mxh);
27989                         w = ow * (h/oh);
27990                        break;
27991                     case "south":
27992                         w = ow * (h/oh);
27993                         w = Math.min(Math.max(mw, w), mxw);
27994                         h = oh * (w/ow);
27995                         break;
27996                     case "northeast":
27997                         w = ow * (h/oh);
27998                         w = Math.min(Math.max(mw, w), mxw);
27999                         h = oh * (w/ow);
28000                     break;
28001                     case "north":
28002                         var tw = w;
28003                         w = ow * (h/oh);
28004                         w = Math.min(Math.max(mw, w), mxw);
28005                         h = oh * (w/ow);
28006                         x += (tw - w) / 2;
28007                         break;
28008                     case "southwest":
28009                         h = oh * (w/ow);
28010                         h = Math.min(Math.max(mh, h), mxh);
28011                         var tw = w;
28012                         w = ow * (h/oh);
28013                         x += tw - w;
28014                         break;
28015                     case "west":
28016                         var th = h;
28017                         h = oh * (w/ow);
28018                         h = Math.min(Math.max(mh, h), mxh);
28019                         y += (th - h) / 2;
28020                         var tw = w;
28021                         w = ow * (h/oh);
28022                         x += tw - w;
28023                        break;
28024                     case "northwest":
28025                         var tw = w;
28026                         var th = h;
28027                         h = oh * (w/ow);
28028                         h = Math.min(Math.max(mh, h), mxh);
28029                         w = ow * (h/oh);
28030                         y += th - h;
28031                         x += tw - w;
28032                        break;
28033
28034                 }
28035             }
28036             if (pos == 'hdrag') {
28037                 w = ow;
28038             }
28039             this.proxy.setBounds(x, y, w, h);
28040             if(this.dynamic){
28041                 this.resizeElement();
28042             }
28043             }catch(e){}
28044         }
28045     },
28046
28047     // private
28048     handleOver : function(){
28049         if(this.enabled){
28050             this.el.addClass("x-resizable-over");
28051         }
28052     },
28053
28054     // private
28055     handleOut : function(){
28056         if(!this.resizing){
28057             this.el.removeClass("x-resizable-over");
28058         }
28059     },
28060
28061     /**
28062      * Returns the element this component is bound to.
28063      * @return {Roo.Element}
28064      */
28065     getEl : function(){
28066         return this.el;
28067     },
28068
28069     /**
28070      * Returns the resizeChild element (or null).
28071      * @return {Roo.Element}
28072      */
28073     getResizeChild : function(){
28074         return this.resizeChild;
28075     },
28076
28077     /**
28078      * Destroys this resizable. If the element was wrapped and
28079      * removeEl is not true then the element remains.
28080      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
28081      */
28082     destroy : function(removeEl){
28083         this.proxy.remove();
28084         if(this.overlay){
28085             this.overlay.removeAllListeners();
28086             this.overlay.remove();
28087         }
28088         var ps = Roo.Resizable.positions;
28089         for(var k in ps){
28090             if(typeof ps[k] != "function" && this[ps[k]]){
28091                 var h = this[ps[k]];
28092                 h.el.removeAllListeners();
28093                 h.el.remove();
28094             }
28095         }
28096         if(removeEl){
28097             this.el.update("");
28098             this.el.remove();
28099         }
28100     }
28101 });
28102
28103 // private
28104 // hash to map config positions to true positions
28105 Roo.Resizable.positions = {
28106     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
28107     hd: "hdrag"
28108 };
28109
28110 // private
28111 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
28112     if(!this.tpl){
28113         // only initialize the template if resizable is used
28114         var tpl = Roo.DomHelper.createTemplate(
28115             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
28116         );
28117         tpl.compile();
28118         Roo.Resizable.Handle.prototype.tpl = tpl;
28119     }
28120     this.position = pos;
28121     this.rz = rz;
28122     // show north drag fro topdra
28123     var handlepos = pos == 'hdrag' ? 'north' : pos;
28124     
28125     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
28126     if (pos == 'hdrag') {
28127         this.el.setStyle('cursor', 'pointer');
28128     }
28129     this.el.unselectable();
28130     if(transparent){
28131         this.el.setOpacity(0);
28132     }
28133     this.el.on("mousedown", this.onMouseDown, this);
28134     if(!disableTrackOver){
28135         this.el.on("mouseover", this.onMouseOver, this);
28136         this.el.on("mouseout", this.onMouseOut, this);
28137     }
28138 };
28139
28140 // private
28141 Roo.Resizable.Handle.prototype = {
28142     afterResize : function(rz){
28143         // do nothing
28144     },
28145     // private
28146     onMouseDown : function(e){
28147         this.rz.onMouseDown(this, e);
28148     },
28149     // private
28150     onMouseOver : function(e){
28151         this.rz.handleOver(this, e);
28152     },
28153     // private
28154     onMouseOut : function(e){
28155         this.rz.handleOut(this, e);
28156     }
28157 };/*
28158  * Based on:
28159  * Ext JS Library 1.1.1
28160  * Copyright(c) 2006-2007, Ext JS, LLC.
28161  *
28162  * Originally Released Under LGPL - original licence link has changed is not relivant.
28163  *
28164  * Fork - LGPL
28165  * <script type="text/javascript">
28166  */
28167
28168 /**
28169  * @class Roo.Editor
28170  * @extends Roo.Component
28171  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
28172  * @constructor
28173  * Create a new Editor
28174  * @param {Roo.form.Field} field The Field object (or descendant)
28175  * @param {Object} config The config object
28176  */
28177 Roo.Editor = function(field, config){
28178     Roo.Editor.superclass.constructor.call(this, config);
28179     this.field = field;
28180     this.addEvents({
28181         /**
28182              * @event beforestartedit
28183              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
28184              * false from the handler of this event.
28185              * @param {Editor} this
28186              * @param {Roo.Element} boundEl The underlying element bound to this editor
28187              * @param {Mixed} value The field value being set
28188              */
28189         "beforestartedit" : true,
28190         /**
28191              * @event startedit
28192              * Fires when this editor is displayed
28193              * @param {Roo.Element} boundEl The underlying element bound to this editor
28194              * @param {Mixed} value The starting field value
28195              */
28196         "startedit" : true,
28197         /**
28198              * @event beforecomplete
28199              * Fires after a change has been made to the field, but before the change is reflected in the underlying
28200              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
28201              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
28202              * event will not fire since no edit actually occurred.
28203              * @param {Editor} this
28204              * @param {Mixed} value The current field value
28205              * @param {Mixed} startValue The original field value
28206              */
28207         "beforecomplete" : true,
28208         /**
28209              * @event complete
28210              * Fires after editing is complete and any changed value has been written to the underlying field.
28211              * @param {Editor} this
28212              * @param {Mixed} value The current field value
28213              * @param {Mixed} startValue The original field value
28214              */
28215         "complete" : true,
28216         /**
28217          * @event specialkey
28218          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
28219          * {@link Roo.EventObject#getKey} to determine which key was pressed.
28220          * @param {Roo.form.Field} this
28221          * @param {Roo.EventObject} e The event object
28222          */
28223         "specialkey" : true
28224     });
28225 };
28226
28227 Roo.extend(Roo.Editor, Roo.Component, {
28228     /**
28229      * @cfg {Boolean/String} autosize
28230      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
28231      * or "height" to adopt the height only (defaults to false)
28232      */
28233     /**
28234      * @cfg {Boolean} revertInvalid
28235      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
28236      * validation fails (defaults to true)
28237      */
28238     /**
28239      * @cfg {Boolean} ignoreNoChange
28240      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
28241      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
28242      * will never be ignored.
28243      */
28244     /**
28245      * @cfg {Boolean} hideEl
28246      * False to keep the bound element visible while the editor is displayed (defaults to true)
28247      */
28248     /**
28249      * @cfg {Mixed} value
28250      * The data value of the underlying field (defaults to "")
28251      */
28252     value : "",
28253     /**
28254      * @cfg {String} alignment
28255      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
28256      */
28257     alignment: "c-c?",
28258     /**
28259      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
28260      * for bottom-right shadow (defaults to "frame")
28261      */
28262     shadow : "frame",
28263     /**
28264      * @cfg {Boolean} constrain True to constrain the editor to the viewport
28265      */
28266     constrain : false,
28267     /**
28268      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
28269      */
28270     completeOnEnter : false,
28271     /**
28272      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
28273      */
28274     cancelOnEsc : false,
28275     /**
28276      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
28277      */
28278     updateEl : false,
28279
28280     // private
28281     onRender : function(ct, position){
28282         this.el = new Roo.Layer({
28283             shadow: this.shadow,
28284             cls: "x-editor",
28285             parentEl : ct,
28286             shim : this.shim,
28287             shadowOffset:4,
28288             id: this.id,
28289             constrain: this.constrain
28290         });
28291         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
28292         if(this.field.msgTarget != 'title'){
28293             this.field.msgTarget = 'qtip';
28294         }
28295         this.field.render(this.el);
28296         if(Roo.isGecko){
28297             this.field.el.dom.setAttribute('autocomplete', 'off');
28298         }
28299         this.field.on("specialkey", this.onSpecialKey, this);
28300         if(this.swallowKeys){
28301             this.field.el.swallowEvent(['keydown','keypress']);
28302         }
28303         this.field.show();
28304         this.field.on("blur", this.onBlur, this);
28305         if(this.field.grow){
28306             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
28307         }
28308     },
28309
28310     onSpecialKey : function(field, e){
28311         //Roo.log('editor onSpecialKey');
28312         if(this.completeOnEnter && e.getKey() == e.ENTER){
28313             e.stopEvent();
28314             this.completeEdit();
28315         }else if(this.cancelOnEsc && e.getKey() == e.ESC){
28316             this.cancelEdit();
28317         }else{
28318             this.fireEvent('specialkey', field, e);
28319         }
28320     },
28321
28322     /**
28323      * Starts the editing process and shows the editor.
28324      * @param {String/HTMLElement/Element} el The element to edit
28325      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
28326       * to the innerHTML of el.
28327      */
28328     startEdit : function(el, value){
28329         if(this.editing){
28330             this.completeEdit();
28331         }
28332         this.boundEl = Roo.get(el);
28333         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
28334         if(!this.rendered){
28335             this.render(this.parentEl || document.body);
28336         }
28337         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
28338             return;
28339         }
28340         this.startValue = v;
28341         this.field.setValue(v);
28342         if(this.autoSize){
28343             var sz = this.boundEl.getSize();
28344             switch(this.autoSize){
28345                 case "width":
28346                 this.setSize(sz.width,  "");
28347                 break;
28348                 case "height":
28349                 this.setSize("",  sz.height);
28350                 break;
28351                 default:
28352                 this.setSize(sz.width,  sz.height);
28353             }
28354         }
28355         this.el.alignTo(this.boundEl, this.alignment);
28356         this.editing = true;
28357         if(Roo.QuickTips){
28358             Roo.QuickTips.disable();
28359         }
28360         this.show();
28361     },
28362
28363     /**
28364      * Sets the height and width of this editor.
28365      * @param {Number} width The new width
28366      * @param {Number} height The new height
28367      */
28368     setSize : function(w, h){
28369         this.field.setSize(w, h);
28370         if(this.el){
28371             this.el.sync();
28372         }
28373     },
28374
28375     /**
28376      * Realigns the editor to the bound field based on the current alignment config value.
28377      */
28378     realign : function(){
28379         this.el.alignTo(this.boundEl, this.alignment);
28380     },
28381
28382     /**
28383      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
28384      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
28385      */
28386     completeEdit : function(remainVisible){
28387         if(!this.editing){
28388             return;
28389         }
28390         var v = this.getValue();
28391         if(this.revertInvalid !== false && !this.field.isValid()){
28392             v = this.startValue;
28393             this.cancelEdit(true);
28394         }
28395         if(String(v) === String(this.startValue) && this.ignoreNoChange){
28396             this.editing = false;
28397             this.hide();
28398             return;
28399         }
28400         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
28401             this.editing = false;
28402             if(this.updateEl && this.boundEl){
28403                 this.boundEl.update(v);
28404             }
28405             if(remainVisible !== true){
28406                 this.hide();
28407             }
28408             this.fireEvent("complete", this, v, this.startValue);
28409         }
28410     },
28411
28412     // private
28413     onShow : function(){
28414         this.el.show();
28415         if(this.hideEl !== false){
28416             this.boundEl.hide();
28417         }
28418         this.field.show();
28419         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
28420             this.fixIEFocus = true;
28421             this.deferredFocus.defer(50, this);
28422         }else{
28423             this.field.focus();
28424         }
28425         this.fireEvent("startedit", this.boundEl, this.startValue);
28426     },
28427
28428     deferredFocus : function(){
28429         if(this.editing){
28430             this.field.focus();
28431         }
28432     },
28433
28434     /**
28435      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
28436      * reverted to the original starting value.
28437      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
28438      * cancel (defaults to false)
28439      */
28440     cancelEdit : function(remainVisible){
28441         if(this.editing){
28442             this.setValue(this.startValue);
28443             if(remainVisible !== true){
28444                 this.hide();
28445             }
28446         }
28447     },
28448
28449     // private
28450     onBlur : function(){
28451         if(this.allowBlur !== true && this.editing){
28452             this.completeEdit();
28453         }
28454     },
28455
28456     // private
28457     onHide : function(){
28458         if(this.editing){
28459             this.completeEdit();
28460             return;
28461         }
28462         this.field.blur();
28463         if(this.field.collapse){
28464             this.field.collapse();
28465         }
28466         this.el.hide();
28467         if(this.hideEl !== false){
28468             this.boundEl.show();
28469         }
28470         if(Roo.QuickTips){
28471             Roo.QuickTips.enable();
28472         }
28473     },
28474
28475     /**
28476      * Sets the data value of the editor
28477      * @param {Mixed} value Any valid value supported by the underlying field
28478      */
28479     setValue : function(v){
28480         this.field.setValue(v);
28481     },
28482
28483     /**
28484      * Gets the data value of the editor
28485      * @return {Mixed} The data value
28486      */
28487     getValue : function(){
28488         return this.field.getValue();
28489     }
28490 });/*
28491  * Based on:
28492  * Ext JS Library 1.1.1
28493  * Copyright(c) 2006-2007, Ext JS, LLC.
28494  *
28495  * Originally Released Under LGPL - original licence link has changed is not relivant.
28496  *
28497  * Fork - LGPL
28498  * <script type="text/javascript">
28499  */
28500  
28501 /**
28502  * @class Roo.BasicDialog
28503  * @extends Roo.util.Observable
28504  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
28505  * <pre><code>
28506 var dlg = new Roo.BasicDialog("my-dlg", {
28507     height: 200,
28508     width: 300,
28509     minHeight: 100,
28510     minWidth: 150,
28511     modal: true,
28512     proxyDrag: true,
28513     shadow: true
28514 });
28515 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
28516 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
28517 dlg.addButton('Cancel', dlg.hide, dlg);
28518 dlg.show();
28519 </code></pre>
28520   <b>A Dialog should always be a direct child of the body element.</b>
28521  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
28522  * @cfg {String} title Default text to display in the title bar (defaults to null)
28523  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28524  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
28525  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
28526  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
28527  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
28528  * (defaults to null with no animation)
28529  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
28530  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
28531  * property for valid values (defaults to 'all')
28532  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
28533  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
28534  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
28535  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
28536  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
28537  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
28538  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
28539  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
28540  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
28541  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
28542  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
28543  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
28544  * draggable = true (defaults to false)
28545  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
28546  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
28547  * shadow (defaults to false)
28548  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
28549  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
28550  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
28551  * @cfg {Array} buttons Array of buttons
28552  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
28553  * @constructor
28554  * Create a new BasicDialog.
28555  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
28556  * @param {Object} config Configuration options
28557  */
28558 Roo.BasicDialog = function(el, config){
28559     this.el = Roo.get(el);
28560     var dh = Roo.DomHelper;
28561     if(!this.el && config && config.autoCreate){
28562         if(typeof config.autoCreate == "object"){
28563             if(!config.autoCreate.id){
28564                 config.autoCreate.id = el;
28565             }
28566             this.el = dh.append(document.body,
28567                         config.autoCreate, true);
28568         }else{
28569             this.el = dh.append(document.body,
28570                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
28571         }
28572     }
28573     el = this.el;
28574     el.setDisplayed(true);
28575     el.hide = this.hideAction;
28576     this.id = el.id;
28577     el.addClass("x-dlg");
28578
28579     Roo.apply(this, config);
28580
28581     this.proxy = el.createProxy("x-dlg-proxy");
28582     this.proxy.hide = this.hideAction;
28583     this.proxy.setOpacity(.5);
28584     this.proxy.hide();
28585
28586     if(config.width){
28587         el.setWidth(config.width);
28588     }
28589     if(config.height){
28590         el.setHeight(config.height);
28591     }
28592     this.size = el.getSize();
28593     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
28594         this.xy = [config.x,config.y];
28595     }else{
28596         this.xy = el.getCenterXY(true);
28597     }
28598     /** The header element @type Roo.Element */
28599     this.header = el.child("> .x-dlg-hd");
28600     /** The body element @type Roo.Element */
28601     this.body = el.child("> .x-dlg-bd");
28602     /** The footer element @type Roo.Element */
28603     this.footer = el.child("> .x-dlg-ft");
28604
28605     if(!this.header){
28606         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
28607     }
28608     if(!this.body){
28609         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
28610     }
28611
28612     this.header.unselectable();
28613     if(this.title){
28614         this.header.update(this.title);
28615     }
28616     // this element allows the dialog to be focused for keyboard event
28617     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
28618     this.focusEl.swallowEvent("click", true);
28619
28620     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
28621
28622     // wrap the body and footer for special rendering
28623     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
28624     if(this.footer){
28625         this.bwrap.dom.appendChild(this.footer.dom);
28626     }
28627
28628     this.bg = this.el.createChild({
28629         tag: "div", cls:"x-dlg-bg",
28630         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
28631     });
28632     this.centerBg = this.bg.child("div.x-dlg-bg-center");
28633
28634
28635     if(this.autoScroll !== false && !this.autoTabs){
28636         this.body.setStyle("overflow", "auto");
28637     }
28638
28639     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
28640
28641     if(this.closable !== false){
28642         this.el.addClass("x-dlg-closable");
28643         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
28644         this.close.on("click", this.closeClick, this);
28645         this.close.addClassOnOver("x-dlg-close-over");
28646     }
28647     if(this.collapsible !== false){
28648         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
28649         this.collapseBtn.on("click", this.collapseClick, this);
28650         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
28651         this.header.on("dblclick", this.collapseClick, this);
28652     }
28653     if(this.resizable !== false){
28654         this.el.addClass("x-dlg-resizable");
28655         this.resizer = new Roo.Resizable(el, {
28656             minWidth: this.minWidth || 80,
28657             minHeight:this.minHeight || 80,
28658             handles: this.resizeHandles || "all",
28659             pinned: true
28660         });
28661         this.resizer.on("beforeresize", this.beforeResize, this);
28662         this.resizer.on("resize", this.onResize, this);
28663     }
28664     if(this.draggable !== false){
28665         el.addClass("x-dlg-draggable");
28666         if (!this.proxyDrag) {
28667             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
28668         }
28669         else {
28670             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
28671         }
28672         dd.setHandleElId(this.header.id);
28673         dd.endDrag = this.endMove.createDelegate(this);
28674         dd.startDrag = this.startMove.createDelegate(this);
28675         dd.onDrag = this.onDrag.createDelegate(this);
28676         dd.scroll = false;
28677         this.dd = dd;
28678     }
28679     if(this.modal){
28680         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
28681         this.mask.enableDisplayMode("block");
28682         this.mask.hide();
28683         this.el.addClass("x-dlg-modal");
28684     }
28685     if(this.shadow){
28686         this.shadow = new Roo.Shadow({
28687             mode : typeof this.shadow == "string" ? this.shadow : "sides",
28688             offset : this.shadowOffset
28689         });
28690     }else{
28691         this.shadowOffset = 0;
28692     }
28693     if(Roo.useShims && this.shim !== false){
28694         this.shim = this.el.createShim();
28695         this.shim.hide = this.hideAction;
28696         this.shim.hide();
28697     }else{
28698         this.shim = false;
28699     }
28700     if(this.autoTabs){
28701         this.initTabs();
28702     }
28703     if (this.buttons) { 
28704         var bts= this.buttons;
28705         this.buttons = [];
28706         Roo.each(bts, function(b) {
28707             this.addButton(b);
28708         }, this);
28709     }
28710     
28711     
28712     this.addEvents({
28713         /**
28714          * @event keydown
28715          * Fires when a key is pressed
28716          * @param {Roo.BasicDialog} this
28717          * @param {Roo.EventObject} e
28718          */
28719         "keydown" : true,
28720         /**
28721          * @event move
28722          * Fires when this dialog is moved by the user.
28723          * @param {Roo.BasicDialog} this
28724          * @param {Number} x The new page X
28725          * @param {Number} y The new page Y
28726          */
28727         "move" : true,
28728         /**
28729          * @event resize
28730          * Fires when this dialog is resized by the user.
28731          * @param {Roo.BasicDialog} this
28732          * @param {Number} width The new width
28733          * @param {Number} height The new height
28734          */
28735         "resize" : true,
28736         /**
28737          * @event beforehide
28738          * Fires before this dialog is hidden.
28739          * @param {Roo.BasicDialog} this
28740          */
28741         "beforehide" : true,
28742         /**
28743          * @event hide
28744          * Fires when this dialog is hidden.
28745          * @param {Roo.BasicDialog} this
28746          */
28747         "hide" : true,
28748         /**
28749          * @event beforeshow
28750          * Fires before this dialog is shown.
28751          * @param {Roo.BasicDialog} this
28752          */
28753         "beforeshow" : true,
28754         /**
28755          * @event show
28756          * Fires when this dialog is shown.
28757          * @param {Roo.BasicDialog} this
28758          */
28759         "show" : true
28760     });
28761     el.on("keydown", this.onKeyDown, this);
28762     el.on("mousedown", this.toFront, this);
28763     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
28764     this.el.hide();
28765     Roo.DialogManager.register(this);
28766     Roo.BasicDialog.superclass.constructor.call(this);
28767 };
28768
28769 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
28770     shadowOffset: Roo.isIE ? 6 : 5,
28771     minHeight: 80,
28772     minWidth: 200,
28773     minButtonWidth: 75,
28774     defaultButton: null,
28775     buttonAlign: "right",
28776     tabTag: 'div',
28777     firstShow: true,
28778
28779     /**
28780      * Sets the dialog title text
28781      * @param {String} text The title text to display
28782      * @return {Roo.BasicDialog} this
28783      */
28784     setTitle : function(text){
28785         this.header.update(text);
28786         return this;
28787     },
28788
28789     // private
28790     closeClick : function(){
28791         this.hide();
28792     },
28793
28794     // private
28795     collapseClick : function(){
28796         this[this.collapsed ? "expand" : "collapse"]();
28797     },
28798
28799     /**
28800      * Collapses the dialog to its minimized state (only the title bar is visible).
28801      * Equivalent to the user clicking the collapse dialog button.
28802      */
28803     collapse : function(){
28804         if(!this.collapsed){
28805             this.collapsed = true;
28806             this.el.addClass("x-dlg-collapsed");
28807             this.restoreHeight = this.el.getHeight();
28808             this.resizeTo(this.el.getWidth(), this.header.getHeight());
28809         }
28810     },
28811
28812     /**
28813      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
28814      * clicking the expand dialog button.
28815      */
28816     expand : function(){
28817         if(this.collapsed){
28818             this.collapsed = false;
28819             this.el.removeClass("x-dlg-collapsed");
28820             this.resizeTo(this.el.getWidth(), this.restoreHeight);
28821         }
28822     },
28823
28824     /**
28825      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
28826      * @return {Roo.TabPanel} The tabs component
28827      */
28828     initTabs : function(){
28829         var tabs = this.getTabs();
28830         while(tabs.getTab(0)){
28831             tabs.removeTab(0);
28832         }
28833         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
28834             var dom = el.dom;
28835             tabs.addTab(Roo.id(dom), dom.title);
28836             dom.title = "";
28837         });
28838         tabs.activate(0);
28839         return tabs;
28840     },
28841
28842     // private
28843     beforeResize : function(){
28844         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
28845     },
28846
28847     // private
28848     onResize : function(){
28849         this.refreshSize();
28850         this.syncBodyHeight();
28851         this.adjustAssets();
28852         this.focus();
28853         this.fireEvent("resize", this, this.size.width, this.size.height);
28854     },
28855
28856     // private
28857     onKeyDown : function(e){
28858         if(this.isVisible()){
28859             this.fireEvent("keydown", this, e);
28860         }
28861     },
28862
28863     /**
28864      * Resizes the dialog.
28865      * @param {Number} width
28866      * @param {Number} height
28867      * @return {Roo.BasicDialog} this
28868      */
28869     resizeTo : function(width, height){
28870         this.el.setSize(width, height);
28871         this.size = {width: width, height: height};
28872         this.syncBodyHeight();
28873         if(this.fixedcenter){
28874             this.center();
28875         }
28876         if(this.isVisible()){
28877             this.constrainXY();
28878             this.adjustAssets();
28879         }
28880         this.fireEvent("resize", this, width, height);
28881         return this;
28882     },
28883
28884
28885     /**
28886      * Resizes the dialog to fit the specified content size.
28887      * @param {Number} width
28888      * @param {Number} height
28889      * @return {Roo.BasicDialog} this
28890      */
28891     setContentSize : function(w, h){
28892         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
28893         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
28894         //if(!this.el.isBorderBox()){
28895             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
28896             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
28897         //}
28898         if(this.tabs){
28899             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
28900             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
28901         }
28902         this.resizeTo(w, h);
28903         return this;
28904     },
28905
28906     /**
28907      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
28908      * executed in response to a particular key being pressed while the dialog is active.
28909      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
28910      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
28911      * @param {Function} fn The function to call
28912      * @param {Object} scope (optional) The scope of the function
28913      * @return {Roo.BasicDialog} this
28914      */
28915     addKeyListener : function(key, fn, scope){
28916         var keyCode, shift, ctrl, alt;
28917         if(typeof key == "object" && !(key instanceof Array)){
28918             keyCode = key["key"];
28919             shift = key["shift"];
28920             ctrl = key["ctrl"];
28921             alt = key["alt"];
28922         }else{
28923             keyCode = key;
28924         }
28925         var handler = function(dlg, e){
28926             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
28927                 var k = e.getKey();
28928                 if(keyCode instanceof Array){
28929                     for(var i = 0, len = keyCode.length; i < len; i++){
28930                         if(keyCode[i] == k){
28931                           fn.call(scope || window, dlg, k, e);
28932                           return;
28933                         }
28934                     }
28935                 }else{
28936                     if(k == keyCode){
28937                         fn.call(scope || window, dlg, k, e);
28938                     }
28939                 }
28940             }
28941         };
28942         this.on("keydown", handler);
28943         return this;
28944     },
28945
28946     /**
28947      * Returns the TabPanel component (creates it if it doesn't exist).
28948      * Note: If you wish to simply check for the existence of tabs without creating them,
28949      * check for a null 'tabs' property.
28950      * @return {Roo.TabPanel} The tabs component
28951      */
28952     getTabs : function(){
28953         if(!this.tabs){
28954             this.el.addClass("x-dlg-auto-tabs");
28955             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
28956             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
28957         }
28958         return this.tabs;
28959     },
28960
28961     /**
28962      * Adds a button to the footer section of the dialog.
28963      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
28964      * object or a valid Roo.DomHelper element config
28965      * @param {Function} handler The function called when the button is clicked
28966      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
28967      * @return {Roo.Button} The new button
28968      */
28969     addButton : function(config, handler, scope){
28970         var dh = Roo.DomHelper;
28971         if(!this.footer){
28972             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
28973         }
28974         if(!this.btnContainer){
28975             var tb = this.footer.createChild({
28976
28977                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
28978                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
28979             }, null, true);
28980             this.btnContainer = tb.firstChild.firstChild.firstChild;
28981         }
28982         var bconfig = {
28983             handler: handler,
28984             scope: scope,
28985             minWidth: this.minButtonWidth,
28986             hideParent:true
28987         };
28988         if(typeof config == "string"){
28989             bconfig.text = config;
28990         }else{
28991             if(config.tag){
28992                 bconfig.dhconfig = config;
28993             }else{
28994                 Roo.apply(bconfig, config);
28995             }
28996         }
28997         var fc = false;
28998         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
28999             bconfig.position = Math.max(0, bconfig.position);
29000             fc = this.btnContainer.childNodes[bconfig.position];
29001         }
29002          
29003         var btn = new Roo.Button(
29004             fc ? 
29005                 this.btnContainer.insertBefore(document.createElement("td"),fc)
29006                 : this.btnContainer.appendChild(document.createElement("td")),
29007             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
29008             bconfig
29009         );
29010         this.syncBodyHeight();
29011         if(!this.buttons){
29012             /**
29013              * Array of all the buttons that have been added to this dialog via addButton
29014              * @type Array
29015              */
29016             this.buttons = [];
29017         }
29018         this.buttons.push(btn);
29019         return btn;
29020     },
29021
29022     /**
29023      * Sets the default button to be focused when the dialog is displayed.
29024      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
29025      * @return {Roo.BasicDialog} this
29026      */
29027     setDefaultButton : function(btn){
29028         this.defaultButton = btn;
29029         return this;
29030     },
29031
29032     // private
29033     getHeaderFooterHeight : function(safe){
29034         var height = 0;
29035         if(this.header){
29036            height += this.header.getHeight();
29037         }
29038         if(this.footer){
29039            var fm = this.footer.getMargins();
29040             height += (this.footer.getHeight()+fm.top+fm.bottom);
29041         }
29042         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
29043         height += this.centerBg.getPadding("tb");
29044         return height;
29045     },
29046
29047     // private
29048     syncBodyHeight : function(){
29049         var bd = this.body, cb = this.centerBg, bw = this.bwrap;
29050         var height = this.size.height - this.getHeaderFooterHeight(false);
29051         bd.setHeight(height-bd.getMargins("tb"));
29052         var hh = this.header.getHeight();
29053         var h = this.size.height-hh;
29054         cb.setHeight(h);
29055         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
29056         bw.setHeight(h-cb.getPadding("tb"));
29057         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
29058         bd.setWidth(bw.getWidth(true));
29059         if(this.tabs){
29060             this.tabs.syncHeight();
29061             if(Roo.isIE){
29062                 this.tabs.el.repaint();
29063             }
29064         }
29065     },
29066
29067     /**
29068      * Restores the previous state of the dialog if Roo.state is configured.
29069      * @return {Roo.BasicDialog} this
29070      */
29071     restoreState : function(){
29072         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
29073         if(box && box.width){
29074             this.xy = [box.x, box.y];
29075             this.resizeTo(box.width, box.height);
29076         }
29077         return this;
29078     },
29079
29080     // private
29081     beforeShow : function(){
29082         this.expand();
29083         if(this.fixedcenter){
29084             this.xy = this.el.getCenterXY(true);
29085         }
29086         if(this.modal){
29087             Roo.get(document.body).addClass("x-body-masked");
29088             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29089             this.mask.show();
29090         }
29091         this.constrainXY();
29092     },
29093
29094     // private
29095     animShow : function(){
29096         var b = Roo.get(this.animateTarget).getBox();
29097         this.proxy.setSize(b.width, b.height);
29098         this.proxy.setLocation(b.x, b.y);
29099         this.proxy.show();
29100         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
29101                     true, .35, this.showEl.createDelegate(this));
29102     },
29103
29104     /**
29105      * Shows the dialog.
29106      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
29107      * @return {Roo.BasicDialog} this
29108      */
29109     show : function(animateTarget){
29110         if (this.fireEvent("beforeshow", this) === false){
29111             return;
29112         }
29113         if(this.syncHeightBeforeShow){
29114             this.syncBodyHeight();
29115         }else if(this.firstShow){
29116             this.firstShow = false;
29117             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
29118         }
29119         this.animateTarget = animateTarget || this.animateTarget;
29120         if(!this.el.isVisible()){
29121             this.beforeShow();
29122             if(this.animateTarget && Roo.get(this.animateTarget)){
29123                 this.animShow();
29124             }else{
29125                 this.showEl();
29126             }
29127         }
29128         return this;
29129     },
29130
29131     // private
29132     showEl : function(){
29133         this.proxy.hide();
29134         this.el.setXY(this.xy);
29135         this.el.show();
29136         this.adjustAssets(true);
29137         this.toFront();
29138         this.focus();
29139         // IE peekaboo bug - fix found by Dave Fenwick
29140         if(Roo.isIE){
29141             this.el.repaint();
29142         }
29143         this.fireEvent("show", this);
29144     },
29145
29146     /**
29147      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
29148      * dialog itself will receive focus.
29149      */
29150     focus : function(){
29151         if(this.defaultButton){
29152             this.defaultButton.focus();
29153         }else{
29154             this.focusEl.focus();
29155         }
29156     },
29157
29158     // private
29159     constrainXY : function(){
29160         if(this.constraintoviewport !== false){
29161             if(!this.viewSize){
29162                 if(this.container){
29163                     var s = this.container.getSize();
29164                     this.viewSize = [s.width, s.height];
29165                 }else{
29166                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
29167                 }
29168             }
29169             var s = Roo.get(this.container||document).getScroll();
29170
29171             var x = this.xy[0], y = this.xy[1];
29172             var w = this.size.width, h = this.size.height;
29173             var vw = this.viewSize[0], vh = this.viewSize[1];
29174             // only move it if it needs it
29175             var moved = false;
29176             // first validate right/bottom
29177             if(x + w > vw+s.left){
29178                 x = vw - w;
29179                 moved = true;
29180             }
29181             if(y + h > vh+s.top){
29182                 y = vh - h;
29183                 moved = true;
29184             }
29185             // then make sure top/left isn't negative
29186             if(x < s.left){
29187                 x = s.left;
29188                 moved = true;
29189             }
29190             if(y < s.top){
29191                 y = s.top;
29192                 moved = true;
29193             }
29194             if(moved){
29195                 // cache xy
29196                 this.xy = [x, y];
29197                 if(this.isVisible()){
29198                     this.el.setLocation(x, y);
29199                     this.adjustAssets();
29200                 }
29201             }
29202         }
29203     },
29204
29205     // private
29206     onDrag : function(){
29207         if(!this.proxyDrag){
29208             this.xy = this.el.getXY();
29209             this.adjustAssets();
29210         }
29211     },
29212
29213     // private
29214     adjustAssets : function(doShow){
29215         var x = this.xy[0], y = this.xy[1];
29216         var w = this.size.width, h = this.size.height;
29217         if(doShow === true){
29218             if(this.shadow){
29219                 this.shadow.show(this.el);
29220             }
29221             if(this.shim){
29222                 this.shim.show();
29223             }
29224         }
29225         if(this.shadow && this.shadow.isVisible()){
29226             this.shadow.show(this.el);
29227         }
29228         if(this.shim && this.shim.isVisible()){
29229             this.shim.setBounds(x, y, w, h);
29230         }
29231     },
29232
29233     // private
29234     adjustViewport : function(w, h){
29235         if(!w || !h){
29236             w = Roo.lib.Dom.getViewWidth();
29237             h = Roo.lib.Dom.getViewHeight();
29238         }
29239         // cache the size
29240         this.viewSize = [w, h];
29241         if(this.modal && this.mask.isVisible()){
29242             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
29243             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29244         }
29245         if(this.isVisible()){
29246             this.constrainXY();
29247         }
29248     },
29249
29250     /**
29251      * Destroys this dialog and all its supporting elements (including any tabs, shim,
29252      * shadow, proxy, mask, etc.)  Also removes all event listeners.
29253      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29254      */
29255     destroy : function(removeEl){
29256         if(this.isVisible()){
29257             this.animateTarget = null;
29258             this.hide();
29259         }
29260         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
29261         if(this.tabs){
29262             this.tabs.destroy(removeEl);
29263         }
29264         Roo.destroy(
29265              this.shim,
29266              this.proxy,
29267              this.resizer,
29268              this.close,
29269              this.mask
29270         );
29271         if(this.dd){
29272             this.dd.unreg();
29273         }
29274         if(this.buttons){
29275            for(var i = 0, len = this.buttons.length; i < len; i++){
29276                this.buttons[i].destroy();
29277            }
29278         }
29279         this.el.removeAllListeners();
29280         if(removeEl === true){
29281             this.el.update("");
29282             this.el.remove();
29283         }
29284         Roo.DialogManager.unregister(this);
29285     },
29286
29287     // private
29288     startMove : function(){
29289         if(this.proxyDrag){
29290             this.proxy.show();
29291         }
29292         if(this.constraintoviewport !== false){
29293             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
29294         }
29295     },
29296
29297     // private
29298     endMove : function(){
29299         if(!this.proxyDrag){
29300             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
29301         }else{
29302             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
29303             this.proxy.hide();
29304         }
29305         this.refreshSize();
29306         this.adjustAssets();
29307         this.focus();
29308         this.fireEvent("move", this, this.xy[0], this.xy[1]);
29309     },
29310
29311     /**
29312      * Brings this dialog to the front of any other visible dialogs
29313      * @return {Roo.BasicDialog} this
29314      */
29315     toFront : function(){
29316         Roo.DialogManager.bringToFront(this);
29317         return this;
29318     },
29319
29320     /**
29321      * Sends this dialog to the back (under) of any other visible dialogs
29322      * @return {Roo.BasicDialog} this
29323      */
29324     toBack : function(){
29325         Roo.DialogManager.sendToBack(this);
29326         return this;
29327     },
29328
29329     /**
29330      * Centers this dialog in the viewport
29331      * @return {Roo.BasicDialog} this
29332      */
29333     center : function(){
29334         var xy = this.el.getCenterXY(true);
29335         this.moveTo(xy[0], xy[1]);
29336         return this;
29337     },
29338
29339     /**
29340      * Moves the dialog's top-left corner to the specified point
29341      * @param {Number} x
29342      * @param {Number} y
29343      * @return {Roo.BasicDialog} this
29344      */
29345     moveTo : function(x, y){
29346         this.xy = [x,y];
29347         if(this.isVisible()){
29348             this.el.setXY(this.xy);
29349             this.adjustAssets();
29350         }
29351         return this;
29352     },
29353
29354     /**
29355      * Aligns the dialog to the specified element
29356      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29357      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
29358      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29359      * @return {Roo.BasicDialog} this
29360      */
29361     alignTo : function(element, position, offsets){
29362         this.xy = this.el.getAlignToXY(element, position, offsets);
29363         if(this.isVisible()){
29364             this.el.setXY(this.xy);
29365             this.adjustAssets();
29366         }
29367         return this;
29368     },
29369
29370     /**
29371      * Anchors an element to another element and realigns it when the window is resized.
29372      * @param {String/HTMLElement/Roo.Element} element The element to align to.
29373      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
29374      * @param {Array} offsets (optional) Offset the positioning by [x, y]
29375      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
29376      * is a number, it is used as the buffer delay (defaults to 50ms).
29377      * @return {Roo.BasicDialog} this
29378      */
29379     anchorTo : function(el, alignment, offsets, monitorScroll){
29380         var action = function(){
29381             this.alignTo(el, alignment, offsets);
29382         };
29383         Roo.EventManager.onWindowResize(action, this);
29384         var tm = typeof monitorScroll;
29385         if(tm != 'undefined'){
29386             Roo.EventManager.on(window, 'scroll', action, this,
29387                 {buffer: tm == 'number' ? monitorScroll : 50});
29388         }
29389         action.call(this);
29390         return this;
29391     },
29392
29393     /**
29394      * Returns true if the dialog is visible
29395      * @return {Boolean}
29396      */
29397     isVisible : function(){
29398         return this.el.isVisible();
29399     },
29400
29401     // private
29402     animHide : function(callback){
29403         var b = Roo.get(this.animateTarget).getBox();
29404         this.proxy.show();
29405         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
29406         this.el.hide();
29407         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
29408                     this.hideEl.createDelegate(this, [callback]));
29409     },
29410
29411     /**
29412      * Hides the dialog.
29413      * @param {Function} callback (optional) Function to call when the dialog is hidden
29414      * @return {Roo.BasicDialog} this
29415      */
29416     hide : function(callback){
29417         if (this.fireEvent("beforehide", this) === false){
29418             return;
29419         }
29420         if(this.shadow){
29421             this.shadow.hide();
29422         }
29423         if(this.shim) {
29424           this.shim.hide();
29425         }
29426         // sometimes animateTarget seems to get set.. causing problems...
29427         // this just double checks..
29428         if(this.animateTarget && Roo.get(this.animateTarget)) {
29429            this.animHide(callback);
29430         }else{
29431             this.el.hide();
29432             this.hideEl(callback);
29433         }
29434         return this;
29435     },
29436
29437     // private
29438     hideEl : function(callback){
29439         this.proxy.hide();
29440         if(this.modal){
29441             this.mask.hide();
29442             Roo.get(document.body).removeClass("x-body-masked");
29443         }
29444         this.fireEvent("hide", this);
29445         if(typeof callback == "function"){
29446             callback();
29447         }
29448     },
29449
29450     // private
29451     hideAction : function(){
29452         this.setLeft("-10000px");
29453         this.setTop("-10000px");
29454         this.setStyle("visibility", "hidden");
29455     },
29456
29457     // private
29458     refreshSize : function(){
29459         this.size = this.el.getSize();
29460         this.xy = this.el.getXY();
29461         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
29462     },
29463
29464     // private
29465     // z-index is managed by the DialogManager and may be overwritten at any time
29466     setZIndex : function(index){
29467         if(this.modal){
29468             this.mask.setStyle("z-index", index);
29469         }
29470         if(this.shim){
29471             this.shim.setStyle("z-index", ++index);
29472         }
29473         if(this.shadow){
29474             this.shadow.setZIndex(++index);
29475         }
29476         this.el.setStyle("z-index", ++index);
29477         if(this.proxy){
29478             this.proxy.setStyle("z-index", ++index);
29479         }
29480         if(this.resizer){
29481             this.resizer.proxy.setStyle("z-index", ++index);
29482         }
29483
29484         this.lastZIndex = index;
29485     },
29486
29487     /**
29488      * Returns the element for this dialog
29489      * @return {Roo.Element} The underlying dialog Element
29490      */
29491     getEl : function(){
29492         return this.el;
29493     }
29494 });
29495
29496 /**
29497  * @class Roo.DialogManager
29498  * Provides global access to BasicDialogs that have been created and
29499  * support for z-indexing (layering) multiple open dialogs.
29500  */
29501 Roo.DialogManager = function(){
29502     var list = {};
29503     var accessList = [];
29504     var front = null;
29505
29506     // private
29507     var sortDialogs = function(d1, d2){
29508         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
29509     };
29510
29511     // private
29512     var orderDialogs = function(){
29513         accessList.sort(sortDialogs);
29514         var seed = Roo.DialogManager.zseed;
29515         for(var i = 0, len = accessList.length; i < len; i++){
29516             var dlg = accessList[i];
29517             if(dlg){
29518                 dlg.setZIndex(seed + (i*10));
29519             }
29520         }
29521     };
29522
29523     return {
29524         /**
29525          * The starting z-index for BasicDialogs (defaults to 9000)
29526          * @type Number The z-index value
29527          */
29528         zseed : 9000,
29529
29530         // private
29531         register : function(dlg){
29532             list[dlg.id] = dlg;
29533             accessList.push(dlg);
29534         },
29535
29536         // private
29537         unregister : function(dlg){
29538             delete list[dlg.id];
29539             var i=0;
29540             var len=0;
29541             if(!accessList.indexOf){
29542                 for(  i = 0, len = accessList.length; i < len; i++){
29543                     if(accessList[i] == dlg){
29544                         accessList.splice(i, 1);
29545                         return;
29546                     }
29547                 }
29548             }else{
29549                  i = accessList.indexOf(dlg);
29550                 if(i != -1){
29551                     accessList.splice(i, 1);
29552                 }
29553             }
29554         },
29555
29556         /**
29557          * Gets a registered dialog by id
29558          * @param {String/Object} id The id of the dialog or a dialog
29559          * @return {Roo.BasicDialog} this
29560          */
29561         get : function(id){
29562             return typeof id == "object" ? id : list[id];
29563         },
29564
29565         /**
29566          * Brings the specified dialog to the front
29567          * @param {String/Object} dlg The id of the dialog or a dialog
29568          * @return {Roo.BasicDialog} this
29569          */
29570         bringToFront : function(dlg){
29571             dlg = this.get(dlg);
29572             if(dlg != front){
29573                 front = dlg;
29574                 dlg._lastAccess = new Date().getTime();
29575                 orderDialogs();
29576             }
29577             return dlg;
29578         },
29579
29580         /**
29581          * Sends the specified dialog to the back
29582          * @param {String/Object} dlg The id of the dialog or a dialog
29583          * @return {Roo.BasicDialog} this
29584          */
29585         sendToBack : function(dlg){
29586             dlg = this.get(dlg);
29587             dlg._lastAccess = -(new Date().getTime());
29588             orderDialogs();
29589             return dlg;
29590         },
29591
29592         /**
29593          * Hides all dialogs
29594          */
29595         hideAll : function(){
29596             for(var id in list){
29597                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
29598                     list[id].hide();
29599                 }
29600             }
29601         }
29602     };
29603 }();
29604
29605 /**
29606  * @class Roo.LayoutDialog
29607  * @extends Roo.BasicDialog
29608  * Dialog which provides adjustments for working with a layout in a Dialog.
29609  * Add your necessary layout config options to the dialog's config.<br>
29610  * Example usage (including a nested layout):
29611  * <pre><code>
29612 if(!dialog){
29613     dialog = new Roo.LayoutDialog("download-dlg", {
29614         modal: true,
29615         width:600,
29616         height:450,
29617         shadow:true,
29618         minWidth:500,
29619         minHeight:350,
29620         autoTabs:true,
29621         proxyDrag:true,
29622         // layout config merges with the dialog config
29623         center:{
29624             tabPosition: "top",
29625             alwaysShowTabs: true
29626         }
29627     });
29628     dialog.addKeyListener(27, dialog.hide, dialog);
29629     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
29630     dialog.addButton("Build It!", this.getDownload, this);
29631
29632     // we can even add nested layouts
29633     var innerLayout = new Roo.BorderLayout("dl-inner", {
29634         east: {
29635             initialSize: 200,
29636             autoScroll:true,
29637             split:true
29638         },
29639         center: {
29640             autoScroll:true
29641         }
29642     });
29643     innerLayout.beginUpdate();
29644     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
29645     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
29646     innerLayout.endUpdate(true);
29647
29648     var layout = dialog.getLayout();
29649     layout.beginUpdate();
29650     layout.add("center", new Roo.ContentPanel("standard-panel",
29651                         {title: "Download the Source", fitToFrame:true}));
29652     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
29653                {title: "Build your own roo.js"}));
29654     layout.getRegion("center").showPanel(sp);
29655     layout.endUpdate();
29656 }
29657 </code></pre>
29658     * @constructor
29659     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
29660     * @param {Object} config configuration options
29661   */
29662 Roo.LayoutDialog = function(el, cfg){
29663     
29664     var config=  cfg;
29665     if (typeof(cfg) == 'undefined') {
29666         config = Roo.apply({}, el);
29667         // not sure why we use documentElement here.. - it should always be body.
29668         // IE7 borks horribly if we use documentElement.
29669         el = Roo.get( Roo.isIE ? (document.body || document.documentElement) : (document.documentElement || document.body) ).createChild();
29670         //config.autoCreate = true;
29671     }
29672     
29673     
29674     config.autoTabs = false;
29675     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
29676     this.body.setStyle({overflow:"hidden", position:"relative"});
29677     this.layout = new Roo.BorderLayout(this.body.dom, config);
29678     this.layout.monitorWindowResize = false;
29679     this.el.addClass("x-dlg-auto-layout");
29680     // fix case when center region overwrites center function
29681     this.center = Roo.BasicDialog.prototype.center;
29682     this.on("show", this.layout.layout, this.layout, true);
29683     if (config.items) {
29684         var xitems = config.items;
29685         delete config.items;
29686         Roo.each(xitems, this.addxtype, this);
29687     }
29688     
29689     
29690 };
29691 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
29692     /**
29693      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
29694      * @deprecated
29695      */
29696     endUpdate : function(){
29697         this.layout.endUpdate();
29698     },
29699
29700     /**
29701      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
29702      *  @deprecated
29703      */
29704     beginUpdate : function(){
29705         this.layout.beginUpdate();
29706     },
29707
29708     /**
29709      * Get the BorderLayout for this dialog
29710      * @return {Roo.BorderLayout}
29711      */
29712     getLayout : function(){
29713         return this.layout;
29714     },
29715
29716     showEl : function(){
29717         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
29718         if(Roo.isIE7){
29719             this.layout.layout();
29720         }
29721     },
29722
29723     // private
29724     // Use the syncHeightBeforeShow config option to control this automatically
29725     syncBodyHeight : function(){
29726         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
29727         if(this.layout){this.layout.layout();}
29728     },
29729     
29730       /**
29731      * Add an xtype element (actually adds to the layout.)
29732      * @return {Object} xdata xtype object data.
29733      */
29734     
29735     addxtype : function(c) {
29736         return this.layout.addxtype(c);
29737     }
29738 });/*
29739  * Based on:
29740  * Ext JS Library 1.1.1
29741  * Copyright(c) 2006-2007, Ext JS, LLC.
29742  *
29743  * Originally Released Under LGPL - original licence link has changed is not relivant.
29744  *
29745  * Fork - LGPL
29746  * <script type="text/javascript">
29747  */
29748  
29749 /**
29750  * @class Roo.MessageBox
29751  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
29752  * Example usage:
29753  *<pre><code>
29754 // Basic alert:
29755 Roo.Msg.alert('Status', 'Changes saved successfully.');
29756
29757 // Prompt for user data:
29758 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
29759     if (btn == 'ok'){
29760         // process text value...
29761     }
29762 });
29763
29764 // Show a dialog using config options:
29765 Roo.Msg.show({
29766    title:'Save Changes?',
29767    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
29768    buttons: Roo.Msg.YESNOCANCEL,
29769    fn: processResult,
29770    animEl: 'elId'
29771 });
29772 </code></pre>
29773  * @singleton
29774  */
29775 Roo.MessageBox = function(){
29776     var dlg, opt, mask, waitTimer;
29777     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
29778     var buttons, activeTextEl, bwidth;
29779
29780     // private
29781     var handleButton = function(button){
29782         dlg.hide();
29783         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
29784     };
29785
29786     // private
29787     var handleHide = function(){
29788         if(opt && opt.cls){
29789             dlg.el.removeClass(opt.cls);
29790         }
29791         if(waitTimer){
29792             Roo.TaskMgr.stop(waitTimer);
29793             waitTimer = null;
29794         }
29795     };
29796
29797     // private
29798     var updateButtons = function(b){
29799         var width = 0;
29800         if(!b){
29801             buttons["ok"].hide();
29802             buttons["cancel"].hide();
29803             buttons["yes"].hide();
29804             buttons["no"].hide();
29805             dlg.footer.dom.style.display = 'none';
29806             return width;
29807         }
29808         dlg.footer.dom.style.display = '';
29809         for(var k in buttons){
29810             if(typeof buttons[k] != "function"){
29811                 if(b[k]){
29812                     buttons[k].show();
29813                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
29814                     width += buttons[k].el.getWidth()+15;
29815                 }else{
29816                     buttons[k].hide();
29817                 }
29818             }
29819         }
29820         return width;
29821     };
29822
29823     // private
29824     var handleEsc = function(d, k, e){
29825         if(opt && opt.closable !== false){
29826             dlg.hide();
29827         }
29828         if(e){
29829             e.stopEvent();
29830         }
29831     };
29832
29833     return {
29834         /**
29835          * Returns a reference to the underlying {@link Roo.BasicDialog} element
29836          * @return {Roo.BasicDialog} The BasicDialog element
29837          */
29838         getDialog : function(){
29839            if(!dlg){
29840                 dlg = new Roo.BasicDialog("x-msg-box", {
29841                     autoCreate : true,
29842                     shadow: true,
29843                     draggable: true,
29844                     resizable:false,
29845                     constraintoviewport:false,
29846                     fixedcenter:true,
29847                     collapsible : false,
29848                     shim:true,
29849                     modal: true,
29850                     width:400, height:100,
29851                     buttonAlign:"center",
29852                     closeClick : function(){
29853                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
29854                             handleButton("no");
29855                         }else{
29856                             handleButton("cancel");
29857                         }
29858                     }
29859                 });
29860                 dlg.on("hide", handleHide);
29861                 mask = dlg.mask;
29862                 dlg.addKeyListener(27, handleEsc);
29863                 buttons = {};
29864                 var bt = this.buttonText;
29865                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
29866                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
29867                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
29868                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
29869                 bodyEl = dlg.body.createChild({
29870
29871                     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>'
29872                 });
29873                 msgEl = bodyEl.dom.firstChild;
29874                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
29875                 textboxEl.enableDisplayMode();
29876                 textboxEl.addKeyListener([10,13], function(){
29877                     if(dlg.isVisible() && opt && opt.buttons){
29878                         if(opt.buttons.ok){
29879                             handleButton("ok");
29880                         }else if(opt.buttons.yes){
29881                             handleButton("yes");
29882                         }
29883                     }
29884                 });
29885                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
29886                 textareaEl.enableDisplayMode();
29887                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
29888                 progressEl.enableDisplayMode();
29889                 var pf = progressEl.dom.firstChild;
29890                 if (pf) {
29891                     pp = Roo.get(pf.firstChild);
29892                     pp.setHeight(pf.offsetHeight);
29893                 }
29894                 
29895             }
29896             return dlg;
29897         },
29898
29899         /**
29900          * Updates the message box body text
29901          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
29902          * the XHTML-compliant non-breaking space character '&amp;#160;')
29903          * @return {Roo.MessageBox} This message box
29904          */
29905         updateText : function(text){
29906             if(!dlg.isVisible() && !opt.width){
29907                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
29908             }
29909             msgEl.innerHTML = text || '&#160;';
29910             var w = Math.max(Math.min(opt.width || msgEl.offsetWidth, this.maxWidth), 
29911                         Math.max(opt.minWidth || this.minWidth, bwidth));
29912             if(opt.prompt){
29913                 activeTextEl.setWidth(w);
29914             }
29915             if(dlg.isVisible()){
29916                 dlg.fixedcenter = false;
29917             }
29918             dlg.setContentSize(w, bodyEl.getHeight());
29919             if(dlg.isVisible()){
29920                 dlg.fixedcenter = true;
29921             }
29922             return this;
29923         },
29924
29925         /**
29926          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
29927          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
29928          * @param {Number} value Any number between 0 and 1 (e.g., .5)
29929          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
29930          * @return {Roo.MessageBox} This message box
29931          */
29932         updateProgress : function(value, text){
29933             if(text){
29934                 this.updateText(text);
29935             }
29936             if (pp) { // weird bug on my firefox - for some reason this is not defined
29937                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
29938             }
29939             return this;
29940         },        
29941
29942         /**
29943          * Returns true if the message box is currently displayed
29944          * @return {Boolean} True if the message box is visible, else false
29945          */
29946         isVisible : function(){
29947             return dlg && dlg.isVisible();  
29948         },
29949
29950         /**
29951          * Hides the message box if it is displayed
29952          */
29953         hide : function(){
29954             if(this.isVisible()){
29955                 dlg.hide();
29956             }  
29957         },
29958
29959         /**
29960          * Displays a new message box, or reinitializes an existing message box, based on the config options
29961          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
29962          * The following config object properties are supported:
29963          * <pre>
29964 Property    Type             Description
29965 ----------  ---------------  ------------------------------------------------------------------------------------
29966 animEl            String/Element   An id or Element from which the message box should animate as it opens and
29967                                    closes (defaults to undefined)
29968 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
29969                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
29970 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
29971                                    progress and wait dialogs will ignore this property and always hide the
29972                                    close button as they can only be closed programmatically.
29973 cls               String           A custom CSS class to apply to the message box element
29974 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
29975                                    displayed (defaults to 75)
29976 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
29977                                    function will be btn (the name of the button that was clicked, if applicable,
29978                                    e.g. "ok"), and text (the value of the active text field, if applicable).
29979                                    Progress and wait dialogs will ignore this option since they do not respond to
29980                                    user actions and can only be closed programmatically, so any required function
29981                                    should be called by the same code after it closes the dialog.
29982 icon              String           A CSS class that provides a background image to be used as an icon for
29983                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
29984 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
29985 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
29986 modal             Boolean          False to allow user interaction with the page while the message box is
29987                                    displayed (defaults to true)
29988 msg               String           A string that will replace the existing message box body text (defaults
29989                                    to the XHTML-compliant non-breaking space character '&#160;')
29990 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
29991 progress          Boolean          True to display a progress bar (defaults to false)
29992 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
29993 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
29994 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
29995 title             String           The title text
29996 value             String           The string value to set into the active textbox element if displayed
29997 wait              Boolean          True to display a progress bar (defaults to false)
29998 width             Number           The width of the dialog in pixels
29999 </pre>
30000          *
30001          * Example usage:
30002          * <pre><code>
30003 Roo.Msg.show({
30004    title: 'Address',
30005    msg: 'Please enter your address:',
30006    width: 300,
30007    buttons: Roo.MessageBox.OKCANCEL,
30008    multiline: true,
30009    fn: saveAddress,
30010    animEl: 'addAddressBtn'
30011 });
30012 </code></pre>
30013          * @param {Object} config Configuration options
30014          * @return {Roo.MessageBox} This message box
30015          */
30016         show : function(options){
30017             if(this.isVisible()){
30018                 this.hide();
30019             }
30020             var d = this.getDialog();
30021             opt = options;
30022             d.setTitle(opt.title || "&#160;");
30023             d.close.setDisplayed(opt.closable !== false);
30024             activeTextEl = textboxEl;
30025             opt.prompt = opt.prompt || (opt.multiline ? true : false);
30026             if(opt.prompt){
30027                 if(opt.multiline){
30028                     textboxEl.hide();
30029                     textareaEl.show();
30030                     textareaEl.setHeight(typeof opt.multiline == "number" ?
30031                         opt.multiline : this.defaultTextHeight);
30032                     activeTextEl = textareaEl;
30033                 }else{
30034                     textboxEl.show();
30035                     textareaEl.hide();
30036                 }
30037             }else{
30038                 textboxEl.hide();
30039                 textareaEl.hide();
30040             }
30041             progressEl.setDisplayed(opt.progress === true);
30042             this.updateProgress(0);
30043             activeTextEl.dom.value = opt.value || "";
30044             if(opt.prompt){
30045                 dlg.setDefaultButton(activeTextEl);
30046             }else{
30047                 var bs = opt.buttons;
30048                 var db = null;
30049                 if(bs && bs.ok){
30050                     db = buttons["ok"];
30051                 }else if(bs && bs.yes){
30052                     db = buttons["yes"];
30053                 }
30054                 dlg.setDefaultButton(db);
30055             }
30056             bwidth = updateButtons(opt.buttons);
30057             this.updateText(opt.msg);
30058             if(opt.cls){
30059                 d.el.addClass(opt.cls);
30060             }
30061             d.proxyDrag = opt.proxyDrag === true;
30062             d.modal = opt.modal !== false;
30063             d.mask = opt.modal !== false ? mask : false;
30064             if(!d.isVisible()){
30065                 // force it to the end of the z-index stack so it gets a cursor in FF
30066                 document.body.appendChild(dlg.el.dom);
30067                 d.animateTarget = null;
30068                 d.show(options.animEl);
30069             }
30070             return this;
30071         },
30072
30073         /**
30074          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
30075          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
30076          * and closing the message box when the process is complete.
30077          * @param {String} title The title bar text
30078          * @param {String} msg The message box body text
30079          * @return {Roo.MessageBox} This message box
30080          */
30081         progress : function(title, msg){
30082             this.show({
30083                 title : title,
30084                 msg : msg,
30085                 buttons: false,
30086                 progress:true,
30087                 closable:false,
30088                 minWidth: this.minProgressWidth,
30089                 modal : true
30090             });
30091             return this;
30092         },
30093
30094         /**
30095          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
30096          * If a callback function is passed it will be called after the user clicks the button, and the
30097          * id of the button that was clicked will be passed as the only parameter to the callback
30098          * (could also be the top-right close button).
30099          * @param {String} title The title bar text
30100          * @param {String} msg The message box body text
30101          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30102          * @param {Object} scope (optional) The scope of the callback function
30103          * @return {Roo.MessageBox} This message box
30104          */
30105         alert : function(title, msg, fn, scope){
30106             this.show({
30107                 title : title,
30108                 msg : msg,
30109                 buttons: this.OK,
30110                 fn: fn,
30111                 scope : scope,
30112                 modal : true
30113             });
30114             return this;
30115         },
30116
30117         /**
30118          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
30119          * interaction while waiting for a long-running process to complete that does not have defined intervals.
30120          * You are responsible for closing the message box when the process is complete.
30121          * @param {String} msg The message box body text
30122          * @param {String} title (optional) The title bar text
30123          * @return {Roo.MessageBox} This message box
30124          */
30125         wait : function(msg, title){
30126             this.show({
30127                 title : title,
30128                 msg : msg,
30129                 buttons: false,
30130                 closable:false,
30131                 progress:true,
30132                 modal:true,
30133                 width:300,
30134                 wait:true
30135             });
30136             waitTimer = Roo.TaskMgr.start({
30137                 run: function(i){
30138                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
30139                 },
30140                 interval: 1000
30141             });
30142             return this;
30143         },
30144
30145         /**
30146          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
30147          * If a callback function is passed it will be called after the user clicks either button, and the id of the
30148          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
30149          * @param {String} title The title bar text
30150          * @param {String} msg The message box body text
30151          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30152          * @param {Object} scope (optional) The scope of the callback function
30153          * @return {Roo.MessageBox} This message box
30154          */
30155         confirm : function(title, msg, fn, scope){
30156             this.show({
30157                 title : title,
30158                 msg : msg,
30159                 buttons: this.YESNO,
30160                 fn: fn,
30161                 scope : scope,
30162                 modal : true
30163             });
30164             return this;
30165         },
30166
30167         /**
30168          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
30169          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
30170          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
30171          * (could also be the top-right close button) and the text that was entered will be passed as the two
30172          * parameters to the callback.
30173          * @param {String} title The title bar text
30174          * @param {String} msg The message box body text
30175          * @param {Function} fn (optional) The callback function invoked after the message box is closed
30176          * @param {Object} scope (optional) The scope of the callback function
30177          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
30178          * property, or the height in pixels to create the textbox (defaults to false / single-line)
30179          * @return {Roo.MessageBox} This message box
30180          */
30181         prompt : function(title, msg, fn, scope, multiline){
30182             this.show({
30183                 title : title,
30184                 msg : msg,
30185                 buttons: this.OKCANCEL,
30186                 fn: fn,
30187                 minWidth:250,
30188                 scope : scope,
30189                 prompt:true,
30190                 multiline: multiline,
30191                 modal : true
30192             });
30193             return this;
30194         },
30195
30196         /**
30197          * Button config that displays a single OK button
30198          * @type Object
30199          */
30200         OK : {ok:true},
30201         /**
30202          * Button config that displays Yes and No buttons
30203          * @type Object
30204          */
30205         YESNO : {yes:true, no:true},
30206         /**
30207          * Button config that displays OK and Cancel buttons
30208          * @type Object
30209          */
30210         OKCANCEL : {ok:true, cancel:true},
30211         /**
30212          * Button config that displays Yes, No and Cancel buttons
30213          * @type Object
30214          */
30215         YESNOCANCEL : {yes:true, no:true, cancel:true},
30216
30217         /**
30218          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
30219          * @type Number
30220          */
30221         defaultTextHeight : 75,
30222         /**
30223          * The maximum width in pixels of the message box (defaults to 600)
30224          * @type Number
30225          */
30226         maxWidth : 600,
30227         /**
30228          * The minimum width in pixels of the message box (defaults to 100)
30229          * @type Number
30230          */
30231         minWidth : 100,
30232         /**
30233          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
30234          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
30235          * @type Number
30236          */
30237         minProgressWidth : 250,
30238         /**
30239          * An object containing the default button text strings that can be overriden for localized language support.
30240          * Supported properties are: ok, cancel, yes and no.
30241          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
30242          * @type Object
30243          */
30244         buttonText : {
30245             ok : "OK",
30246             cancel : "Cancel",
30247             yes : "Yes",
30248             no : "No"
30249         }
30250     };
30251 }();
30252
30253 /**
30254  * Shorthand for {@link Roo.MessageBox}
30255  */
30256 Roo.Msg = Roo.MessageBox;/*
30257  * Based on:
30258  * Ext JS Library 1.1.1
30259  * Copyright(c) 2006-2007, Ext JS, LLC.
30260  *
30261  * Originally Released Under LGPL - original licence link has changed is not relivant.
30262  *
30263  * Fork - LGPL
30264  * <script type="text/javascript">
30265  */
30266 /**
30267  * @class Roo.QuickTips
30268  * Provides attractive and customizable tooltips for any element.
30269  * @singleton
30270  */
30271 Roo.QuickTips = function(){
30272     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
30273     var ce, bd, xy, dd;
30274     var visible = false, disabled = true, inited = false;
30275     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
30276     
30277     var onOver = function(e){
30278         if(disabled){
30279             return;
30280         }
30281         var t = e.getTarget();
30282         if(!t || t.nodeType !== 1 || t == document || t == document.body){
30283             return;
30284         }
30285         if(ce && t == ce.el){
30286             clearTimeout(hideProc);
30287             return;
30288         }
30289         if(t && tagEls[t.id]){
30290             tagEls[t.id].el = t;
30291             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
30292             return;
30293         }
30294         var ttp, et = Roo.fly(t);
30295         var ns = cfg.namespace;
30296         if(tm.interceptTitles && t.title){
30297             ttp = t.title;
30298             t.qtip = ttp;
30299             t.removeAttribute("title");
30300             e.preventDefault();
30301         }else{
30302             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
30303         }
30304         if(ttp){
30305             showProc = show.defer(tm.showDelay, tm, [{
30306                 el: t, 
30307                 text: ttp, 
30308                 width: et.getAttributeNS(ns, cfg.width),
30309                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
30310                 title: et.getAttributeNS(ns, cfg.title),
30311                     cls: et.getAttributeNS(ns, cfg.cls)
30312             }]);
30313         }
30314     };
30315     
30316     var onOut = function(e){
30317         clearTimeout(showProc);
30318         var t = e.getTarget();
30319         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
30320             hideProc = setTimeout(hide, tm.hideDelay);
30321         }
30322     };
30323     
30324     var onMove = function(e){
30325         if(disabled){
30326             return;
30327         }
30328         xy = e.getXY();
30329         xy[1] += 18;
30330         if(tm.trackMouse && ce){
30331             el.setXY(xy);
30332         }
30333     };
30334     
30335     var onDown = function(e){
30336         clearTimeout(showProc);
30337         clearTimeout(hideProc);
30338         if(!e.within(el)){
30339             if(tm.hideOnClick){
30340                 hide();
30341                 tm.disable();
30342                 tm.enable.defer(100, tm);
30343             }
30344         }
30345     };
30346     
30347     var getPad = function(){
30348         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
30349     };
30350
30351     var show = function(o){
30352         if(disabled){
30353             return;
30354         }
30355         clearTimeout(dismissProc);
30356         ce = o;
30357         if(removeCls){ // in case manually hidden
30358             el.removeClass(removeCls);
30359             removeCls = null;
30360         }
30361         if(ce.cls){
30362             el.addClass(ce.cls);
30363             removeCls = ce.cls;
30364         }
30365         if(ce.title){
30366             tipTitle.update(ce.title);
30367             tipTitle.show();
30368         }else{
30369             tipTitle.update('');
30370             tipTitle.hide();
30371         }
30372         el.dom.style.width  = tm.maxWidth+'px';
30373         //tipBody.dom.style.width = '';
30374         tipBodyText.update(o.text);
30375         var p = getPad(), w = ce.width;
30376         if(!w){
30377             var td = tipBodyText.dom;
30378             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
30379             if(aw > tm.maxWidth){
30380                 w = tm.maxWidth;
30381             }else if(aw < tm.minWidth){
30382                 w = tm.minWidth;
30383             }else{
30384                 w = aw;
30385             }
30386         }
30387         //tipBody.setWidth(w);
30388         el.setWidth(parseInt(w, 10) + p);
30389         if(ce.autoHide === false){
30390             close.setDisplayed(true);
30391             if(dd){
30392                 dd.unlock();
30393             }
30394         }else{
30395             close.setDisplayed(false);
30396             if(dd){
30397                 dd.lock();
30398             }
30399         }
30400         if(xy){
30401             el.avoidY = xy[1]-18;
30402             el.setXY(xy);
30403         }
30404         if(tm.animate){
30405             el.setOpacity(.1);
30406             el.setStyle("visibility", "visible");
30407             el.fadeIn({callback: afterShow});
30408         }else{
30409             afterShow();
30410         }
30411     };
30412     
30413     var afterShow = function(){
30414         if(ce){
30415             el.show();
30416             esc.enable();
30417             if(tm.autoDismiss && ce.autoHide !== false){
30418                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
30419             }
30420         }
30421     };
30422     
30423     var hide = function(noanim){
30424         clearTimeout(dismissProc);
30425         clearTimeout(hideProc);
30426         ce = null;
30427         if(el.isVisible()){
30428             esc.disable();
30429             if(noanim !== true && tm.animate){
30430                 el.fadeOut({callback: afterHide});
30431             }else{
30432                 afterHide();
30433             } 
30434         }
30435     };
30436     
30437     var afterHide = function(){
30438         el.hide();
30439         if(removeCls){
30440             el.removeClass(removeCls);
30441             removeCls = null;
30442         }
30443     };
30444     
30445     return {
30446         /**
30447         * @cfg {Number} minWidth
30448         * The minimum width of the quick tip (defaults to 40)
30449         */
30450        minWidth : 40,
30451         /**
30452         * @cfg {Number} maxWidth
30453         * The maximum width of the quick tip (defaults to 300)
30454         */
30455        maxWidth : 300,
30456         /**
30457         * @cfg {Boolean} interceptTitles
30458         * True to automatically use the element's DOM title value if available (defaults to false)
30459         */
30460        interceptTitles : false,
30461         /**
30462         * @cfg {Boolean} trackMouse
30463         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
30464         */
30465        trackMouse : false,
30466         /**
30467         * @cfg {Boolean} hideOnClick
30468         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
30469         */
30470        hideOnClick : true,
30471         /**
30472         * @cfg {Number} showDelay
30473         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
30474         */
30475        showDelay : 500,
30476         /**
30477         * @cfg {Number} hideDelay
30478         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
30479         */
30480        hideDelay : 200,
30481         /**
30482         * @cfg {Boolean} autoHide
30483         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
30484         * Used in conjunction with hideDelay.
30485         */
30486        autoHide : true,
30487         /**
30488         * @cfg {Boolean}
30489         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
30490         * (defaults to true).  Used in conjunction with autoDismissDelay.
30491         */
30492        autoDismiss : true,
30493         /**
30494         * @cfg {Number}
30495         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
30496         */
30497        autoDismissDelay : 5000,
30498        /**
30499         * @cfg {Boolean} animate
30500         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
30501         */
30502        animate : false,
30503
30504        /**
30505         * @cfg {String} title
30506         * Title text to display (defaults to '').  This can be any valid HTML markup.
30507         */
30508         title: '',
30509        /**
30510         * @cfg {String} text
30511         * Body text to display (defaults to '').  This can be any valid HTML markup.
30512         */
30513         text : '',
30514        /**
30515         * @cfg {String} cls
30516         * A CSS class to apply to the base quick tip element (defaults to '').
30517         */
30518         cls : '',
30519        /**
30520         * @cfg {Number} width
30521         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
30522         * minWidth or maxWidth.
30523         */
30524         width : null,
30525
30526     /**
30527      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
30528      * or display QuickTips in a page.
30529      */
30530        init : function(){
30531           tm = Roo.QuickTips;
30532           cfg = tm.tagConfig;
30533           if(!inited){
30534               if(!Roo.isReady){ // allow calling of init() before onReady
30535                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
30536                   return;
30537               }
30538               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
30539               el.fxDefaults = {stopFx: true};
30540               // maximum custom styling
30541               //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>');
30542               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>');              
30543               tipTitle = el.child('h3');
30544               tipTitle.enableDisplayMode("block");
30545               tipBody = el.child('div.x-tip-bd');
30546               tipBodyText = el.child('div.x-tip-bd-inner');
30547               //bdLeft = el.child('div.x-tip-bd-left');
30548               //bdRight = el.child('div.x-tip-bd-right');
30549               close = el.child('div.x-tip-close');
30550               close.enableDisplayMode("block");
30551               close.on("click", hide);
30552               var d = Roo.get(document);
30553               d.on("mousedown", onDown);
30554               d.on("mouseover", onOver);
30555               d.on("mouseout", onOut);
30556               d.on("mousemove", onMove);
30557               esc = d.addKeyListener(27, hide);
30558               esc.disable();
30559               if(Roo.dd.DD){
30560                   dd = el.initDD("default", null, {
30561                       onDrag : function(){
30562                           el.sync();  
30563                       }
30564                   });
30565                   dd.setHandleElId(tipTitle.id);
30566                   dd.lock();
30567               }
30568               inited = true;
30569           }
30570           this.enable(); 
30571        },
30572
30573     /**
30574      * Configures a new quick tip instance and assigns it to a target element.  The following config options
30575      * are supported:
30576      * <pre>
30577 Property    Type                   Description
30578 ----------  ---------------------  ------------------------------------------------------------------------
30579 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
30580      * </ul>
30581      * @param {Object} config The config object
30582      */
30583        register : function(config){
30584            var cs = config instanceof Array ? config : arguments;
30585            for(var i = 0, len = cs.length; i < len; i++) {
30586                var c = cs[i];
30587                var target = c.target;
30588                if(target){
30589                    if(target instanceof Array){
30590                        for(var j = 0, jlen = target.length; j < jlen; j++){
30591                            tagEls[target[j]] = c;
30592                        }
30593                    }else{
30594                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
30595                    }
30596                }
30597            }
30598        },
30599
30600     /**
30601      * Removes this quick tip from its element and destroys it.
30602      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
30603      */
30604        unregister : function(el){
30605            delete tagEls[Roo.id(el)];
30606        },
30607
30608     /**
30609      * Enable this quick tip.
30610      */
30611        enable : function(){
30612            if(inited && disabled){
30613                locks.pop();
30614                if(locks.length < 1){
30615                    disabled = false;
30616                }
30617            }
30618        },
30619
30620     /**
30621      * Disable this quick tip.
30622      */
30623        disable : function(){
30624           disabled = true;
30625           clearTimeout(showProc);
30626           clearTimeout(hideProc);
30627           clearTimeout(dismissProc);
30628           if(ce){
30629               hide(true);
30630           }
30631           locks.push(1);
30632        },
30633
30634     /**
30635      * Returns true if the quick tip is enabled, else false.
30636      */
30637        isEnabled : function(){
30638             return !disabled;
30639        },
30640
30641         // private
30642        tagConfig : {
30643            namespace : "ext",
30644            attribute : "qtip",
30645            width : "width",
30646            target : "target",
30647            title : "qtitle",
30648            hide : "hide",
30649            cls : "qclass"
30650        }
30651    };
30652 }();
30653
30654 // backwards compat
30655 Roo.QuickTips.tips = Roo.QuickTips.register;/*
30656  * Based on:
30657  * Ext JS Library 1.1.1
30658  * Copyright(c) 2006-2007, Ext JS, LLC.
30659  *
30660  * Originally Released Under LGPL - original licence link has changed is not relivant.
30661  *
30662  * Fork - LGPL
30663  * <script type="text/javascript">
30664  */
30665  
30666
30667 /**
30668  * @class Roo.tree.TreePanel
30669  * @extends Roo.data.Tree
30670
30671  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
30672  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
30673  * @cfg {Boolean} enableDD true to enable drag and drop
30674  * @cfg {Boolean} enableDrag true to enable just drag
30675  * @cfg {Boolean} enableDrop true to enable just drop
30676  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
30677  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
30678  * @cfg {String} ddGroup The DD group this TreePanel belongs to
30679  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
30680  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
30681  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
30682  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
30683  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
30684  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
30685  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
30686  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
30687  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
30688  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
30689  * @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>
30690  * @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>
30691  * 
30692  * @constructor
30693  * @param {String/HTMLElement/Element} el The container element
30694  * @param {Object} config
30695  */
30696 Roo.tree.TreePanel = function(el, config){
30697     var root = false;
30698     var loader = false;
30699     if (config.root) {
30700         root = config.root;
30701         delete config.root;
30702     }
30703     if (config.loader) {
30704         loader = config.loader;
30705         delete config.loader;
30706     }
30707     
30708     Roo.apply(this, config);
30709     Roo.tree.TreePanel.superclass.constructor.call(this);
30710     this.el = Roo.get(el);
30711     this.el.addClass('x-tree');
30712     //console.log(root);
30713     if (root) {
30714         this.setRootNode( Roo.factory(root, Roo.tree));
30715     }
30716     if (loader) {
30717         this.loader = Roo.factory(loader, Roo.tree);
30718     }
30719    /**
30720     * Read-only. The id of the container element becomes this TreePanel's id.
30721     */
30722    this.id = this.el.id;
30723    this.addEvents({
30724         /**
30725         * @event beforeload
30726         * Fires before a node is loaded, return false to cancel
30727         * @param {Node} node The node being loaded
30728         */
30729         "beforeload" : true,
30730         /**
30731         * @event load
30732         * Fires when a node is loaded
30733         * @param {Node} node The node that was loaded
30734         */
30735         "load" : true,
30736         /**
30737         * @event textchange
30738         * Fires when the text for a node is changed
30739         * @param {Node} node The node
30740         * @param {String} text The new text
30741         * @param {String} oldText The old text
30742         */
30743         "textchange" : true,
30744         /**
30745         * @event beforeexpand
30746         * Fires before a node is expanded, return false to cancel.
30747         * @param {Node} node The node
30748         * @param {Boolean} deep
30749         * @param {Boolean} anim
30750         */
30751         "beforeexpand" : true,
30752         /**
30753         * @event beforecollapse
30754         * Fires before a node is collapsed, return false to cancel.
30755         * @param {Node} node The node
30756         * @param {Boolean} deep
30757         * @param {Boolean} anim
30758         */
30759         "beforecollapse" : true,
30760         /**
30761         * @event expand
30762         * Fires when a node is expanded
30763         * @param {Node} node The node
30764         */
30765         "expand" : true,
30766         /**
30767         * @event disabledchange
30768         * Fires when the disabled status of a node changes
30769         * @param {Node} node The node
30770         * @param {Boolean} disabled
30771         */
30772         "disabledchange" : true,
30773         /**
30774         * @event collapse
30775         * Fires when a node is collapsed
30776         * @param {Node} node The node
30777         */
30778         "collapse" : true,
30779         /**
30780         * @event beforeclick
30781         * Fires before click processing on a node. Return false to cancel the default action.
30782         * @param {Node} node The node
30783         * @param {Roo.EventObject} e The event object
30784         */
30785         "beforeclick":true,
30786         /**
30787         * @event checkchange
30788         * Fires when a node with a checkbox's checked property changes
30789         * @param {Node} this This node
30790         * @param {Boolean} checked
30791         */
30792         "checkchange":true,
30793         /**
30794         * @event click
30795         * Fires when a node is clicked
30796         * @param {Node} node The node
30797         * @param {Roo.EventObject} e The event object
30798         */
30799         "click":true,
30800         /**
30801         * @event dblclick
30802         * Fires when a node is double clicked
30803         * @param {Node} node The node
30804         * @param {Roo.EventObject} e The event object
30805         */
30806         "dblclick":true,
30807         /**
30808         * @event contextmenu
30809         * Fires when a node is right clicked
30810         * @param {Node} node The node
30811         * @param {Roo.EventObject} e The event object
30812         */
30813         "contextmenu":true,
30814         /**
30815         * @event beforechildrenrendered
30816         * Fires right before the child nodes for a node are rendered
30817         * @param {Node} node The node
30818         */
30819         "beforechildrenrendered":true,
30820        /**
30821              * @event startdrag
30822              * Fires when a node starts being dragged
30823              * @param {Roo.tree.TreePanel} this
30824              * @param {Roo.tree.TreeNode} node
30825              * @param {event} e The raw browser event
30826              */ 
30827             "startdrag" : true,
30828             /**
30829              * @event enddrag
30830              * Fires when a drag operation is complete
30831              * @param {Roo.tree.TreePanel} this
30832              * @param {Roo.tree.TreeNode} node
30833              * @param {event} e The raw browser event
30834              */
30835             "enddrag" : true,
30836             /**
30837              * @event dragdrop
30838              * Fires when a dragged node is dropped on a valid DD target
30839              * @param {Roo.tree.TreePanel} this
30840              * @param {Roo.tree.TreeNode} node
30841              * @param {DD} dd The dd it was dropped on
30842              * @param {event} e The raw browser event
30843              */
30844             "dragdrop" : true,
30845             /**
30846              * @event beforenodedrop
30847              * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
30848              * passed to handlers has the following properties:<br />
30849              * <ul style="padding:5px;padding-left:16px;">
30850              * <li>tree - The TreePanel</li>
30851              * <li>target - The node being targeted for the drop</li>
30852              * <li>data - The drag data from the drag source</li>
30853              * <li>point - The point of the drop - append, above or below</li>
30854              * <li>source - The drag source</li>
30855              * <li>rawEvent - Raw mouse event</li>
30856              * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
30857              * to be inserted by setting them on this object.</li>
30858              * <li>cancel - Set this to true to cancel the drop.</li>
30859              * </ul>
30860              * @param {Object} dropEvent
30861              */
30862             "beforenodedrop" : true,
30863             /**
30864              * @event nodedrop
30865              * Fires after a DD object is dropped on a node in this tree. The dropEvent
30866              * passed to handlers has the following properties:<br />
30867              * <ul style="padding:5px;padding-left:16px;">
30868              * <li>tree - The TreePanel</li>
30869              * <li>target - The node being targeted for the drop</li>
30870              * <li>data - The drag data from the drag source</li>
30871              * <li>point - The point of the drop - append, above or below</li>
30872              * <li>source - The drag source</li>
30873              * <li>rawEvent - Raw mouse event</li>
30874              * <li>dropNode - Dropped node(s).</li>
30875              * </ul>
30876              * @param {Object} dropEvent
30877              */
30878             "nodedrop" : true,
30879              /**
30880              * @event nodedragover
30881              * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
30882              * passed to handlers has the following properties:<br />
30883              * <ul style="padding:5px;padding-left:16px;">
30884              * <li>tree - The TreePanel</li>
30885              * <li>target - The node being targeted for the drop</li>
30886              * <li>data - The drag data from the drag source</li>
30887              * <li>point - The point of the drop - append, above or below</li>
30888              * <li>source - The drag source</li>
30889              * <li>rawEvent - Raw mouse event</li>
30890              * <li>dropNode - Drop node(s) provided by the source.</li>
30891              * <li>cancel - Set this to true to signal drop not allowed.</li>
30892              * </ul>
30893              * @param {Object} dragOverEvent
30894              */
30895             "nodedragover" : true
30896         
30897    });
30898    if(this.singleExpand){
30899        this.on("beforeexpand", this.restrictExpand, this);
30900    }
30901 };
30902 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
30903     rootVisible : true,
30904     animate: Roo.enableFx,
30905     lines : true,
30906     enableDD : false,
30907     hlDrop : Roo.enableFx,
30908   
30909     renderer: false,
30910     
30911     rendererTip: false,
30912     // private
30913     restrictExpand : function(node){
30914         var p = node.parentNode;
30915         if(p){
30916             if(p.expandedChild && p.expandedChild.parentNode == p){
30917                 p.expandedChild.collapse();
30918             }
30919             p.expandedChild = node;
30920         }
30921     },
30922
30923     // private override
30924     setRootNode : function(node){
30925         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
30926         if(!this.rootVisible){
30927             node.ui = new Roo.tree.RootTreeNodeUI(node);
30928         }
30929         return node;
30930     },
30931
30932     /**
30933      * Returns the container element for this TreePanel
30934      */
30935     getEl : function(){
30936         return this.el;
30937     },
30938
30939     /**
30940      * Returns the default TreeLoader for this TreePanel
30941      */
30942     getLoader : function(){
30943         return this.loader;
30944     },
30945
30946     /**
30947      * Expand all nodes
30948      */
30949     expandAll : function(){
30950         this.root.expand(true);
30951     },
30952
30953     /**
30954      * Collapse all nodes
30955      */
30956     collapseAll : function(){
30957         this.root.collapse(true);
30958     },
30959
30960     /**
30961      * Returns the selection model used by this TreePanel
30962      */
30963     getSelectionModel : function(){
30964         if(!this.selModel){
30965             this.selModel = new Roo.tree.DefaultSelectionModel();
30966         }
30967         return this.selModel;
30968     },
30969
30970     /**
30971      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
30972      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
30973      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
30974      * @return {Array}
30975      */
30976     getChecked : function(a, startNode){
30977         startNode = startNode || this.root;
30978         var r = [];
30979         var f = function(){
30980             if(this.attributes.checked){
30981                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
30982             }
30983         }
30984         startNode.cascade(f);
30985         return r;
30986     },
30987
30988     /**
30989      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
30990      * @param {String} path
30991      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
30992      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
30993      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
30994      */
30995     expandPath : function(path, attr, callback){
30996         attr = attr || "id";
30997         var keys = path.split(this.pathSeparator);
30998         var curNode = this.root;
30999         if(curNode.attributes[attr] != keys[1]){ // invalid root
31000             if(callback){
31001                 callback(false, null);
31002             }
31003             return;
31004         }
31005         var index = 1;
31006         var f = function(){
31007             if(++index == keys.length){
31008                 if(callback){
31009                     callback(true, curNode);
31010                 }
31011                 return;
31012             }
31013             var c = curNode.findChild(attr, keys[index]);
31014             if(!c){
31015                 if(callback){
31016                     callback(false, curNode);
31017                 }
31018                 return;
31019             }
31020             curNode = c;
31021             c.expand(false, false, f);
31022         };
31023         curNode.expand(false, false, f);
31024     },
31025
31026     /**
31027      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
31028      * @param {String} path
31029      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
31030      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
31031      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
31032      */
31033     selectPath : function(path, attr, callback){
31034         attr = attr || "id";
31035         var keys = path.split(this.pathSeparator);
31036         var v = keys.pop();
31037         if(keys.length > 0){
31038             var f = function(success, node){
31039                 if(success && node){
31040                     var n = node.findChild(attr, v);
31041                     if(n){
31042                         n.select();
31043                         if(callback){
31044                             callback(true, n);
31045                         }
31046                     }else if(callback){
31047                         callback(false, n);
31048                     }
31049                 }else{
31050                     if(callback){
31051                         callback(false, n);
31052                     }
31053                 }
31054             };
31055             this.expandPath(keys.join(this.pathSeparator), attr, f);
31056         }else{
31057             this.root.select();
31058             if(callback){
31059                 callback(true, this.root);
31060             }
31061         }
31062     },
31063
31064     getTreeEl : function(){
31065         return this.el;
31066     },
31067
31068     /**
31069      * Trigger rendering of this TreePanel
31070      */
31071     render : function(){
31072         if (this.innerCt) {
31073             return this; // stop it rendering more than once!!
31074         }
31075         
31076         this.innerCt = this.el.createChild({tag:"ul",
31077                cls:"x-tree-root-ct " +
31078                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
31079
31080         if(this.containerScroll){
31081             Roo.dd.ScrollManager.register(this.el);
31082         }
31083         if((this.enableDD || this.enableDrop) && !this.dropZone){
31084            /**
31085             * The dropZone used by this tree if drop is enabled
31086             * @type Roo.tree.TreeDropZone
31087             */
31088              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
31089                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
31090            });
31091         }
31092         if((this.enableDD || this.enableDrag) && !this.dragZone){
31093            /**
31094             * The dragZone used by this tree if drag is enabled
31095             * @type Roo.tree.TreeDragZone
31096             */
31097             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
31098                ddGroup: this.ddGroup || "TreeDD",
31099                scroll: this.ddScroll
31100            });
31101         }
31102         this.getSelectionModel().init(this);
31103         if (!this.root) {
31104             console.log("ROOT not set in tree");
31105             return;
31106         }
31107         this.root.render();
31108         if(!this.rootVisible){
31109             this.root.renderChildren();
31110         }
31111         return this;
31112     }
31113 });/*
31114  * Based on:
31115  * Ext JS Library 1.1.1
31116  * Copyright(c) 2006-2007, Ext JS, LLC.
31117  *
31118  * Originally Released Under LGPL - original licence link has changed is not relivant.
31119  *
31120  * Fork - LGPL
31121  * <script type="text/javascript">
31122  */
31123  
31124
31125 /**
31126  * @class Roo.tree.DefaultSelectionModel
31127  * @extends Roo.util.Observable
31128  * The default single selection for a TreePanel.
31129  */
31130 Roo.tree.DefaultSelectionModel = function(){
31131    this.selNode = null;
31132    
31133    this.addEvents({
31134        /**
31135         * @event selectionchange
31136         * Fires when the selected node changes
31137         * @param {DefaultSelectionModel} this
31138         * @param {TreeNode} node the new selection
31139         */
31140        "selectionchange" : true,
31141
31142        /**
31143         * @event beforeselect
31144         * Fires before the selected node changes, return false to cancel the change
31145         * @param {DefaultSelectionModel} this
31146         * @param {TreeNode} node the new selection
31147         * @param {TreeNode} node the old selection
31148         */
31149        "beforeselect" : true
31150    });
31151 };
31152
31153 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
31154     init : function(tree){
31155         this.tree = tree;
31156         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31157         tree.on("click", this.onNodeClick, this);
31158     },
31159     
31160     onNodeClick : function(node, e){
31161         if (e.ctrlKey && this.selNode == node)  {
31162             this.unselect(node);
31163             return;
31164         }
31165         this.select(node);
31166     },
31167     
31168     /**
31169      * Select a node.
31170      * @param {TreeNode} node The node to select
31171      * @return {TreeNode} The selected node
31172      */
31173     select : function(node){
31174         var last = this.selNode;
31175         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
31176             if(last){
31177                 last.ui.onSelectedChange(false);
31178             }
31179             this.selNode = node;
31180             node.ui.onSelectedChange(true);
31181             this.fireEvent("selectionchange", this, node, last);
31182         }
31183         return node;
31184     },
31185     
31186     /**
31187      * Deselect a node.
31188      * @param {TreeNode} node The node to unselect
31189      */
31190     unselect : function(node){
31191         if(this.selNode == node){
31192             this.clearSelections();
31193         }    
31194     },
31195     
31196     /**
31197      * Clear all selections
31198      */
31199     clearSelections : function(){
31200         var n = this.selNode;
31201         if(n){
31202             n.ui.onSelectedChange(false);
31203             this.selNode = null;
31204             this.fireEvent("selectionchange", this, null);
31205         }
31206         return n;
31207     },
31208     
31209     /**
31210      * Get the selected node
31211      * @return {TreeNode} The selected node
31212      */
31213     getSelectedNode : function(){
31214         return this.selNode;    
31215     },
31216     
31217     /**
31218      * Returns true if the node is selected
31219      * @param {TreeNode} node The node to check
31220      * @return {Boolean}
31221      */
31222     isSelected : function(node){
31223         return this.selNode == node;  
31224     },
31225
31226     /**
31227      * Selects the node above the selected node in the tree, intelligently walking the nodes
31228      * @return TreeNode The new selection
31229      */
31230     selectPrevious : function(){
31231         var s = this.selNode || this.lastSelNode;
31232         if(!s){
31233             return null;
31234         }
31235         var ps = s.previousSibling;
31236         if(ps){
31237             if(!ps.isExpanded() || ps.childNodes.length < 1){
31238                 return this.select(ps);
31239             } else{
31240                 var lc = ps.lastChild;
31241                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
31242                     lc = lc.lastChild;
31243                 }
31244                 return this.select(lc);
31245             }
31246         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
31247             return this.select(s.parentNode);
31248         }
31249         return null;
31250     },
31251
31252     /**
31253      * Selects the node above the selected node in the tree, intelligently walking the nodes
31254      * @return TreeNode The new selection
31255      */
31256     selectNext : function(){
31257         var s = this.selNode || this.lastSelNode;
31258         if(!s){
31259             return null;
31260         }
31261         if(s.firstChild && s.isExpanded()){
31262              return this.select(s.firstChild);
31263          }else if(s.nextSibling){
31264              return this.select(s.nextSibling);
31265          }else if(s.parentNode){
31266             var newS = null;
31267             s.parentNode.bubble(function(){
31268                 if(this.nextSibling){
31269                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
31270                     return false;
31271                 }
31272             });
31273             return newS;
31274          }
31275         return null;
31276     },
31277
31278     onKeyDown : function(e){
31279         var s = this.selNode || this.lastSelNode;
31280         // undesirable, but required
31281         var sm = this;
31282         if(!s){
31283             return;
31284         }
31285         var k = e.getKey();
31286         switch(k){
31287              case e.DOWN:
31288                  e.stopEvent();
31289                  this.selectNext();
31290              break;
31291              case e.UP:
31292                  e.stopEvent();
31293                  this.selectPrevious();
31294              break;
31295              case e.RIGHT:
31296                  e.preventDefault();
31297                  if(s.hasChildNodes()){
31298                      if(!s.isExpanded()){
31299                          s.expand();
31300                      }else if(s.firstChild){
31301                          this.select(s.firstChild, e);
31302                      }
31303                  }
31304              break;
31305              case e.LEFT:
31306                  e.preventDefault();
31307                  if(s.hasChildNodes() && s.isExpanded()){
31308                      s.collapse();
31309                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
31310                      this.select(s.parentNode, e);
31311                  }
31312              break;
31313         };
31314     }
31315 });
31316
31317 /**
31318  * @class Roo.tree.MultiSelectionModel
31319  * @extends Roo.util.Observable
31320  * Multi selection for a TreePanel.
31321  */
31322 Roo.tree.MultiSelectionModel = function(){
31323    this.selNodes = [];
31324    this.selMap = {};
31325    this.addEvents({
31326        /**
31327         * @event selectionchange
31328         * Fires when the selected nodes change
31329         * @param {MultiSelectionModel} this
31330         * @param {Array} nodes Array of the selected nodes
31331         */
31332        "selectionchange" : true
31333    });
31334 };
31335
31336 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
31337     init : function(tree){
31338         this.tree = tree;
31339         tree.getTreeEl().on("keydown", this.onKeyDown, this);
31340         tree.on("click", this.onNodeClick, this);
31341     },
31342     
31343     onNodeClick : function(node, e){
31344         this.select(node, e, e.ctrlKey);
31345     },
31346     
31347     /**
31348      * Select a node.
31349      * @param {TreeNode} node The node to select
31350      * @param {EventObject} e (optional) An event associated with the selection
31351      * @param {Boolean} keepExisting True to retain existing selections
31352      * @return {TreeNode} The selected node
31353      */
31354     select : function(node, e, keepExisting){
31355         if(keepExisting !== true){
31356             this.clearSelections(true);
31357         }
31358         if(this.isSelected(node)){
31359             this.lastSelNode = node;
31360             return node;
31361         }
31362         this.selNodes.push(node);
31363         this.selMap[node.id] = node;
31364         this.lastSelNode = node;
31365         node.ui.onSelectedChange(true);
31366         this.fireEvent("selectionchange", this, this.selNodes);
31367         return node;
31368     },
31369     
31370     /**
31371      * Deselect a node.
31372      * @param {TreeNode} node The node to unselect
31373      */
31374     unselect : function(node){
31375         if(this.selMap[node.id]){
31376             node.ui.onSelectedChange(false);
31377             var sn = this.selNodes;
31378             var index = -1;
31379             if(sn.indexOf){
31380                 index = sn.indexOf(node);
31381             }else{
31382                 for(var i = 0, len = sn.length; i < len; i++){
31383                     if(sn[i] == node){
31384                         index = i;
31385                         break;
31386                     }
31387                 }
31388             }
31389             if(index != -1){
31390                 this.selNodes.splice(index, 1);
31391             }
31392             delete this.selMap[node.id];
31393             this.fireEvent("selectionchange", this, this.selNodes);
31394         }
31395     },
31396     
31397     /**
31398      * Clear all selections
31399      */
31400     clearSelections : function(suppressEvent){
31401         var sn = this.selNodes;
31402         if(sn.length > 0){
31403             for(var i = 0, len = sn.length; i < len; i++){
31404                 sn[i].ui.onSelectedChange(false);
31405             }
31406             this.selNodes = [];
31407             this.selMap = {};
31408             if(suppressEvent !== true){
31409                 this.fireEvent("selectionchange", this, this.selNodes);
31410             }
31411         }
31412     },
31413     
31414     /**
31415      * Returns true if the node is selected
31416      * @param {TreeNode} node The node to check
31417      * @return {Boolean}
31418      */
31419     isSelected : function(node){
31420         return this.selMap[node.id] ? true : false;  
31421     },
31422     
31423     /**
31424      * Returns an array of the selected nodes
31425      * @return {Array}
31426      */
31427     getSelectedNodes : function(){
31428         return this.selNodes;    
31429     },
31430
31431     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
31432
31433     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
31434
31435     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
31436 });/*
31437  * Based on:
31438  * Ext JS Library 1.1.1
31439  * Copyright(c) 2006-2007, Ext JS, LLC.
31440  *
31441  * Originally Released Under LGPL - original licence link has changed is not relivant.
31442  *
31443  * Fork - LGPL
31444  * <script type="text/javascript">
31445  */
31446  
31447 /**
31448  * @class Roo.tree.TreeNode
31449  * @extends Roo.data.Node
31450  * @cfg {String} text The text for this node
31451  * @cfg {Boolean} expanded true to start the node expanded
31452  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
31453  * @cfg {Boolean} allowDrop false if this node cannot be drop on
31454  * @cfg {Boolean} disabled true to start the node disabled
31455  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
31456  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
31457  * @cfg {String} cls A css class to be added to the node
31458  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
31459  * @cfg {String} href URL of the link used for the node (defaults to #)
31460  * @cfg {String} hrefTarget target frame for the link
31461  * @cfg {String} qtip An Ext QuickTip for the node
31462  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
31463  * @cfg {Boolean} singleClickExpand True for single click expand on this node
31464  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
31465  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
31466  * (defaults to undefined with no checkbox rendered)
31467  * @constructor
31468  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
31469  */
31470 Roo.tree.TreeNode = function(attributes){
31471     attributes = attributes || {};
31472     if(typeof attributes == "string"){
31473         attributes = {text: attributes};
31474     }
31475     this.childrenRendered = false;
31476     this.rendered = false;
31477     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
31478     this.expanded = attributes.expanded === true;
31479     this.isTarget = attributes.isTarget !== false;
31480     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
31481     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
31482
31483     /**
31484      * Read-only. The text for this node. To change it use setText().
31485      * @type String
31486      */
31487     this.text = attributes.text;
31488     /**
31489      * True if this node is disabled.
31490      * @type Boolean
31491      */
31492     this.disabled = attributes.disabled === true;
31493
31494     this.addEvents({
31495         /**
31496         * @event textchange
31497         * Fires when the text for this node is changed
31498         * @param {Node} this This node
31499         * @param {String} text The new text
31500         * @param {String} oldText The old text
31501         */
31502         "textchange" : true,
31503         /**
31504         * @event beforeexpand
31505         * Fires before this node is expanded, return false to cancel.
31506         * @param {Node} this This node
31507         * @param {Boolean} deep
31508         * @param {Boolean} anim
31509         */
31510         "beforeexpand" : true,
31511         /**
31512         * @event beforecollapse
31513         * Fires before this node is collapsed, return false to cancel.
31514         * @param {Node} this This node
31515         * @param {Boolean} deep
31516         * @param {Boolean} anim
31517         */
31518         "beforecollapse" : true,
31519         /**
31520         * @event expand
31521         * Fires when this node is expanded
31522         * @param {Node} this This node
31523         */
31524         "expand" : true,
31525         /**
31526         * @event disabledchange
31527         * Fires when the disabled status of this node changes
31528         * @param {Node} this This node
31529         * @param {Boolean} disabled
31530         */
31531         "disabledchange" : true,
31532         /**
31533         * @event collapse
31534         * Fires when this node is collapsed
31535         * @param {Node} this This node
31536         */
31537         "collapse" : true,
31538         /**
31539         * @event beforeclick
31540         * Fires before click processing. Return false to cancel the default action.
31541         * @param {Node} this This node
31542         * @param {Roo.EventObject} e The event object
31543         */
31544         "beforeclick":true,
31545         /**
31546         * @event checkchange
31547         * Fires when a node with a checkbox's checked property changes
31548         * @param {Node} this This node
31549         * @param {Boolean} checked
31550         */
31551         "checkchange":true,
31552         /**
31553         * @event click
31554         * Fires when this node is clicked
31555         * @param {Node} this This node
31556         * @param {Roo.EventObject} e The event object
31557         */
31558         "click":true,
31559         /**
31560         * @event dblclick
31561         * Fires when this node is double clicked
31562         * @param {Node} this This node
31563         * @param {Roo.EventObject} e The event object
31564         */
31565         "dblclick":true,
31566         /**
31567         * @event contextmenu
31568         * Fires when this node is right clicked
31569         * @param {Node} this This node
31570         * @param {Roo.EventObject} e The event object
31571         */
31572         "contextmenu":true,
31573         /**
31574         * @event beforechildrenrendered
31575         * Fires right before the child nodes for this node are rendered
31576         * @param {Node} this This node
31577         */
31578         "beforechildrenrendered":true
31579     });
31580
31581     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
31582
31583     /**
31584      * Read-only. The UI for this node
31585      * @type TreeNodeUI
31586      */
31587     this.ui = new uiClass(this);
31588 };
31589 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
31590     preventHScroll: true,
31591     /**
31592      * Returns true if this node is expanded
31593      * @return {Boolean}
31594      */
31595     isExpanded : function(){
31596         return this.expanded;
31597     },
31598
31599     /**
31600      * Returns the UI object for this node
31601      * @return {TreeNodeUI}
31602      */
31603     getUI : function(){
31604         return this.ui;
31605     },
31606
31607     // private override
31608     setFirstChild : function(node){
31609         var of = this.firstChild;
31610         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
31611         if(this.childrenRendered && of && node != of){
31612             of.renderIndent(true, true);
31613         }
31614         if(this.rendered){
31615             this.renderIndent(true, true);
31616         }
31617     },
31618
31619     // private override
31620     setLastChild : function(node){
31621         var ol = this.lastChild;
31622         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
31623         if(this.childrenRendered && ol && node != ol){
31624             ol.renderIndent(true, true);
31625         }
31626         if(this.rendered){
31627             this.renderIndent(true, true);
31628         }
31629     },
31630
31631     // these methods are overridden to provide lazy rendering support
31632     // private override
31633     appendChild : function(){
31634         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
31635         if(node && this.childrenRendered){
31636             node.render();
31637         }
31638         this.ui.updateExpandIcon();
31639         return node;
31640     },
31641
31642     // private override
31643     removeChild : function(node){
31644         this.ownerTree.getSelectionModel().unselect(node);
31645         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
31646         // if it's been rendered remove dom node
31647         if(this.childrenRendered){
31648             node.ui.remove();
31649         }
31650         if(this.childNodes.length < 1){
31651             this.collapse(false, false);
31652         }else{
31653             this.ui.updateExpandIcon();
31654         }
31655         if(!this.firstChild) {
31656             this.childrenRendered = false;
31657         }
31658         return node;
31659     },
31660
31661     // private override
31662     insertBefore : function(node, refNode){
31663         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
31664         if(newNode && refNode && this.childrenRendered){
31665             node.render();
31666         }
31667         this.ui.updateExpandIcon();
31668         return newNode;
31669     },
31670
31671     /**
31672      * Sets the text for this node
31673      * @param {String} text
31674      */
31675     setText : function(text){
31676         var oldText = this.text;
31677         this.text = text;
31678         this.attributes.text = text;
31679         if(this.rendered){ // event without subscribing
31680             this.ui.onTextChange(this, text, oldText);
31681         }
31682         this.fireEvent("textchange", this, text, oldText);
31683     },
31684
31685     /**
31686      * Triggers selection of this node
31687      */
31688     select : function(){
31689         this.getOwnerTree().getSelectionModel().select(this);
31690     },
31691
31692     /**
31693      * Triggers deselection of this node
31694      */
31695     unselect : function(){
31696         this.getOwnerTree().getSelectionModel().unselect(this);
31697     },
31698
31699     /**
31700      * Returns true if this node is selected
31701      * @return {Boolean}
31702      */
31703     isSelected : function(){
31704         return this.getOwnerTree().getSelectionModel().isSelected(this);
31705     },
31706
31707     /**
31708      * Expand this node.
31709      * @param {Boolean} deep (optional) True to expand all children as well
31710      * @param {Boolean} anim (optional) false to cancel the default animation
31711      * @param {Function} callback (optional) A callback to be called when
31712      * expanding this node completes (does not wait for deep expand to complete).
31713      * Called with 1 parameter, this node.
31714      */
31715     expand : function(deep, anim, callback){
31716         if(!this.expanded){
31717             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
31718                 return;
31719             }
31720             if(!this.childrenRendered){
31721                 this.renderChildren();
31722             }
31723             this.expanded = true;
31724             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
31725                 this.ui.animExpand(function(){
31726                     this.fireEvent("expand", this);
31727                     if(typeof callback == "function"){
31728                         callback(this);
31729                     }
31730                     if(deep === true){
31731                         this.expandChildNodes(true);
31732                     }
31733                 }.createDelegate(this));
31734                 return;
31735             }else{
31736                 this.ui.expand();
31737                 this.fireEvent("expand", this);
31738                 if(typeof callback == "function"){
31739                     callback(this);
31740                 }
31741             }
31742         }else{
31743            if(typeof callback == "function"){
31744                callback(this);
31745            }
31746         }
31747         if(deep === true){
31748             this.expandChildNodes(true);
31749         }
31750     },
31751
31752     isHiddenRoot : function(){
31753         return this.isRoot && !this.getOwnerTree().rootVisible;
31754     },
31755
31756     /**
31757      * Collapse this node.
31758      * @param {Boolean} deep (optional) True to collapse all children as well
31759      * @param {Boolean} anim (optional) false to cancel the default animation
31760      */
31761     collapse : function(deep, anim){
31762         if(this.expanded && !this.isHiddenRoot()){
31763             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
31764                 return;
31765             }
31766             this.expanded = false;
31767             if((this.getOwnerTree().animate && anim !== false) || anim){
31768                 this.ui.animCollapse(function(){
31769                     this.fireEvent("collapse", this);
31770                     if(deep === true){
31771                         this.collapseChildNodes(true);
31772                     }
31773                 }.createDelegate(this));
31774                 return;
31775             }else{
31776                 this.ui.collapse();
31777                 this.fireEvent("collapse", this);
31778             }
31779         }
31780         if(deep === true){
31781             var cs = this.childNodes;
31782             for(var i = 0, len = cs.length; i < len; i++) {
31783                 cs[i].collapse(true, false);
31784             }
31785         }
31786     },
31787
31788     // private
31789     delayedExpand : function(delay){
31790         if(!this.expandProcId){
31791             this.expandProcId = this.expand.defer(delay, this);
31792         }
31793     },
31794
31795     // private
31796     cancelExpand : function(){
31797         if(this.expandProcId){
31798             clearTimeout(this.expandProcId);
31799         }
31800         this.expandProcId = false;
31801     },
31802
31803     /**
31804      * Toggles expanded/collapsed state of the node
31805      */
31806     toggle : function(){
31807         if(this.expanded){
31808             this.collapse();
31809         }else{
31810             this.expand();
31811         }
31812     },
31813
31814     /**
31815      * Ensures all parent nodes are expanded
31816      */
31817     ensureVisible : function(callback){
31818         var tree = this.getOwnerTree();
31819         tree.expandPath(this.parentNode.getPath(), false, function(){
31820             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
31821             Roo.callback(callback);
31822         }.createDelegate(this));
31823     },
31824
31825     /**
31826      * Expand all child nodes
31827      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
31828      */
31829     expandChildNodes : function(deep){
31830         var cs = this.childNodes;
31831         for(var i = 0, len = cs.length; i < len; i++) {
31832                 cs[i].expand(deep);
31833         }
31834     },
31835
31836     /**
31837      * Collapse all child nodes
31838      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
31839      */
31840     collapseChildNodes : function(deep){
31841         var cs = this.childNodes;
31842         for(var i = 0, len = cs.length; i < len; i++) {
31843                 cs[i].collapse(deep);
31844         }
31845     },
31846
31847     /**
31848      * Disables this node
31849      */
31850     disable : function(){
31851         this.disabled = true;
31852         this.unselect();
31853         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31854             this.ui.onDisableChange(this, true);
31855         }
31856         this.fireEvent("disabledchange", this, true);
31857     },
31858
31859     /**
31860      * Enables this node
31861      */
31862     enable : function(){
31863         this.disabled = false;
31864         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
31865             this.ui.onDisableChange(this, false);
31866         }
31867         this.fireEvent("disabledchange", this, false);
31868     },
31869
31870     // private
31871     renderChildren : function(suppressEvent){
31872         if(suppressEvent !== false){
31873             this.fireEvent("beforechildrenrendered", this);
31874         }
31875         var cs = this.childNodes;
31876         for(var i = 0, len = cs.length; i < len; i++){
31877             cs[i].render(true);
31878         }
31879         this.childrenRendered = true;
31880     },
31881
31882     // private
31883     sort : function(fn, scope){
31884         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
31885         if(this.childrenRendered){
31886             var cs = this.childNodes;
31887             for(var i = 0, len = cs.length; i < len; i++){
31888                 cs[i].render(true);
31889             }
31890         }
31891     },
31892
31893     // private
31894     render : function(bulkRender){
31895         this.ui.render(bulkRender);
31896         if(!this.rendered){
31897             this.rendered = true;
31898             if(this.expanded){
31899                 this.expanded = false;
31900                 this.expand(false, false);
31901             }
31902         }
31903     },
31904
31905     // private
31906     renderIndent : function(deep, refresh){
31907         if(refresh){
31908             this.ui.childIndent = null;
31909         }
31910         this.ui.renderIndent();
31911         if(deep === true && this.childrenRendered){
31912             var cs = this.childNodes;
31913             for(var i = 0, len = cs.length; i < len; i++){
31914                 cs[i].renderIndent(true, refresh);
31915             }
31916         }
31917     }
31918 });/*
31919  * Based on:
31920  * Ext JS Library 1.1.1
31921  * Copyright(c) 2006-2007, Ext JS, LLC.
31922  *
31923  * Originally Released Under LGPL - original licence link has changed is not relivant.
31924  *
31925  * Fork - LGPL
31926  * <script type="text/javascript">
31927  */
31928  
31929 /**
31930  * @class Roo.tree.AsyncTreeNode
31931  * @extends Roo.tree.TreeNode
31932  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
31933  * @constructor
31934  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
31935  */
31936  Roo.tree.AsyncTreeNode = function(config){
31937     this.loaded = false;
31938     this.loading = false;
31939     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
31940     /**
31941     * @event beforeload
31942     * Fires before this node is loaded, return false to cancel
31943     * @param {Node} this This node
31944     */
31945     this.addEvents({'beforeload':true, 'load': true});
31946     /**
31947     * @event load
31948     * Fires when this node is loaded
31949     * @param {Node} this This node
31950     */
31951     /**
31952      * The loader used by this node (defaults to using the tree's defined loader)
31953      * @type TreeLoader
31954      * @property loader
31955      */
31956 };
31957 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
31958     expand : function(deep, anim, callback){
31959         if(this.loading){ // if an async load is already running, waiting til it's done
31960             var timer;
31961             var f = function(){
31962                 if(!this.loading){ // done loading
31963                     clearInterval(timer);
31964                     this.expand(deep, anim, callback);
31965                 }
31966             }.createDelegate(this);
31967             timer = setInterval(f, 200);
31968             return;
31969         }
31970         if(!this.loaded){
31971             if(this.fireEvent("beforeload", this) === false){
31972                 return;
31973             }
31974             this.loading = true;
31975             this.ui.beforeLoad(this);
31976             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
31977             if(loader){
31978                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
31979                 return;
31980             }
31981         }
31982         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
31983     },
31984     
31985     /**
31986      * Returns true if this node is currently loading
31987      * @return {Boolean}
31988      */
31989     isLoading : function(){
31990         return this.loading;  
31991     },
31992     
31993     loadComplete : function(deep, anim, callback){
31994         this.loading = false;
31995         this.loaded = true;
31996         this.ui.afterLoad(this);
31997         this.fireEvent("load", this);
31998         this.expand(deep, anim, callback);
31999     },
32000     
32001     /**
32002      * Returns true if this node has been loaded
32003      * @return {Boolean}
32004      */
32005     isLoaded : function(){
32006         return this.loaded;
32007     },
32008     
32009     hasChildNodes : function(){
32010         if(!this.isLeaf() && !this.loaded){
32011             return true;
32012         }else{
32013             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
32014         }
32015     },
32016
32017     /**
32018      * Trigger a reload for this node
32019      * @param {Function} callback
32020      */
32021     reload : function(callback){
32022         this.collapse(false, false);
32023         while(this.firstChild){
32024             this.removeChild(this.firstChild);
32025         }
32026         this.childrenRendered = false;
32027         this.loaded = false;
32028         if(this.isHiddenRoot()){
32029             this.expanded = false;
32030         }
32031         this.expand(false, false, callback);
32032     }
32033 });/*
32034  * Based on:
32035  * Ext JS Library 1.1.1
32036  * Copyright(c) 2006-2007, Ext JS, LLC.
32037  *
32038  * Originally Released Under LGPL - original licence link has changed is not relivant.
32039  *
32040  * Fork - LGPL
32041  * <script type="text/javascript">
32042  */
32043  
32044 /**
32045  * @class Roo.tree.TreeNodeUI
32046  * @constructor
32047  * @param {Object} node The node to render
32048  * The TreeNode UI implementation is separate from the
32049  * tree implementation. Unless you are customizing the tree UI,
32050  * you should never have to use this directly.
32051  */
32052 Roo.tree.TreeNodeUI = function(node){
32053     this.node = node;
32054     this.rendered = false;
32055     this.animating = false;
32056     this.emptyIcon = Roo.BLANK_IMAGE_URL;
32057 };
32058
32059 Roo.tree.TreeNodeUI.prototype = {
32060     removeChild : function(node){
32061         if(this.rendered){
32062             this.ctNode.removeChild(node.ui.getEl());
32063         }
32064     },
32065
32066     beforeLoad : function(){
32067          this.addClass("x-tree-node-loading");
32068     },
32069
32070     afterLoad : function(){
32071          this.removeClass("x-tree-node-loading");
32072     },
32073
32074     onTextChange : function(node, text, oldText){
32075         if(this.rendered){
32076             this.textNode.innerHTML = text;
32077         }
32078     },
32079
32080     onDisableChange : function(node, state){
32081         this.disabled = state;
32082         if(state){
32083             this.addClass("x-tree-node-disabled");
32084         }else{
32085             this.removeClass("x-tree-node-disabled");
32086         }
32087     },
32088
32089     onSelectedChange : function(state){
32090         if(state){
32091             this.focus();
32092             this.addClass("x-tree-selected");
32093         }else{
32094             //this.blur();
32095             this.removeClass("x-tree-selected");
32096         }
32097     },
32098
32099     onMove : function(tree, node, oldParent, newParent, index, refNode){
32100         this.childIndent = null;
32101         if(this.rendered){
32102             var targetNode = newParent.ui.getContainer();
32103             if(!targetNode){//target not rendered
32104                 this.holder = document.createElement("div");
32105                 this.holder.appendChild(this.wrap);
32106                 return;
32107             }
32108             var insertBefore = refNode ? refNode.ui.getEl() : null;
32109             if(insertBefore){
32110                 targetNode.insertBefore(this.wrap, insertBefore);
32111             }else{
32112                 targetNode.appendChild(this.wrap);
32113             }
32114             this.node.renderIndent(true);
32115         }
32116     },
32117
32118     addClass : function(cls){
32119         if(this.elNode){
32120             Roo.fly(this.elNode).addClass(cls);
32121         }
32122     },
32123
32124     removeClass : function(cls){
32125         if(this.elNode){
32126             Roo.fly(this.elNode).removeClass(cls);
32127         }
32128     },
32129
32130     remove : function(){
32131         if(this.rendered){
32132             this.holder = document.createElement("div");
32133             this.holder.appendChild(this.wrap);
32134         }
32135     },
32136
32137     fireEvent : function(){
32138         return this.node.fireEvent.apply(this.node, arguments);
32139     },
32140
32141     initEvents : function(){
32142         this.node.on("move", this.onMove, this);
32143         var E = Roo.EventManager;
32144         var a = this.anchor;
32145
32146         var el = Roo.fly(a, '_treeui');
32147
32148         if(Roo.isOpera){ // opera render bug ignores the CSS
32149             el.setStyle("text-decoration", "none");
32150         }
32151
32152         el.on("click", this.onClick, this);
32153         el.on("dblclick", this.onDblClick, this);
32154
32155         if(this.checkbox){
32156             Roo.EventManager.on(this.checkbox,
32157                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
32158         }
32159
32160         el.on("contextmenu", this.onContextMenu, this);
32161
32162         var icon = Roo.fly(this.iconNode);
32163         icon.on("click", this.onClick, this);
32164         icon.on("dblclick", this.onDblClick, this);
32165         icon.on("contextmenu", this.onContextMenu, this);
32166         E.on(this.ecNode, "click", this.ecClick, this, true);
32167
32168         if(this.node.disabled){
32169             this.addClass("x-tree-node-disabled");
32170         }
32171         if(this.node.hidden){
32172             this.addClass("x-tree-node-disabled");
32173         }
32174         var ot = this.node.getOwnerTree();
32175         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
32176         if(dd && (!this.node.isRoot || ot.rootVisible)){
32177             Roo.dd.Registry.register(this.elNode, {
32178                 node: this.node,
32179                 handles: this.getDDHandles(),
32180                 isHandle: false
32181             });
32182         }
32183     },
32184
32185     getDDHandles : function(){
32186         return [this.iconNode, this.textNode];
32187     },
32188
32189     hide : function(){
32190         if(this.rendered){
32191             this.wrap.style.display = "none";
32192         }
32193     },
32194
32195     show : function(){
32196         if(this.rendered){
32197             this.wrap.style.display = "";
32198         }
32199     },
32200
32201     onContextMenu : function(e){
32202         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
32203             e.preventDefault();
32204             this.focus();
32205             this.fireEvent("contextmenu", this.node, e);
32206         }
32207     },
32208
32209     onClick : function(e){
32210         if(this.dropping){
32211             e.stopEvent();
32212             return;
32213         }
32214         if(this.fireEvent("beforeclick", this.node, e) !== false){
32215             if(!this.disabled && this.node.attributes.href){
32216                 this.fireEvent("click", this.node, e);
32217                 return;
32218             }
32219             e.preventDefault();
32220             if(this.disabled){
32221                 return;
32222             }
32223
32224             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
32225                 this.node.toggle();
32226             }
32227
32228             this.fireEvent("click", this.node, e);
32229         }else{
32230             e.stopEvent();
32231         }
32232     },
32233
32234     onDblClick : function(e){
32235         e.preventDefault();
32236         if(this.disabled){
32237             return;
32238         }
32239         if(this.checkbox){
32240             this.toggleCheck();
32241         }
32242         if(!this.animating && this.node.hasChildNodes()){
32243             this.node.toggle();
32244         }
32245         this.fireEvent("dblclick", this.node, e);
32246     },
32247
32248     onCheckChange : function(){
32249         var checked = this.checkbox.checked;
32250         this.node.attributes.checked = checked;
32251         this.fireEvent('checkchange', this.node, checked);
32252     },
32253
32254     ecClick : function(e){
32255         if(!this.animating && this.node.hasChildNodes()){
32256             this.node.toggle();
32257         }
32258     },
32259
32260     startDrop : function(){
32261         this.dropping = true;
32262     },
32263
32264     // delayed drop so the click event doesn't get fired on a drop
32265     endDrop : function(){
32266        setTimeout(function(){
32267            this.dropping = false;
32268        }.createDelegate(this), 50);
32269     },
32270
32271     expand : function(){
32272         this.updateExpandIcon();
32273         this.ctNode.style.display = "";
32274     },
32275
32276     focus : function(){
32277         if(!this.node.preventHScroll){
32278             try{this.anchor.focus();
32279             }catch(e){}
32280         }else if(!Roo.isIE){
32281             try{
32282                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
32283                 var l = noscroll.scrollLeft;
32284                 this.anchor.focus();
32285                 noscroll.scrollLeft = l;
32286             }catch(e){}
32287         }
32288     },
32289
32290     toggleCheck : function(value){
32291         var cb = this.checkbox;
32292         if(cb){
32293             cb.checked = (value === undefined ? !cb.checked : value);
32294         }
32295     },
32296
32297     blur : function(){
32298         try{
32299             this.anchor.blur();
32300         }catch(e){}
32301     },
32302
32303     animExpand : function(callback){
32304         var ct = Roo.get(this.ctNode);
32305         ct.stopFx();
32306         if(!this.node.hasChildNodes()){
32307             this.updateExpandIcon();
32308             this.ctNode.style.display = "";
32309             Roo.callback(callback);
32310             return;
32311         }
32312         this.animating = true;
32313         this.updateExpandIcon();
32314
32315         ct.slideIn('t', {
32316            callback : function(){
32317                this.animating = false;
32318                Roo.callback(callback);
32319             },
32320             scope: this,
32321             duration: this.node.ownerTree.duration || .25
32322         });
32323     },
32324
32325     highlight : function(){
32326         var tree = this.node.getOwnerTree();
32327         Roo.fly(this.wrap).highlight(
32328             tree.hlColor || "C3DAF9",
32329             {endColor: tree.hlBaseColor}
32330         );
32331     },
32332
32333     collapse : function(){
32334         this.updateExpandIcon();
32335         this.ctNode.style.display = "none";
32336     },
32337
32338     animCollapse : function(callback){
32339         var ct = Roo.get(this.ctNode);
32340         ct.enableDisplayMode('block');
32341         ct.stopFx();
32342
32343         this.animating = true;
32344         this.updateExpandIcon();
32345
32346         ct.slideOut('t', {
32347             callback : function(){
32348                this.animating = false;
32349                Roo.callback(callback);
32350             },
32351             scope: this,
32352             duration: this.node.ownerTree.duration || .25
32353         });
32354     },
32355
32356     getContainer : function(){
32357         return this.ctNode;
32358     },
32359
32360     getEl : function(){
32361         return this.wrap;
32362     },
32363
32364     appendDDGhost : function(ghostNode){
32365         ghostNode.appendChild(this.elNode.cloneNode(true));
32366     },
32367
32368     getDDRepairXY : function(){
32369         return Roo.lib.Dom.getXY(this.iconNode);
32370     },
32371
32372     onRender : function(){
32373         this.render();
32374     },
32375
32376     render : function(bulkRender){
32377         var n = this.node, a = n.attributes;
32378         var targetNode = n.parentNode ?
32379               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
32380
32381         if(!this.rendered){
32382             this.rendered = true;
32383
32384             this.renderElements(n, a, targetNode, bulkRender);
32385
32386             if(a.qtip){
32387                if(this.textNode.setAttributeNS){
32388                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
32389                    if(a.qtipTitle){
32390                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
32391                    }
32392                }else{
32393                    this.textNode.setAttribute("ext:qtip", a.qtip);
32394                    if(a.qtipTitle){
32395                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
32396                    }
32397                }
32398             }else if(a.qtipCfg){
32399                 a.qtipCfg.target = Roo.id(this.textNode);
32400                 Roo.QuickTips.register(a.qtipCfg);
32401             }
32402             this.initEvents();
32403             if(!this.node.expanded){
32404                 this.updateExpandIcon();
32405             }
32406         }else{
32407             if(bulkRender === true) {
32408                 targetNode.appendChild(this.wrap);
32409             }
32410         }
32411     },
32412
32413     renderElements : function(n, a, targetNode, bulkRender){
32414         // add some indent caching, this helps performance when rendering a large tree
32415         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
32416         var t = n.getOwnerTree();
32417         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
32418         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
32419         var cb = typeof a.checked == 'boolean';
32420         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
32421         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
32422             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
32423             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
32424             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
32425             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
32426             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
32427              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
32428                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
32429             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
32430             "</li>"];
32431
32432         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
32433             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
32434                                 n.nextSibling.ui.getEl(), buf.join(""));
32435         }else{
32436             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
32437         }
32438
32439         this.elNode = this.wrap.childNodes[0];
32440         this.ctNode = this.wrap.childNodes[1];
32441         var cs = this.elNode.childNodes;
32442         this.indentNode = cs[0];
32443         this.ecNode = cs[1];
32444         this.iconNode = cs[2];
32445         var index = 3;
32446         if(cb){
32447             this.checkbox = cs[3];
32448             index++;
32449         }
32450         this.anchor = cs[index];
32451         this.textNode = cs[index].firstChild;
32452     },
32453
32454     getAnchor : function(){
32455         return this.anchor;
32456     },
32457
32458     getTextEl : function(){
32459         return this.textNode;
32460     },
32461
32462     getIconEl : function(){
32463         return this.iconNode;
32464     },
32465
32466     isChecked : function(){
32467         return this.checkbox ? this.checkbox.checked : false;
32468     },
32469
32470     updateExpandIcon : function(){
32471         if(this.rendered){
32472             var n = this.node, c1, c2;
32473             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
32474             var hasChild = n.hasChildNodes();
32475             if(hasChild){
32476                 if(n.expanded){
32477                     cls += "-minus";
32478                     c1 = "x-tree-node-collapsed";
32479                     c2 = "x-tree-node-expanded";
32480                 }else{
32481                     cls += "-plus";
32482                     c1 = "x-tree-node-expanded";
32483                     c2 = "x-tree-node-collapsed";
32484                 }
32485                 if(this.wasLeaf){
32486                     this.removeClass("x-tree-node-leaf");
32487                     this.wasLeaf = false;
32488                 }
32489                 if(this.c1 != c1 || this.c2 != c2){
32490                     Roo.fly(this.elNode).replaceClass(c1, c2);
32491                     this.c1 = c1; this.c2 = c2;
32492                 }
32493             }else{
32494                 if(!this.wasLeaf){
32495                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
32496                     delete this.c1;
32497                     delete this.c2;
32498                     this.wasLeaf = true;
32499                 }
32500             }
32501             var ecc = "x-tree-ec-icon "+cls;
32502             if(this.ecc != ecc){
32503                 this.ecNode.className = ecc;
32504                 this.ecc = ecc;
32505             }
32506         }
32507     },
32508
32509     getChildIndent : function(){
32510         if(!this.childIndent){
32511             var buf = [];
32512             var p = this.node;
32513             while(p){
32514                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
32515                     if(!p.isLast()) {
32516                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
32517                     } else {
32518                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
32519                     }
32520                 }
32521                 p = p.parentNode;
32522             }
32523             this.childIndent = buf.join("");
32524         }
32525         return this.childIndent;
32526     },
32527
32528     renderIndent : function(){
32529         if(this.rendered){
32530             var indent = "";
32531             var p = this.node.parentNode;
32532             if(p){
32533                 indent = p.ui.getChildIndent();
32534             }
32535             if(this.indentMarkup != indent){ // don't rerender if not required
32536                 this.indentNode.innerHTML = indent;
32537                 this.indentMarkup = indent;
32538             }
32539             this.updateExpandIcon();
32540         }
32541     }
32542 };
32543
32544 Roo.tree.RootTreeNodeUI = function(){
32545     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
32546 };
32547 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
32548     render : function(){
32549         if(!this.rendered){
32550             var targetNode = this.node.ownerTree.innerCt.dom;
32551             this.node.expanded = true;
32552             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
32553             this.wrap = this.ctNode = targetNode.firstChild;
32554         }
32555     },
32556     collapse : function(){
32557     },
32558     expand : function(){
32559     }
32560 });/*
32561  * Based on:
32562  * Ext JS Library 1.1.1
32563  * Copyright(c) 2006-2007, Ext JS, LLC.
32564  *
32565  * Originally Released Under LGPL - original licence link has changed is not relivant.
32566  *
32567  * Fork - LGPL
32568  * <script type="text/javascript">
32569  */
32570 /**
32571  * @class Roo.tree.TreeLoader
32572  * @extends Roo.util.Observable
32573  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
32574  * nodes from a specified URL. The response must be a javascript Array definition
32575  * who's elements are node definition objects. eg:
32576  * <pre><code>
32577    [{ 'id': 1, 'text': 'A folder Node', 'leaf': false },
32578     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }]
32579 </code></pre>
32580  * <br><br>
32581  * A server request is sent, and child nodes are loaded only when a node is expanded.
32582  * The loading node's id is passed to the server under the parameter name "node" to
32583  * enable the server to produce the correct child nodes.
32584  * <br><br>
32585  * To pass extra parameters, an event handler may be attached to the "beforeload"
32586  * event, and the parameters specified in the TreeLoader's baseParams property:
32587  * <pre><code>
32588     myTreeLoader.on("beforeload", function(treeLoader, node) {
32589         this.baseParams.category = node.attributes.category;
32590     }, this);
32591 </code></pre><
32592  * This would pass an HTTP parameter called "category" to the server containing
32593  * the value of the Node's "category" attribute.
32594  * @constructor
32595  * Creates a new Treeloader.
32596  * @param {Object} config A config object containing config properties.
32597  */
32598 Roo.tree.TreeLoader = function(config){
32599     this.baseParams = {};
32600     this.requestMethod = "POST";
32601     Roo.apply(this, config);
32602
32603     this.addEvents({
32604     
32605         /**
32606          * @event beforeload
32607          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
32608          * @param {Object} This TreeLoader object.
32609          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32610          * @param {Object} callback The callback function specified in the {@link #load} call.
32611          */
32612         beforeload : true,
32613         /**
32614          * @event load
32615          * Fires when the node has been successfuly loaded.
32616          * @param {Object} This TreeLoader object.
32617          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32618          * @param {Object} response The response object containing the data from the server.
32619          */
32620         load : true,
32621         /**
32622          * @event loadexception
32623          * Fires if the network request failed.
32624          * @param {Object} This TreeLoader object.
32625          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
32626          * @param {Object} response The response object containing the data from the server.
32627          */
32628         loadexception : true,
32629         /**
32630          * @event create
32631          * Fires before a node is created, enabling you to return custom Node types 
32632          * @param {Object} This TreeLoader object.
32633          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
32634          */
32635         create : true
32636     });
32637
32638     Roo.tree.TreeLoader.superclass.constructor.call(this);
32639 };
32640
32641 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
32642     /**
32643     * @cfg {String} dataUrl The URL from which to request a Json string which
32644     * specifies an array of node definition object representing the child nodes
32645     * to be loaded.
32646     */
32647     /**
32648     * @cfg {Object} baseParams (optional) An object containing properties which
32649     * specify HTTP parameters to be passed to each request for child nodes.
32650     */
32651     /**
32652     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
32653     * created by this loader. If the attributes sent by the server have an attribute in this object,
32654     * they take priority.
32655     */
32656     /**
32657     * @cfg {Object} uiProviders (optional) An object containing properties which
32658     * 
32659     * DEPRECIATED - use 'create' event handler to modify attributes - which affect creation.
32660     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
32661     * <i>uiProvider</i> attribute of a returned child node is a string rather
32662     * than a reference to a TreeNodeUI implementation, this that string value
32663     * is used as a property name in the uiProviders object. You can define the provider named
32664     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
32665     */
32666     uiProviders : {},
32667
32668     /**
32669     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
32670     * child nodes before loading.
32671     */
32672     clearOnLoad : true,
32673
32674     /**
32675     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
32676     * property on loading, rather than expecting an array. (eg. more compatible to a standard
32677     * Grid query { data : [ .....] }
32678     */
32679     
32680     root : false,
32681      /**
32682     * @cfg {String} queryParam (optional) 
32683     * Name of the query as it will be passed on the querystring (defaults to 'node')
32684     * eg. the request will be ?node=[id]
32685     */
32686     
32687     
32688     queryParam: false,
32689     
32690     /**
32691      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
32692      * This is called automatically when a node is expanded, but may be used to reload
32693      * a node (or append new children if the {@link #clearOnLoad} option is false.)
32694      * @param {Roo.tree.TreeNode} node
32695      * @param {Function} callback
32696      */
32697     load : function(node, callback){
32698         if(this.clearOnLoad){
32699             while(node.firstChild){
32700                 node.removeChild(node.firstChild);
32701             }
32702         }
32703         if(node.attributes.children){ // preloaded json children
32704             var cs = node.attributes.children;
32705             for(var i = 0, len = cs.length; i < len; i++){
32706                 node.appendChild(this.createNode(cs[i]));
32707             }
32708             if(typeof callback == "function"){
32709                 callback();
32710             }
32711         }else if(this.dataUrl){
32712             this.requestData(node, callback);
32713         }
32714     },
32715
32716     getParams: function(node){
32717         var buf = [], bp = this.baseParams;
32718         for(var key in bp){
32719             if(typeof bp[key] != "function"){
32720                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
32721             }
32722         }
32723         var n = this.queryParam === false ? 'node' : this.queryParam;
32724         buf.push(n + "=", encodeURIComponent(node.id));
32725         return buf.join("");
32726     },
32727
32728     requestData : function(node, callback){
32729         if(this.fireEvent("beforeload", this, node, callback) !== false){
32730             this.transId = Roo.Ajax.request({
32731                 method:this.requestMethod,
32732                 url: this.dataUrl||this.url,
32733                 success: this.handleResponse,
32734                 failure: this.handleFailure,
32735                 scope: this,
32736                 argument: {callback: callback, node: node},
32737                 params: this.getParams(node)
32738             });
32739         }else{
32740             // if the load is cancelled, make sure we notify
32741             // the node that we are done
32742             if(typeof callback == "function"){
32743                 callback();
32744             }
32745         }
32746     },
32747
32748     isLoading : function(){
32749         return this.transId ? true : false;
32750     },
32751
32752     abort : function(){
32753         if(this.isLoading()){
32754             Roo.Ajax.abort(this.transId);
32755         }
32756     },
32757
32758     // private
32759     createNode : function(attr){
32760         // apply baseAttrs, nice idea Corey!
32761         if(this.baseAttrs){
32762             Roo.applyIf(attr, this.baseAttrs);
32763         }
32764         if(this.applyLoader !== false){
32765             attr.loader = this;
32766         }
32767         // uiProvider = depreciated..
32768         
32769         if(typeof(attr.uiProvider) == 'string'){
32770            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
32771                 /**  eval:var:attr */ eval(attr.uiProvider);
32772         }
32773         if(typeof(this.uiProviders['default']) != 'undefined') {
32774             attr.uiProvider = this.uiProviders['default'];
32775         }
32776         
32777         this.fireEvent('create', this, attr);
32778         
32779         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
32780         return(attr.leaf ?
32781                         new Roo.tree.TreeNode(attr) :
32782                         new Roo.tree.AsyncTreeNode(attr));
32783     },
32784
32785     processResponse : function(response, node, callback){
32786         var json = response.responseText;
32787         try {
32788             
32789             var o = /**  eval:var:zzzzzzzzzz */ eval("("+json+")");
32790             if (this.root !== false) {
32791                 o = o[this.root];
32792             }
32793             
32794             for(var i = 0, len = o.length; i < len; i++){
32795                 var n = this.createNode(o[i]);
32796                 if(n){
32797                     node.appendChild(n);
32798                 }
32799             }
32800             if(typeof callback == "function"){
32801                 callback(this, node);
32802             }
32803         }catch(e){
32804             this.handleFailure(response);
32805         }
32806     },
32807
32808     handleResponse : function(response){
32809         this.transId = false;
32810         var a = response.argument;
32811         this.processResponse(response, a.node, a.callback);
32812         this.fireEvent("load", this, a.node, response);
32813     },
32814
32815     handleFailure : function(response){
32816         this.transId = false;
32817         var a = response.argument;
32818         this.fireEvent("loadexception", this, a.node, response);
32819         if(typeof a.callback == "function"){
32820             a.callback(this, a.node);
32821         }
32822     }
32823 });/*
32824  * Based on:
32825  * Ext JS Library 1.1.1
32826  * Copyright(c) 2006-2007, Ext JS, LLC.
32827  *
32828  * Originally Released Under LGPL - original licence link has changed is not relivant.
32829  *
32830  * Fork - LGPL
32831  * <script type="text/javascript">
32832  */
32833
32834 /**
32835 * @class Roo.tree.TreeFilter
32836 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
32837 * @param {TreePanel} tree
32838 * @param {Object} config (optional)
32839  */
32840 Roo.tree.TreeFilter = function(tree, config){
32841     this.tree = tree;
32842     this.filtered = {};
32843     Roo.apply(this, config);
32844 };
32845
32846 Roo.tree.TreeFilter.prototype = {
32847     clearBlank:false,
32848     reverse:false,
32849     autoClear:false,
32850     remove:false,
32851
32852      /**
32853      * Filter the data by a specific attribute.
32854      * @param {String/RegExp} value Either string that the attribute value
32855      * should start with or a RegExp to test against the attribute
32856      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
32857      * @param {TreeNode} startNode (optional) The node to start the filter at.
32858      */
32859     filter : function(value, attr, startNode){
32860         attr = attr || "text";
32861         var f;
32862         if(typeof value == "string"){
32863             var vlen = value.length;
32864             // auto clear empty filter
32865             if(vlen == 0 && this.clearBlank){
32866                 this.clear();
32867                 return;
32868             }
32869             value = value.toLowerCase();
32870             f = function(n){
32871                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
32872             };
32873         }else if(value.exec){ // regex?
32874             f = function(n){
32875                 return value.test(n.attributes[attr]);
32876             };
32877         }else{
32878             throw 'Illegal filter type, must be string or regex';
32879         }
32880         this.filterBy(f, null, startNode);
32881         },
32882
32883     /**
32884      * Filter by a function. The passed function will be called with each
32885      * node in the tree (or from the startNode). If the function returns true, the node is kept
32886      * otherwise it is filtered. If a node is filtered, its children are also filtered.
32887      * @param {Function} fn The filter function
32888      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
32889      */
32890     filterBy : function(fn, scope, startNode){
32891         startNode = startNode || this.tree.root;
32892         if(this.autoClear){
32893             this.clear();
32894         }
32895         var af = this.filtered, rv = this.reverse;
32896         var f = function(n){
32897             if(n == startNode){
32898                 return true;
32899             }
32900             if(af[n.id]){
32901                 return false;
32902             }
32903             var m = fn.call(scope || n, n);
32904             if(!m || rv){
32905                 af[n.id] = n;
32906                 n.ui.hide();
32907                 return false;
32908             }
32909             return true;
32910         };
32911         startNode.cascade(f);
32912         if(this.remove){
32913            for(var id in af){
32914                if(typeof id != "function"){
32915                    var n = af[id];
32916                    if(n && n.parentNode){
32917                        n.parentNode.removeChild(n);
32918                    }
32919                }
32920            }
32921         }
32922     },
32923
32924     /**
32925      * Clears the current filter. Note: with the "remove" option
32926      * set a filter cannot be cleared.
32927      */
32928     clear : function(){
32929         var t = this.tree;
32930         var af = this.filtered;
32931         for(var id in af){
32932             if(typeof id != "function"){
32933                 var n = af[id];
32934                 if(n){
32935                     n.ui.show();
32936                 }
32937             }
32938         }
32939         this.filtered = {};
32940     }
32941 };
32942 /*
32943  * Based on:
32944  * Ext JS Library 1.1.1
32945  * Copyright(c) 2006-2007, Ext JS, LLC.
32946  *
32947  * Originally Released Under LGPL - original licence link has changed is not relivant.
32948  *
32949  * Fork - LGPL
32950  * <script type="text/javascript">
32951  */
32952  
32953
32954 /**
32955  * @class Roo.tree.TreeSorter
32956  * Provides sorting of nodes in a TreePanel
32957  * 
32958  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
32959  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
32960  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
32961  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
32962  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
32963  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
32964  * @constructor
32965  * @param {TreePanel} tree
32966  * @param {Object} config
32967  */
32968 Roo.tree.TreeSorter = function(tree, config){
32969     Roo.apply(this, config);
32970     tree.on("beforechildrenrendered", this.doSort, this);
32971     tree.on("append", this.updateSort, this);
32972     tree.on("insert", this.updateSort, this);
32973     
32974     var dsc = this.dir && this.dir.toLowerCase() == "desc";
32975     var p = this.property || "text";
32976     var sortType = this.sortType;
32977     var fs = this.folderSort;
32978     var cs = this.caseSensitive === true;
32979     var leafAttr = this.leafAttr || 'leaf';
32980
32981     this.sortFn = function(n1, n2){
32982         if(fs){
32983             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
32984                 return 1;
32985             }
32986             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
32987                 return -1;
32988             }
32989         }
32990         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
32991         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
32992         if(v1 < v2){
32993                         return dsc ? +1 : -1;
32994                 }else if(v1 > v2){
32995                         return dsc ? -1 : +1;
32996         }else{
32997                 return 0;
32998         }
32999     };
33000 };
33001
33002 Roo.tree.TreeSorter.prototype = {
33003     doSort : function(node){
33004         node.sort(this.sortFn);
33005     },
33006     
33007     compareNodes : function(n1, n2){
33008         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
33009     },
33010     
33011     updateSort : function(tree, node){
33012         if(node.childrenRendered){
33013             this.doSort.defer(1, this, [node]);
33014         }
33015     }
33016 };/*
33017  * Based on:
33018  * Ext JS Library 1.1.1
33019  * Copyright(c) 2006-2007, Ext JS, LLC.
33020  *
33021  * Originally Released Under LGPL - original licence link has changed is not relivant.
33022  *
33023  * Fork - LGPL
33024  * <script type="text/javascript">
33025  */
33026
33027 if(Roo.dd.DropZone){
33028     
33029 Roo.tree.TreeDropZone = function(tree, config){
33030     this.allowParentInsert = false;
33031     this.allowContainerDrop = false;
33032     this.appendOnly = false;
33033     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
33034     this.tree = tree;
33035     this.lastInsertClass = "x-tree-no-status";
33036     this.dragOverData = {};
33037 };
33038
33039 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
33040     ddGroup : "TreeDD",
33041     
33042     expandDelay : 1000,
33043     
33044     expandNode : function(node){
33045         if(node.hasChildNodes() && !node.isExpanded()){
33046             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
33047         }
33048     },
33049     
33050     queueExpand : function(node){
33051         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
33052     },
33053     
33054     cancelExpand : function(){
33055         if(this.expandProcId){
33056             clearTimeout(this.expandProcId);
33057             this.expandProcId = false;
33058         }
33059     },
33060     
33061     isValidDropPoint : function(n, pt, dd, e, data){
33062         if(!n || !data){ return false; }
33063         var targetNode = n.node;
33064         var dropNode = data.node;
33065         // default drop rules
33066         if(!(targetNode && targetNode.isTarget && pt)){
33067             return false;
33068         }
33069         if(pt == "append" && targetNode.allowChildren === false){
33070             return false;
33071         }
33072         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
33073             return false;
33074         }
33075         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
33076             return false;
33077         }
33078         // reuse the object
33079         var overEvent = this.dragOverData;
33080         overEvent.tree = this.tree;
33081         overEvent.target = targetNode;
33082         overEvent.data = data;
33083         overEvent.point = pt;
33084         overEvent.source = dd;
33085         overEvent.rawEvent = e;
33086         overEvent.dropNode = dropNode;
33087         overEvent.cancel = false;  
33088         var result = this.tree.fireEvent("nodedragover", overEvent);
33089         return overEvent.cancel === false && result !== false;
33090     },
33091     
33092     getDropPoint : function(e, n, dd){
33093         var tn = n.node;
33094         if(tn.isRoot){
33095             return tn.allowChildren !== false ? "append" : false; // always append for root
33096         }
33097         var dragEl = n.ddel;
33098         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
33099         var y = Roo.lib.Event.getPageY(e);
33100         //var noAppend = tn.allowChildren === false || tn.isLeaf();
33101         
33102         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
33103         var noAppend = tn.allowChildren === false;
33104         if(this.appendOnly || tn.parentNode.allowChildren === false){
33105             return noAppend ? false : "append";
33106         }
33107         var noBelow = false;
33108         if(!this.allowParentInsert){
33109             noBelow = tn.hasChildNodes() && tn.isExpanded();
33110         }
33111         var q = (b - t) / (noAppend ? 2 : 3);
33112         if(y >= t && y < (t + q)){
33113             return "above";
33114         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
33115             return "below";
33116         }else{
33117             return "append";
33118         }
33119     },
33120     
33121     onNodeEnter : function(n, dd, e, data){
33122         this.cancelExpand();
33123     },
33124     
33125     onNodeOver : function(n, dd, e, data){
33126         var pt = this.getDropPoint(e, n, dd);
33127         var node = n.node;
33128         
33129         // auto node expand check
33130         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
33131             this.queueExpand(node);
33132         }else if(pt != "append"){
33133             this.cancelExpand();
33134         }
33135         
33136         // set the insert point style on the target node
33137         var returnCls = this.dropNotAllowed;
33138         if(this.isValidDropPoint(n, pt, dd, e, data)){
33139            if(pt){
33140                var el = n.ddel;
33141                var cls;
33142                if(pt == "above"){
33143                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
33144                    cls = "x-tree-drag-insert-above";
33145                }else if(pt == "below"){
33146                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
33147                    cls = "x-tree-drag-insert-below";
33148                }else{
33149                    returnCls = "x-tree-drop-ok-append";
33150                    cls = "x-tree-drag-append";
33151                }
33152                if(this.lastInsertClass != cls){
33153                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
33154                    this.lastInsertClass = cls;
33155                }
33156            }
33157        }
33158        return returnCls;
33159     },
33160     
33161     onNodeOut : function(n, dd, e, data){
33162         this.cancelExpand();
33163         this.removeDropIndicators(n);
33164     },
33165     
33166     onNodeDrop : function(n, dd, e, data){
33167         var point = this.getDropPoint(e, n, dd);
33168         var targetNode = n.node;
33169         targetNode.ui.startDrop();
33170         if(!this.isValidDropPoint(n, point, dd, e, data)){
33171             targetNode.ui.endDrop();
33172             return false;
33173         }
33174         // first try to find the drop node
33175         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
33176         var dropEvent = {
33177             tree : this.tree,
33178             target: targetNode,
33179             data: data,
33180             point: point,
33181             source: dd,
33182             rawEvent: e,
33183             dropNode: dropNode,
33184             cancel: !dropNode   
33185         };
33186         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
33187         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
33188             targetNode.ui.endDrop();
33189             return false;
33190         }
33191         // allow target changing
33192         targetNode = dropEvent.target;
33193         if(point == "append" && !targetNode.isExpanded()){
33194             targetNode.expand(false, null, function(){
33195                 this.completeDrop(dropEvent);
33196             }.createDelegate(this));
33197         }else{
33198             this.completeDrop(dropEvent);
33199         }
33200         return true;
33201     },
33202     
33203     completeDrop : function(de){
33204         var ns = de.dropNode, p = de.point, t = de.target;
33205         if(!(ns instanceof Array)){
33206             ns = [ns];
33207         }
33208         var n;
33209         for(var i = 0, len = ns.length; i < len; i++){
33210             n = ns[i];
33211             if(p == "above"){
33212                 t.parentNode.insertBefore(n, t);
33213             }else if(p == "below"){
33214                 t.parentNode.insertBefore(n, t.nextSibling);
33215             }else{
33216                 t.appendChild(n);
33217             }
33218         }
33219         n.ui.focus();
33220         if(this.tree.hlDrop){
33221             n.ui.highlight();
33222         }
33223         t.ui.endDrop();
33224         this.tree.fireEvent("nodedrop", de);
33225     },
33226     
33227     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
33228         if(this.tree.hlDrop){
33229             dropNode.ui.focus();
33230             dropNode.ui.highlight();
33231         }
33232         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
33233     },
33234     
33235     getTree : function(){
33236         return this.tree;
33237     },
33238     
33239     removeDropIndicators : function(n){
33240         if(n && n.ddel){
33241             var el = n.ddel;
33242             Roo.fly(el).removeClass([
33243                     "x-tree-drag-insert-above",
33244                     "x-tree-drag-insert-below",
33245                     "x-tree-drag-append"]);
33246             this.lastInsertClass = "_noclass";
33247         }
33248     },
33249     
33250     beforeDragDrop : function(target, e, id){
33251         this.cancelExpand();
33252         return true;
33253     },
33254     
33255     afterRepair : function(data){
33256         if(data && Roo.enableFx){
33257             data.node.ui.highlight();
33258         }
33259         this.hideProxy();
33260     }    
33261 });
33262
33263 }
33264 /*
33265  * Based on:
33266  * Ext JS Library 1.1.1
33267  * Copyright(c) 2006-2007, Ext JS, LLC.
33268  *
33269  * Originally Released Under LGPL - original licence link has changed is not relivant.
33270  *
33271  * Fork - LGPL
33272  * <script type="text/javascript">
33273  */
33274  
33275
33276 if(Roo.dd.DragZone){
33277 Roo.tree.TreeDragZone = function(tree, config){
33278     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
33279     this.tree = tree;
33280 };
33281
33282 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
33283     ddGroup : "TreeDD",
33284     
33285     onBeforeDrag : function(data, e){
33286         var n = data.node;
33287         return n && n.draggable && !n.disabled;
33288     },
33289     
33290     onInitDrag : function(e){
33291         var data = this.dragData;
33292         this.tree.getSelectionModel().select(data.node);
33293         this.proxy.update("");
33294         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
33295         this.tree.fireEvent("startdrag", this.tree, data.node, e);
33296     },
33297     
33298     getRepairXY : function(e, data){
33299         return data.node.ui.getDDRepairXY();
33300     },
33301     
33302     onEndDrag : function(data, e){
33303         this.tree.fireEvent("enddrag", this.tree, data.node, e);
33304     },
33305     
33306     onValidDrop : function(dd, e, id){
33307         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
33308         this.hideProxy();
33309     },
33310     
33311     beforeInvalidDrop : function(e, id){
33312         // this scrolls the original position back into view
33313         var sm = this.tree.getSelectionModel();
33314         sm.clearSelections();
33315         sm.select(this.dragData.node);
33316     }
33317 });
33318 }/*
33319  * Based on:
33320  * Ext JS Library 1.1.1
33321  * Copyright(c) 2006-2007, Ext JS, LLC.
33322  *
33323  * Originally Released Under LGPL - original licence link has changed is not relivant.
33324  *
33325  * Fork - LGPL
33326  * <script type="text/javascript">
33327  */
33328 /**
33329  * @class Roo.tree.TreeEditor
33330  * @extends Roo.Editor
33331  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
33332  * as the editor field.
33333  * @constructor
33334  * @param {TreePanel} tree
33335  * @param {Object} config Either a prebuilt {@link Roo.form.Field} instance or a Field config object
33336  */
33337 Roo.tree.TreeEditor = function(tree, config){
33338     config = config || {};
33339     var field = config.events ? config : new Roo.form.TextField(config);
33340     Roo.tree.TreeEditor.superclass.constructor.call(this, field);
33341
33342     this.tree = tree;
33343
33344     tree.on('beforeclick', this.beforeNodeClick, this);
33345     tree.getTreeEl().on('mousedown', this.hide, this);
33346     this.on('complete', this.updateNode, this);
33347     this.on('beforestartedit', this.fitToTree, this);
33348     this.on('startedit', this.bindScroll, this, {delay:10});
33349     this.on('specialkey', this.onSpecialKey, this);
33350 };
33351
33352 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
33353     /**
33354      * @cfg {String} alignment
33355      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
33356      */
33357     alignment: "l-l",
33358     // inherit
33359     autoSize: false,
33360     /**
33361      * @cfg {Boolean} hideEl
33362      * True to hide the bound element while the editor is displayed (defaults to false)
33363      */
33364     hideEl : false,
33365     /**
33366      * @cfg {String} cls
33367      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
33368      */
33369     cls: "x-small-editor x-tree-editor",
33370     /**
33371      * @cfg {Boolean} shim
33372      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
33373      */
33374     shim:false,
33375     // inherit
33376     shadow:"frame",
33377     /**
33378      * @cfg {Number} maxWidth
33379      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
33380      * the containing tree element's size, it will be automatically limited for you to the container width, taking
33381      * scroll and client offsets into account prior to each edit.
33382      */
33383     maxWidth: 250,
33384
33385     editDelay : 350,
33386
33387     // private
33388     fitToTree : function(ed, el){
33389         var td = this.tree.getTreeEl().dom, nd = el.dom;
33390         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
33391             td.scrollLeft = nd.offsetLeft;
33392         }
33393         var w = Math.min(
33394                 this.maxWidth,
33395                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
33396         this.setSize(w, '');
33397     },
33398
33399     // private
33400     triggerEdit : function(node){
33401         this.completeEdit();
33402         this.editNode = node;
33403         this.startEdit(node.ui.textNode, node.text);
33404     },
33405
33406     // private
33407     bindScroll : function(){
33408         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
33409     },
33410
33411     // private
33412     beforeNodeClick : function(node, e){
33413         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
33414         this.lastClick = new Date();
33415         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
33416             e.stopEvent();
33417             this.triggerEdit(node);
33418             return false;
33419         }
33420     },
33421
33422     // private
33423     updateNode : function(ed, value){
33424         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
33425         this.editNode.setText(value);
33426     },
33427
33428     // private
33429     onHide : function(){
33430         Roo.tree.TreeEditor.superclass.onHide.call(this);
33431         if(this.editNode){
33432             this.editNode.ui.focus();
33433         }
33434     },
33435
33436     // private
33437     onSpecialKey : function(field, e){
33438         var k = e.getKey();
33439         if(k == e.ESC){
33440             e.stopEvent();
33441             this.cancelEdit();
33442         }else if(k == e.ENTER && !e.hasModifier()){
33443             e.stopEvent();
33444             this.completeEdit();
33445         }
33446     }
33447 });//<Script type="text/javascript">
33448 /*
33449  * Based on:
33450  * Ext JS Library 1.1.1
33451  * Copyright(c) 2006-2007, Ext JS, LLC.
33452  *
33453  * Originally Released Under LGPL - original licence link has changed is not relivant.
33454  *
33455  * Fork - LGPL
33456  * <script type="text/javascript">
33457  */
33458  
33459 /**
33460  * Not documented??? - probably should be...
33461  */
33462
33463 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
33464     //focus: Roo.emptyFn, // prevent odd scrolling behavior
33465     
33466     renderElements : function(n, a, targetNode, bulkRender){
33467         //consel.log("renderElements?");
33468         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33469
33470         var t = n.getOwnerTree();
33471         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
33472         
33473         var cols = t.columns;
33474         var bw = t.borderWidth;
33475         var c = cols[0];
33476         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33477          var cb = typeof a.checked == "boolean";
33478         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33479         var colcls = 'x-t-' + tid + '-c0';
33480         var buf = [
33481             '<li class="x-tree-node">',
33482             
33483                 
33484                 '<div class="x-tree-node-el ', a.cls,'">',
33485                     // extran...
33486                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
33487                 
33488                 
33489                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
33490                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
33491                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
33492                            (a.icon ? ' x-tree-node-inline-icon' : ''),
33493                            (a.iconCls ? ' '+a.iconCls : ''),
33494                            '" unselectable="on" />',
33495                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
33496                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
33497                              
33498                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33499                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
33500                             '<span unselectable="on" qtip="' + tx + '">',
33501                              tx,
33502                              '</span></a>' ,
33503                     '</div>',
33504                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
33505                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
33506                  ];
33507         for(var i = 1, len = cols.length; i < len; i++){
33508             c = cols[i];
33509             colcls = 'x-t-' + tid + '-c' +i;
33510             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
33511             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
33512                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
33513                       "</div>");
33514          }
33515          
33516          buf.push(
33517             '</a>',
33518             '<div class="x-clear"></div></div>',
33519             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33520             "</li>");
33521         
33522         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33523             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33524                                 n.nextSibling.ui.getEl(), buf.join(""));
33525         }else{
33526             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33527         }
33528         var el = this.wrap.firstChild;
33529         this.elRow = el;
33530         this.elNode = el.firstChild;
33531         this.ranchor = el.childNodes[1];
33532         this.ctNode = this.wrap.childNodes[1];
33533         var cs = el.firstChild.childNodes;
33534         this.indentNode = cs[0];
33535         this.ecNode = cs[1];
33536         this.iconNode = cs[2];
33537         var index = 3;
33538         if(cb){
33539             this.checkbox = cs[3];
33540             index++;
33541         }
33542         this.anchor = cs[index];
33543         
33544         this.textNode = cs[index].firstChild;
33545         
33546         //el.on("click", this.onClick, this);
33547         //el.on("dblclick", this.onDblClick, this);
33548         
33549         
33550        // console.log(this);
33551     },
33552     initEvents : function(){
33553         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
33554         
33555             
33556         var a = this.ranchor;
33557
33558         var el = Roo.get(a);
33559
33560         if(Roo.isOpera){ // opera render bug ignores the CSS
33561             el.setStyle("text-decoration", "none");
33562         }
33563
33564         el.on("click", this.onClick, this);
33565         el.on("dblclick", this.onDblClick, this);
33566         el.on("contextmenu", this.onContextMenu, this);
33567         
33568     },
33569     
33570     /*onSelectedChange : function(state){
33571         if(state){
33572             this.focus();
33573             this.addClass("x-tree-selected");
33574         }else{
33575             //this.blur();
33576             this.removeClass("x-tree-selected");
33577         }
33578     },*/
33579     addClass : function(cls){
33580         if(this.elRow){
33581             Roo.fly(this.elRow).addClass(cls);
33582         }
33583         
33584     },
33585     
33586     
33587     removeClass : function(cls){
33588         if(this.elRow){
33589             Roo.fly(this.elRow).removeClass(cls);
33590         }
33591     }
33592
33593     
33594     
33595 });//<Script type="text/javascript">
33596
33597 /*
33598  * Based on:
33599  * Ext JS Library 1.1.1
33600  * Copyright(c) 2006-2007, Ext JS, LLC.
33601  *
33602  * Originally Released Under LGPL - original licence link has changed is not relivant.
33603  *
33604  * Fork - LGPL
33605  * <script type="text/javascript">
33606  */
33607  
33608
33609 /**
33610  * @class Roo.tree.ColumnTree
33611  * @extends Roo.data.TreePanel
33612  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
33613  * @cfg {int} borderWidth  compined right/left border allowance
33614  * @constructor
33615  * @param {String/HTMLElement/Element} el The container element
33616  * @param {Object} config
33617  */
33618 Roo.tree.ColumnTree =  function(el, config)
33619 {
33620    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
33621    this.addEvents({
33622         /**
33623         * @event resize
33624         * Fire this event on a container when it resizes
33625         * @param {int} w Width
33626         * @param {int} h Height
33627         */
33628        "resize" : true
33629     });
33630     this.on('resize', this.onResize, this);
33631 };
33632
33633 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
33634     //lines:false,
33635     
33636     
33637     borderWidth: Roo.isBorderBox ? 0 : 2, 
33638     headEls : false,
33639     
33640     render : function(){
33641         // add the header.....
33642        
33643         Roo.tree.ColumnTree.superclass.render.apply(this);
33644         
33645         this.el.addClass('x-column-tree');
33646         
33647         this.headers = this.el.createChild(
33648             {cls:'x-tree-headers'},this.innerCt.dom);
33649    
33650         var cols = this.columns, c;
33651         var totalWidth = 0;
33652         this.headEls = [];
33653         var  len = cols.length;
33654         for(var i = 0; i < len; i++){
33655              c = cols[i];
33656              totalWidth += c.width;
33657             this.headEls.push(this.headers.createChild({
33658                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
33659                  cn: {
33660                      cls:'x-tree-hd-text',
33661                      html: c.header
33662                  },
33663                  style:'width:'+(c.width-this.borderWidth)+'px;'
33664              }));
33665         }
33666         this.headers.createChild({cls:'x-clear'});
33667         // prevent floats from wrapping when clipped
33668         this.headers.setWidth(totalWidth);
33669         //this.innerCt.setWidth(totalWidth);
33670         this.innerCt.setStyle({ overflow: 'auto' });
33671         this.onResize(this.width, this.height);
33672              
33673         
33674     },
33675     onResize : function(w,h)
33676     {
33677         this.height = h;
33678         this.width = w;
33679         // resize cols..
33680         this.innerCt.setWidth(this.width);
33681         this.innerCt.setHeight(this.height-20);
33682         
33683         // headers...
33684         var cols = this.columns, c;
33685         var totalWidth = 0;
33686         var expEl = false;
33687         var len = cols.length;
33688         for(var i = 0; i < len; i++){
33689             c = cols[i];
33690             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
33691                 // it's the expander..
33692                 expEl  = this.headEls[i];
33693                 continue;
33694             }
33695             totalWidth += c.width;
33696             
33697         }
33698         if (expEl) {
33699             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
33700         }
33701         this.headers.setWidth(w-20);
33702
33703         
33704         
33705         
33706     }
33707 });
33708 /*
33709  * Based on:
33710  * Ext JS Library 1.1.1
33711  * Copyright(c) 2006-2007, Ext JS, LLC.
33712  *
33713  * Originally Released Under LGPL - original licence link has changed is not relivant.
33714  *
33715  * Fork - LGPL
33716  * <script type="text/javascript">
33717  */
33718  
33719 /**
33720  * @class Roo.menu.Menu
33721  * @extends Roo.util.Observable
33722  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
33723  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
33724  * @constructor
33725  * Creates a new Menu
33726  * @param {Object} config Configuration options
33727  */
33728 Roo.menu.Menu = function(config){
33729     Roo.apply(this, config);
33730     this.id = this.id || Roo.id();
33731     this.addEvents({
33732         /**
33733          * @event beforeshow
33734          * Fires before this menu is displayed
33735          * @param {Roo.menu.Menu} this
33736          */
33737         beforeshow : true,
33738         /**
33739          * @event beforehide
33740          * Fires before this menu is hidden
33741          * @param {Roo.menu.Menu} this
33742          */
33743         beforehide : true,
33744         /**
33745          * @event show
33746          * Fires after this menu is displayed
33747          * @param {Roo.menu.Menu} this
33748          */
33749         show : true,
33750         /**
33751          * @event hide
33752          * Fires after this menu is hidden
33753          * @param {Roo.menu.Menu} this
33754          */
33755         hide : true,
33756         /**
33757          * @event click
33758          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
33759          * @param {Roo.menu.Menu} this
33760          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33761          * @param {Roo.EventObject} e
33762          */
33763         click : true,
33764         /**
33765          * @event mouseover
33766          * Fires when the mouse is hovering over this menu
33767          * @param {Roo.menu.Menu} this
33768          * @param {Roo.EventObject} e
33769          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33770          */
33771         mouseover : true,
33772         /**
33773          * @event mouseout
33774          * Fires when the mouse exits this menu
33775          * @param {Roo.menu.Menu} this
33776          * @param {Roo.EventObject} e
33777          * @param {Roo.menu.Item} menuItem The menu item that was clicked
33778          */
33779         mouseout : true,
33780         /**
33781          * @event itemclick
33782          * Fires when a menu item contained in this menu is clicked
33783          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
33784          * @param {Roo.EventObject} e
33785          */
33786         itemclick: true
33787     });
33788     if (this.registerMenu) {
33789         Roo.menu.MenuMgr.register(this);
33790     }
33791     
33792     var mis = this.items;
33793     this.items = new Roo.util.MixedCollection();
33794     if(mis){
33795         this.add.apply(this, mis);
33796     }
33797 };
33798
33799 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
33800     /**
33801      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
33802      */
33803     minWidth : 120,
33804     /**
33805      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
33806      * for bottom-right shadow (defaults to "sides")
33807      */
33808     shadow : "sides",
33809     /**
33810      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
33811      * this menu (defaults to "tl-tr?")
33812      */
33813     subMenuAlign : "tl-tr?",
33814     /**
33815      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
33816      * relative to its element of origin (defaults to "tl-bl?")
33817      */
33818     defaultAlign : "tl-bl?",
33819     /**
33820      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
33821      */
33822     allowOtherMenus : false,
33823     /**
33824      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
33825      */
33826     registerMenu : true,
33827
33828     hidden:true,
33829
33830     // private
33831     render : function(){
33832         if(this.el){
33833             return;
33834         }
33835         var el = this.el = new Roo.Layer({
33836             cls: "x-menu",
33837             shadow:this.shadow,
33838             constrain: false,
33839             parentEl: this.parentEl || document.body,
33840             zindex:15000
33841         });
33842
33843         this.keyNav = new Roo.menu.MenuNav(this);
33844
33845         if(this.plain){
33846             el.addClass("x-menu-plain");
33847         }
33848         if(this.cls){
33849             el.addClass(this.cls);
33850         }
33851         // generic focus element
33852         this.focusEl = el.createChild({
33853             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
33854         });
33855         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
33856         ul.on("click", this.onClick, this);
33857         ul.on("mouseover", this.onMouseOver, this);
33858         ul.on("mouseout", this.onMouseOut, this);
33859         this.items.each(function(item){
33860             var li = document.createElement("li");
33861             li.className = "x-menu-list-item";
33862             ul.dom.appendChild(li);
33863             item.render(li, this);
33864         }, this);
33865         this.ul = ul;
33866         this.autoWidth();
33867     },
33868
33869     // private
33870     autoWidth : function(){
33871         var el = this.el, ul = this.ul;
33872         if(!el){
33873             return;
33874         }
33875         var w = this.width;
33876         if(w){
33877             el.setWidth(w);
33878         }else if(Roo.isIE){
33879             el.setWidth(this.minWidth);
33880             var t = el.dom.offsetWidth; // force recalc
33881             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
33882         }
33883     },
33884
33885     // private
33886     delayAutoWidth : function(){
33887         if(this.rendered){
33888             if(!this.awTask){
33889                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
33890             }
33891             this.awTask.delay(20);
33892         }
33893     },
33894
33895     // private
33896     findTargetItem : function(e){
33897         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
33898         if(t && t.menuItemId){
33899             return this.items.get(t.menuItemId);
33900         }
33901     },
33902
33903     // private
33904     onClick : function(e){
33905         var t;
33906         if(t = this.findTargetItem(e)){
33907             t.onClick(e);
33908             this.fireEvent("click", this, t, e);
33909         }
33910     },
33911
33912     // private
33913     setActiveItem : function(item, autoExpand){
33914         if(item != this.activeItem){
33915             if(this.activeItem){
33916                 this.activeItem.deactivate();
33917             }
33918             this.activeItem = item;
33919             item.activate(autoExpand);
33920         }else if(autoExpand){
33921             item.expandMenu();
33922         }
33923     },
33924
33925     // private
33926     tryActivate : function(start, step){
33927         var items = this.items;
33928         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
33929             var item = items.get(i);
33930             if(!item.disabled && item.canActivate){
33931                 this.setActiveItem(item, false);
33932                 return item;
33933             }
33934         }
33935         return false;
33936     },
33937
33938     // private
33939     onMouseOver : function(e){
33940         var t;
33941         if(t = this.findTargetItem(e)){
33942             if(t.canActivate && !t.disabled){
33943                 this.setActiveItem(t, true);
33944             }
33945         }
33946         this.fireEvent("mouseover", this, e, t);
33947     },
33948
33949     // private
33950     onMouseOut : function(e){
33951         var t;
33952         if(t = this.findTargetItem(e)){
33953             if(t == this.activeItem && t.shouldDeactivate(e)){
33954                 this.activeItem.deactivate();
33955                 delete this.activeItem;
33956             }
33957         }
33958         this.fireEvent("mouseout", this, e, t);
33959     },
33960
33961     /**
33962      * Read-only.  Returns true if the menu is currently displayed, else false.
33963      * @type Boolean
33964      */
33965     isVisible : function(){
33966         return this.el && !this.hidden;
33967     },
33968
33969     /**
33970      * Displays this menu relative to another element
33971      * @param {String/HTMLElement/Roo.Element} element The element to align to
33972      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
33973      * the element (defaults to this.defaultAlign)
33974      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33975      */
33976     show : function(el, pos, parentMenu){
33977         this.parentMenu = parentMenu;
33978         if(!this.el){
33979             this.render();
33980         }
33981         this.fireEvent("beforeshow", this);
33982         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
33983     },
33984
33985     /**
33986      * Displays this menu at a specific xy position
33987      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
33988      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
33989      */
33990     showAt : function(xy, parentMenu, /* private: */_e){
33991         this.parentMenu = parentMenu;
33992         if(!this.el){
33993             this.render();
33994         }
33995         if(_e !== false){
33996             this.fireEvent("beforeshow", this);
33997             xy = this.el.adjustForConstraints(xy);
33998         }
33999         this.el.setXY(xy);
34000         this.el.show();
34001         this.hidden = false;
34002         this.focus();
34003         this.fireEvent("show", this);
34004     },
34005
34006     focus : function(){
34007         if(!this.hidden){
34008             this.doFocus.defer(50, this);
34009         }
34010     },
34011
34012     doFocus : function(){
34013         if(!this.hidden){
34014             this.focusEl.focus();
34015         }
34016     },
34017
34018     /**
34019      * Hides this menu and optionally all parent menus
34020      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
34021      */
34022     hide : function(deep){
34023         if(this.el && this.isVisible()){
34024             this.fireEvent("beforehide", this);
34025             if(this.activeItem){
34026                 this.activeItem.deactivate();
34027                 this.activeItem = null;
34028             }
34029             this.el.hide();
34030             this.hidden = true;
34031             this.fireEvent("hide", this);
34032         }
34033         if(deep === true && this.parentMenu){
34034             this.parentMenu.hide(true);
34035         }
34036     },
34037
34038     /**
34039      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
34040      * Any of the following are valid:
34041      * <ul>
34042      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
34043      * <li>An HTMLElement object which will be converted to a menu item</li>
34044      * <li>A menu item config object that will be created as a new menu item</li>
34045      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
34046      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
34047      * </ul>
34048      * Usage:
34049      * <pre><code>
34050 // Create the menu
34051 var menu = new Roo.menu.Menu();
34052
34053 // Create a menu item to add by reference
34054 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
34055
34056 // Add a bunch of items at once using different methods.
34057 // Only the last item added will be returned.
34058 var item = menu.add(
34059     menuItem,                // add existing item by ref
34060     'Dynamic Item',          // new TextItem
34061     '-',                     // new separator
34062     { text: 'Config Item' }  // new item by config
34063 );
34064 </code></pre>
34065      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
34066      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
34067      */
34068     add : function(){
34069         var a = arguments, l = a.length, item;
34070         for(var i = 0; i < l; i++){
34071             var el = a[i];
34072             if ((typeof(el) == "object") && el.xtype && el.xns) {
34073                 el = Roo.factory(el, Roo.menu);
34074             }
34075             
34076             if(el.render){ // some kind of Item
34077                 item = this.addItem(el);
34078             }else if(typeof el == "string"){ // string
34079                 if(el == "separator" || el == "-"){
34080                     item = this.addSeparator();
34081                 }else{
34082                     item = this.addText(el);
34083                 }
34084             }else if(el.tagName || el.el){ // element
34085                 item = this.addElement(el);
34086             }else if(typeof el == "object"){ // must be menu item config?
34087                 item = this.addMenuItem(el);
34088             }
34089         }
34090         return item;
34091     },
34092
34093     /**
34094      * Returns this menu's underlying {@link Roo.Element} object
34095      * @return {Roo.Element} The element
34096      */
34097     getEl : function(){
34098         if(!this.el){
34099             this.render();
34100         }
34101         return this.el;
34102     },
34103
34104     /**
34105      * Adds a separator bar to the menu
34106      * @return {Roo.menu.Item} The menu item that was added
34107      */
34108     addSeparator : function(){
34109         return this.addItem(new Roo.menu.Separator());
34110     },
34111
34112     /**
34113      * Adds an {@link Roo.Element} object to the menu
34114      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
34115      * @return {Roo.menu.Item} The menu item that was added
34116      */
34117     addElement : function(el){
34118         return this.addItem(new Roo.menu.BaseItem(el));
34119     },
34120
34121     /**
34122      * Adds an existing object based on {@link Roo.menu.Item} to the menu
34123      * @param {Roo.menu.Item} item The menu item to add
34124      * @return {Roo.menu.Item} The menu item that was added
34125      */
34126     addItem : function(item){
34127         this.items.add(item);
34128         if(this.ul){
34129             var li = document.createElement("li");
34130             li.className = "x-menu-list-item";
34131             this.ul.dom.appendChild(li);
34132             item.render(li, this);
34133             this.delayAutoWidth();
34134         }
34135         return item;
34136     },
34137
34138     /**
34139      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
34140      * @param {Object} config A MenuItem config object
34141      * @return {Roo.menu.Item} The menu item that was added
34142      */
34143     addMenuItem : function(config){
34144         if(!(config instanceof Roo.menu.Item)){
34145             if(typeof config.checked == "boolean"){ // must be check menu item config?
34146                 config = new Roo.menu.CheckItem(config);
34147             }else{
34148                 config = new Roo.menu.Item(config);
34149             }
34150         }
34151         return this.addItem(config);
34152     },
34153
34154     /**
34155      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
34156      * @param {String} text The text to display in the menu item
34157      * @return {Roo.menu.Item} The menu item that was added
34158      */
34159     addText : function(text){
34160         return this.addItem(new Roo.menu.TextItem({ text : text }));
34161     },
34162
34163     /**
34164      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
34165      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
34166      * @param {Roo.menu.Item} item The menu item to add
34167      * @return {Roo.menu.Item} The menu item that was added
34168      */
34169     insert : function(index, item){
34170         this.items.insert(index, item);
34171         if(this.ul){
34172             var li = document.createElement("li");
34173             li.className = "x-menu-list-item";
34174             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
34175             item.render(li, this);
34176             this.delayAutoWidth();
34177         }
34178         return item;
34179     },
34180
34181     /**
34182      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
34183      * @param {Roo.menu.Item} item The menu item to remove
34184      */
34185     remove : function(item){
34186         this.items.removeKey(item.id);
34187         item.destroy();
34188     },
34189
34190     /**
34191      * Removes and destroys all items in the menu
34192      */
34193     removeAll : function(){
34194         var f;
34195         while(f = this.items.first()){
34196             this.remove(f);
34197         }
34198     }
34199 });
34200
34201 // MenuNav is a private utility class used internally by the Menu
34202 Roo.menu.MenuNav = function(menu){
34203     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
34204     this.scope = this.menu = menu;
34205 };
34206
34207 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
34208     doRelay : function(e, h){
34209         var k = e.getKey();
34210         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
34211             this.menu.tryActivate(0, 1);
34212             return false;
34213         }
34214         return h.call(this.scope || this, e, this.menu);
34215     },
34216
34217     up : function(e, m){
34218         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
34219             m.tryActivate(m.items.length-1, -1);
34220         }
34221     },
34222
34223     down : function(e, m){
34224         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
34225             m.tryActivate(0, 1);
34226         }
34227     },
34228
34229     right : function(e, m){
34230         if(m.activeItem){
34231             m.activeItem.expandMenu(true);
34232         }
34233     },
34234
34235     left : function(e, m){
34236         m.hide();
34237         if(m.parentMenu && m.parentMenu.activeItem){
34238             m.parentMenu.activeItem.activate();
34239         }
34240     },
34241
34242     enter : function(e, m){
34243         if(m.activeItem){
34244             e.stopPropagation();
34245             m.activeItem.onClick(e);
34246             m.fireEvent("click", this, m.activeItem);
34247             return true;
34248         }
34249     }
34250 });/*
34251  * Based on:
34252  * Ext JS Library 1.1.1
34253  * Copyright(c) 2006-2007, Ext JS, LLC.
34254  *
34255  * Originally Released Under LGPL - original licence link has changed is not relivant.
34256  *
34257  * Fork - LGPL
34258  * <script type="text/javascript">
34259  */
34260  
34261 /**
34262  * @class Roo.menu.MenuMgr
34263  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
34264  * @singleton
34265  */
34266 Roo.menu.MenuMgr = function(){
34267    var menus, active, groups = {}, attached = false, lastShow = new Date();
34268
34269    // private - called when first menu is created
34270    function init(){
34271        menus = {};
34272        active = new Roo.util.MixedCollection();
34273        Roo.get(document).addKeyListener(27, function(){
34274            if(active.length > 0){
34275                hideAll();
34276            }
34277        });
34278    }
34279
34280    // private
34281    function hideAll(){
34282        if(active && active.length > 0){
34283            var c = active.clone();
34284            c.each(function(m){
34285                m.hide();
34286            });
34287        }
34288    }
34289
34290    // private
34291    function onHide(m){
34292        active.remove(m);
34293        if(active.length < 1){
34294            Roo.get(document).un("mousedown", onMouseDown);
34295            attached = false;
34296        }
34297    }
34298
34299    // private
34300    function onShow(m){
34301        var last = active.last();
34302        lastShow = new Date();
34303        active.add(m);
34304        if(!attached){
34305            Roo.get(document).on("mousedown", onMouseDown);
34306            attached = true;
34307        }
34308        if(m.parentMenu){
34309           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
34310           m.parentMenu.activeChild = m;
34311        }else if(last && last.isVisible()){
34312           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
34313        }
34314    }
34315
34316    // private
34317    function onBeforeHide(m){
34318        if(m.activeChild){
34319            m.activeChild.hide();
34320        }
34321        if(m.autoHideTimer){
34322            clearTimeout(m.autoHideTimer);
34323            delete m.autoHideTimer;
34324        }
34325    }
34326
34327    // private
34328    function onBeforeShow(m){
34329        var pm = m.parentMenu;
34330        if(!pm && !m.allowOtherMenus){
34331            hideAll();
34332        }else if(pm && pm.activeChild && active != m){
34333            pm.activeChild.hide();
34334        }
34335    }
34336
34337    // private
34338    function onMouseDown(e){
34339        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
34340            hideAll();
34341        }
34342    }
34343
34344    // private
34345    function onBeforeCheck(mi, state){
34346        if(state){
34347            var g = groups[mi.group];
34348            for(var i = 0, l = g.length; i < l; i++){
34349                if(g[i] != mi){
34350                    g[i].setChecked(false);
34351                }
34352            }
34353        }
34354    }
34355
34356    return {
34357
34358        /**
34359         * Hides all menus that are currently visible
34360         */
34361        hideAll : function(){
34362             hideAll();  
34363        },
34364
34365        // private
34366        register : function(menu){
34367            if(!menus){
34368                init();
34369            }
34370            menus[menu.id] = menu;
34371            menu.on("beforehide", onBeforeHide);
34372            menu.on("hide", onHide);
34373            menu.on("beforeshow", onBeforeShow);
34374            menu.on("show", onShow);
34375            var g = menu.group;
34376            if(g && menu.events["checkchange"]){
34377                if(!groups[g]){
34378                    groups[g] = [];
34379                }
34380                groups[g].push(menu);
34381                menu.on("checkchange", onCheck);
34382            }
34383        },
34384
34385         /**
34386          * Returns a {@link Roo.menu.Menu} object
34387          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
34388          * be used to generate and return a new Menu instance.
34389          */
34390        get : function(menu){
34391            if(typeof menu == "string"){ // menu id
34392                return menus[menu];
34393            }else if(menu.events){  // menu instance
34394                return menu;
34395            }else if(typeof menu.length == 'number'){ // array of menu items?
34396                return new Roo.menu.Menu({items:menu});
34397            }else{ // otherwise, must be a config
34398                return new Roo.menu.Menu(menu);
34399            }
34400        },
34401
34402        // private
34403        unregister : function(menu){
34404            delete menus[menu.id];
34405            menu.un("beforehide", onBeforeHide);
34406            menu.un("hide", onHide);
34407            menu.un("beforeshow", onBeforeShow);
34408            menu.un("show", onShow);
34409            var g = menu.group;
34410            if(g && menu.events["checkchange"]){
34411                groups[g].remove(menu);
34412                menu.un("checkchange", onCheck);
34413            }
34414        },
34415
34416        // private
34417        registerCheckable : function(menuItem){
34418            var g = menuItem.group;
34419            if(g){
34420                if(!groups[g]){
34421                    groups[g] = [];
34422                }
34423                groups[g].push(menuItem);
34424                menuItem.on("beforecheckchange", onBeforeCheck);
34425            }
34426        },
34427
34428        // private
34429        unregisterCheckable : function(menuItem){
34430            var g = menuItem.group;
34431            if(g){
34432                groups[g].remove(menuItem);
34433                menuItem.un("beforecheckchange", onBeforeCheck);
34434            }
34435        }
34436    };
34437 }();/*
34438  * Based on:
34439  * Ext JS Library 1.1.1
34440  * Copyright(c) 2006-2007, Ext JS, LLC.
34441  *
34442  * Originally Released Under LGPL - original licence link has changed is not relivant.
34443  *
34444  * Fork - LGPL
34445  * <script type="text/javascript">
34446  */
34447  
34448
34449 /**
34450  * @class Roo.menu.BaseItem
34451  * @extends Roo.Component
34452  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
34453  * management and base configuration options shared by all menu components.
34454  * @constructor
34455  * Creates a new BaseItem
34456  * @param {Object} config Configuration options
34457  */
34458 Roo.menu.BaseItem = function(config){
34459     Roo.menu.BaseItem.superclass.constructor.call(this, config);
34460
34461     this.addEvents({
34462         /**
34463          * @event click
34464          * Fires when this item is clicked
34465          * @param {Roo.menu.BaseItem} this
34466          * @param {Roo.EventObject} e
34467          */
34468         click: true,
34469         /**
34470          * @event activate
34471          * Fires when this item is activated
34472          * @param {Roo.menu.BaseItem} this
34473          */
34474         activate : true,
34475         /**
34476          * @event deactivate
34477          * Fires when this item is deactivated
34478          * @param {Roo.menu.BaseItem} this
34479          */
34480         deactivate : true
34481     });
34482
34483     if(this.handler){
34484         this.on("click", this.handler, this.scope, true);
34485     }
34486 };
34487
34488 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
34489     /**
34490      * @cfg {Function} handler
34491      * A function that will handle the click event of this menu item (defaults to undefined)
34492      */
34493     /**
34494      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
34495      */
34496     canActivate : false,
34497     /**
34498      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
34499      */
34500     activeClass : "x-menu-item-active",
34501     /**
34502      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
34503      */
34504     hideOnClick : true,
34505     /**
34506      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
34507      */
34508     hideDelay : 100,
34509
34510     // private
34511     ctype: "Roo.menu.BaseItem",
34512
34513     // private
34514     actionMode : "container",
34515
34516     // private
34517     render : function(container, parentMenu){
34518         this.parentMenu = parentMenu;
34519         Roo.menu.BaseItem.superclass.render.call(this, container);
34520         this.container.menuItemId = this.id;
34521     },
34522
34523     // private
34524     onRender : function(container, position){
34525         this.el = Roo.get(this.el);
34526         container.dom.appendChild(this.el.dom);
34527     },
34528
34529     // private
34530     onClick : function(e){
34531         if(!this.disabled && this.fireEvent("click", this, e) !== false
34532                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
34533             this.handleClick(e);
34534         }else{
34535             e.stopEvent();
34536         }
34537     },
34538
34539     // private
34540     activate : function(){
34541         if(this.disabled){
34542             return false;
34543         }
34544         var li = this.container;
34545         li.addClass(this.activeClass);
34546         this.region = li.getRegion().adjust(2, 2, -2, -2);
34547         this.fireEvent("activate", this);
34548         return true;
34549     },
34550
34551     // private
34552     deactivate : function(){
34553         this.container.removeClass(this.activeClass);
34554         this.fireEvent("deactivate", this);
34555     },
34556
34557     // private
34558     shouldDeactivate : function(e){
34559         return !this.region || !this.region.contains(e.getPoint());
34560     },
34561
34562     // private
34563     handleClick : function(e){
34564         if(this.hideOnClick){
34565             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
34566         }
34567     },
34568
34569     // private
34570     expandMenu : function(autoActivate){
34571         // do nothing
34572     },
34573
34574     // private
34575     hideMenu : function(){
34576         // do nothing
34577     }
34578 });/*
34579  * Based on:
34580  * Ext JS Library 1.1.1
34581  * Copyright(c) 2006-2007, Ext JS, LLC.
34582  *
34583  * Originally Released Under LGPL - original licence link has changed is not relivant.
34584  *
34585  * Fork - LGPL
34586  * <script type="text/javascript">
34587  */
34588  
34589 /**
34590  * @class Roo.menu.Adapter
34591  * @extends Roo.menu.BaseItem
34592  * 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.
34593  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
34594  * @constructor
34595  * Creates a new Adapter
34596  * @param {Object} config Configuration options
34597  */
34598 Roo.menu.Adapter = function(component, config){
34599     Roo.menu.Adapter.superclass.constructor.call(this, config);
34600     this.component = component;
34601 };
34602 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
34603     // private
34604     canActivate : true,
34605
34606     // private
34607     onRender : function(container, position){
34608         this.component.render(container);
34609         this.el = this.component.getEl();
34610     },
34611
34612     // private
34613     activate : function(){
34614         if(this.disabled){
34615             return false;
34616         }
34617         this.component.focus();
34618         this.fireEvent("activate", this);
34619         return true;
34620     },
34621
34622     // private
34623     deactivate : function(){
34624         this.fireEvent("deactivate", this);
34625     },
34626
34627     // private
34628     disable : function(){
34629         this.component.disable();
34630         Roo.menu.Adapter.superclass.disable.call(this);
34631     },
34632
34633     // private
34634     enable : function(){
34635         this.component.enable();
34636         Roo.menu.Adapter.superclass.enable.call(this);
34637     }
34638 });/*
34639  * Based on:
34640  * Ext JS Library 1.1.1
34641  * Copyright(c) 2006-2007, Ext JS, LLC.
34642  *
34643  * Originally Released Under LGPL - original licence link has changed is not relivant.
34644  *
34645  * Fork - LGPL
34646  * <script type="text/javascript">
34647  */
34648
34649 /**
34650  * @class Roo.menu.TextItem
34651  * @extends Roo.menu.BaseItem
34652  * Adds a static text string to a menu, usually used as either a heading or group separator.
34653  * Note: old style constructor with text is still supported.
34654  * 
34655  * @constructor
34656  * Creates a new TextItem
34657  * @param {Object} cfg Configuration
34658  */
34659 Roo.menu.TextItem = function(cfg){
34660     if (typeof(cfg) == 'string') {
34661         this.text = cfg;
34662     } else {
34663         Roo.apply(this,cfg);
34664     }
34665     
34666     Roo.menu.TextItem.superclass.constructor.call(this);
34667 };
34668
34669 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
34670     /**
34671      * @cfg {Boolean} text Text to show on item.
34672      */
34673     text : '',
34674     
34675     /**
34676      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34677      */
34678     hideOnClick : false,
34679     /**
34680      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
34681      */
34682     itemCls : "x-menu-text",
34683
34684     // private
34685     onRender : function(){
34686         var s = document.createElement("span");
34687         s.className = this.itemCls;
34688         s.innerHTML = this.text;
34689         this.el = s;
34690         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
34691     }
34692 });/*
34693  * Based on:
34694  * Ext JS Library 1.1.1
34695  * Copyright(c) 2006-2007, Ext JS, LLC.
34696  *
34697  * Originally Released Under LGPL - original licence link has changed is not relivant.
34698  *
34699  * Fork - LGPL
34700  * <script type="text/javascript">
34701  */
34702
34703 /**
34704  * @class Roo.menu.Separator
34705  * @extends Roo.menu.BaseItem
34706  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
34707  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
34708  * @constructor
34709  * @param {Object} config Configuration options
34710  */
34711 Roo.menu.Separator = function(config){
34712     Roo.menu.Separator.superclass.constructor.call(this, config);
34713 };
34714
34715 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
34716     /**
34717      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
34718      */
34719     itemCls : "x-menu-sep",
34720     /**
34721      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
34722      */
34723     hideOnClick : false,
34724
34725     // private
34726     onRender : function(li){
34727         var s = document.createElement("span");
34728         s.className = this.itemCls;
34729         s.innerHTML = "&#160;";
34730         this.el = s;
34731         li.addClass("x-menu-sep-li");
34732         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
34733     }
34734 });/*
34735  * Based on:
34736  * Ext JS Library 1.1.1
34737  * Copyright(c) 2006-2007, Ext JS, LLC.
34738  *
34739  * Originally Released Under LGPL - original licence link has changed is not relivant.
34740  *
34741  * Fork - LGPL
34742  * <script type="text/javascript">
34743  */
34744 /**
34745  * @class Roo.menu.Item
34746  * @extends Roo.menu.BaseItem
34747  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
34748  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
34749  * activation and click handling.
34750  * @constructor
34751  * Creates a new Item
34752  * @param {Object} config Configuration options
34753  */
34754 Roo.menu.Item = function(config){
34755     Roo.menu.Item.superclass.constructor.call(this, config);
34756     if(this.menu){
34757         this.menu = Roo.menu.MenuMgr.get(this.menu);
34758     }
34759 };
34760 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
34761     
34762     /**
34763      * @cfg {String} text
34764      * The text to show on the menu item.
34765      */
34766     text: '',
34767      /**
34768      * @cfg {String} HTML to render in menu
34769      * The text to show on the menu item (HTML version).
34770      */
34771     html: '',
34772     /**
34773      * @cfg {String} icon
34774      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
34775      */
34776     icon: undefined,
34777     /**
34778      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
34779      */
34780     itemCls : "x-menu-item",
34781     /**
34782      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
34783      */
34784     canActivate : true,
34785     /**
34786      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
34787      */
34788     showDelay: 200,
34789     // doc'd in BaseItem
34790     hideDelay: 200,
34791
34792     // private
34793     ctype: "Roo.menu.Item",
34794     
34795     // private
34796     onRender : function(container, position){
34797         var el = document.createElement("a");
34798         el.hideFocus = true;
34799         el.unselectable = "on";
34800         el.href = this.href || "#";
34801         if(this.hrefTarget){
34802             el.target = this.hrefTarget;
34803         }
34804         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
34805         
34806         var html = this.html.length ? this.html  : String.format('{0}',this.text);
34807         
34808         el.innerHTML = String.format(
34809                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
34810                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
34811         this.el = el;
34812         Roo.menu.Item.superclass.onRender.call(this, container, position);
34813     },
34814
34815     /**
34816      * Sets the text to display in this menu item
34817      * @param {String} text The text to display
34818      * @param {Boolean} isHTML true to indicate text is pure html.
34819      */
34820     setText : function(text, isHTML){
34821         if (isHTML) {
34822             this.html = text;
34823         } else {
34824             this.text = text;
34825             this.html = '';
34826         }
34827         if(this.rendered){
34828             var html = this.html.length ? this.html  : String.format('{0}',this.text);
34829      
34830             this.el.update(String.format(
34831                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
34832                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
34833             this.parentMenu.autoWidth();
34834         }
34835     },
34836
34837     // private
34838     handleClick : function(e){
34839         if(!this.href){ // if no link defined, stop the event automatically
34840             e.stopEvent();
34841         }
34842         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
34843     },
34844
34845     // private
34846     activate : function(autoExpand){
34847         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
34848             this.focus();
34849             if(autoExpand){
34850                 this.expandMenu();
34851             }
34852         }
34853         return true;
34854     },
34855
34856     // private
34857     shouldDeactivate : function(e){
34858         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
34859             if(this.menu && this.menu.isVisible()){
34860                 return !this.menu.getEl().getRegion().contains(e.getPoint());
34861             }
34862             return true;
34863         }
34864         return false;
34865     },
34866
34867     // private
34868     deactivate : function(){
34869         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
34870         this.hideMenu();
34871     },
34872
34873     // private
34874     expandMenu : function(autoActivate){
34875         if(!this.disabled && this.menu){
34876             clearTimeout(this.hideTimer);
34877             delete this.hideTimer;
34878             if(!this.menu.isVisible() && !this.showTimer){
34879                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
34880             }else if (this.menu.isVisible() && autoActivate){
34881                 this.menu.tryActivate(0, 1);
34882             }
34883         }
34884     },
34885
34886     // private
34887     deferExpand : function(autoActivate){
34888         delete this.showTimer;
34889         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
34890         if(autoActivate){
34891             this.menu.tryActivate(0, 1);
34892         }
34893     },
34894
34895     // private
34896     hideMenu : function(){
34897         clearTimeout(this.showTimer);
34898         delete this.showTimer;
34899         if(!this.hideTimer && this.menu && this.menu.isVisible()){
34900             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
34901         }
34902     },
34903
34904     // private
34905     deferHide : function(){
34906         delete this.hideTimer;
34907         this.menu.hide();
34908     }
34909 });/*
34910  * Based on:
34911  * Ext JS Library 1.1.1
34912  * Copyright(c) 2006-2007, Ext JS, LLC.
34913  *
34914  * Originally Released Under LGPL - original licence link has changed is not relivant.
34915  *
34916  * Fork - LGPL
34917  * <script type="text/javascript">
34918  */
34919  
34920 /**
34921  * @class Roo.menu.CheckItem
34922  * @extends Roo.menu.Item
34923  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
34924  * @constructor
34925  * Creates a new CheckItem
34926  * @param {Object} config Configuration options
34927  */
34928 Roo.menu.CheckItem = function(config){
34929     Roo.menu.CheckItem.superclass.constructor.call(this, config);
34930     this.addEvents({
34931         /**
34932          * @event beforecheckchange
34933          * Fires before the checked value is set, providing an opportunity to cancel if needed
34934          * @param {Roo.menu.CheckItem} this
34935          * @param {Boolean} checked The new checked value that will be set
34936          */
34937         "beforecheckchange" : true,
34938         /**
34939          * @event checkchange
34940          * Fires after the checked value has been set
34941          * @param {Roo.menu.CheckItem} this
34942          * @param {Boolean} checked The checked value that was set
34943          */
34944         "checkchange" : true
34945     });
34946     if(this.checkHandler){
34947         this.on('checkchange', this.checkHandler, this.scope);
34948     }
34949 };
34950 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
34951     /**
34952      * @cfg {String} group
34953      * All check items with the same group name will automatically be grouped into a single-select
34954      * radio button group (defaults to '')
34955      */
34956     /**
34957      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
34958      */
34959     itemCls : "x-menu-item x-menu-check-item",
34960     /**
34961      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
34962      */
34963     groupClass : "x-menu-group-item",
34964
34965     /**
34966      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
34967      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
34968      * initialized with checked = true will be rendered as checked.
34969      */
34970     checked: false,
34971
34972     // private
34973     ctype: "Roo.menu.CheckItem",
34974
34975     // private
34976     onRender : function(c){
34977         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
34978         if(this.group){
34979             this.el.addClass(this.groupClass);
34980         }
34981         Roo.menu.MenuMgr.registerCheckable(this);
34982         if(this.checked){
34983             this.checked = false;
34984             this.setChecked(true, true);
34985         }
34986     },
34987
34988     // private
34989     destroy : function(){
34990         if(this.rendered){
34991             Roo.menu.MenuMgr.unregisterCheckable(this);
34992         }
34993         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
34994     },
34995
34996     /**
34997      * Set the checked state of this item
34998      * @param {Boolean} checked The new checked value
34999      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
35000      */
35001     setChecked : function(state, suppressEvent){
35002         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
35003             if(this.container){
35004                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
35005             }
35006             this.checked = state;
35007             if(suppressEvent !== true){
35008                 this.fireEvent("checkchange", this, state);
35009             }
35010         }
35011     },
35012
35013     // private
35014     handleClick : function(e){
35015        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
35016            this.setChecked(!this.checked);
35017        }
35018        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
35019     }
35020 });/*
35021  * Based on:
35022  * Ext JS Library 1.1.1
35023  * Copyright(c) 2006-2007, Ext JS, LLC.
35024  *
35025  * Originally Released Under LGPL - original licence link has changed is not relivant.
35026  *
35027  * Fork - LGPL
35028  * <script type="text/javascript">
35029  */
35030  
35031 /**
35032  * @class Roo.menu.DateItem
35033  * @extends Roo.menu.Adapter
35034  * A menu item that wraps the {@link Roo.DatPicker} component.
35035  * @constructor
35036  * Creates a new DateItem
35037  * @param {Object} config Configuration options
35038  */
35039 Roo.menu.DateItem = function(config){
35040     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
35041     /** The Roo.DatePicker object @type Roo.DatePicker */
35042     this.picker = this.component;
35043     this.addEvents({select: true});
35044     
35045     this.picker.on("render", function(picker){
35046         picker.getEl().swallowEvent("click");
35047         picker.container.addClass("x-menu-date-item");
35048     });
35049
35050     this.picker.on("select", this.onSelect, this);
35051 };
35052
35053 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
35054     // private
35055     onSelect : function(picker, date){
35056         this.fireEvent("select", this, date, picker);
35057         Roo.menu.DateItem.superclass.handleClick.call(this);
35058     }
35059 });/*
35060  * Based on:
35061  * Ext JS Library 1.1.1
35062  * Copyright(c) 2006-2007, Ext JS, LLC.
35063  *
35064  * Originally Released Under LGPL - original licence link has changed is not relivant.
35065  *
35066  * Fork - LGPL
35067  * <script type="text/javascript">
35068  */
35069  
35070 /**
35071  * @class Roo.menu.ColorItem
35072  * @extends Roo.menu.Adapter
35073  * A menu item that wraps the {@link Roo.ColorPalette} component.
35074  * @constructor
35075  * Creates a new ColorItem
35076  * @param {Object} config Configuration options
35077  */
35078 Roo.menu.ColorItem = function(config){
35079     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
35080     /** The Roo.ColorPalette object @type Roo.ColorPalette */
35081     this.palette = this.component;
35082     this.relayEvents(this.palette, ["select"]);
35083     if(this.selectHandler){
35084         this.on('select', this.selectHandler, this.scope);
35085     }
35086 };
35087 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
35088  * Based on:
35089  * Ext JS Library 1.1.1
35090  * Copyright(c) 2006-2007, Ext JS, LLC.
35091  *
35092  * Originally Released Under LGPL - original licence link has changed is not relivant.
35093  *
35094  * Fork - LGPL
35095  * <script type="text/javascript">
35096  */
35097  
35098
35099 /**
35100  * @class Roo.menu.DateMenu
35101  * @extends Roo.menu.Menu
35102  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
35103  * @constructor
35104  * Creates a new DateMenu
35105  * @param {Object} config Configuration options
35106  */
35107 Roo.menu.DateMenu = function(config){
35108     Roo.menu.DateMenu.superclass.constructor.call(this, config);
35109     this.plain = true;
35110     var di = new Roo.menu.DateItem(config);
35111     this.add(di);
35112     /**
35113      * The {@link Roo.DatePicker} instance for this DateMenu
35114      * @type DatePicker
35115      */
35116     this.picker = di.picker;
35117     /**
35118      * @event select
35119      * @param {DatePicker} picker
35120      * @param {Date} date
35121      */
35122     this.relayEvents(di, ["select"]);
35123
35124     this.on('beforeshow', function(){
35125         if(this.picker){
35126             this.picker.hideMonthPicker(true);
35127         }
35128     }, this);
35129 };
35130 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
35131     cls:'x-date-menu'
35132 });/*
35133  * Based on:
35134  * Ext JS Library 1.1.1
35135  * Copyright(c) 2006-2007, Ext JS, LLC.
35136  *
35137  * Originally Released Under LGPL - original licence link has changed is not relivant.
35138  *
35139  * Fork - LGPL
35140  * <script type="text/javascript">
35141  */
35142  
35143
35144 /**
35145  * @class Roo.menu.ColorMenu
35146  * @extends Roo.menu.Menu
35147  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
35148  * @constructor
35149  * Creates a new ColorMenu
35150  * @param {Object} config Configuration options
35151  */
35152 Roo.menu.ColorMenu = function(config){
35153     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
35154     this.plain = true;
35155     var ci = new Roo.menu.ColorItem(config);
35156     this.add(ci);
35157     /**
35158      * The {@link Roo.ColorPalette} instance for this ColorMenu
35159      * @type ColorPalette
35160      */
35161     this.palette = ci.palette;
35162     /**
35163      * @event select
35164      * @param {ColorPalette} palette
35165      * @param {String} color
35166      */
35167     this.relayEvents(ci, ["select"]);
35168 };
35169 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
35170  * Based on:
35171  * Ext JS Library 1.1.1
35172  * Copyright(c) 2006-2007, Ext JS, LLC.
35173  *
35174  * Originally Released Under LGPL - original licence link has changed is not relivant.
35175  *
35176  * Fork - LGPL
35177  * <script type="text/javascript">
35178  */
35179  
35180 /**
35181  * @class Roo.form.Field
35182  * @extends Roo.BoxComponent
35183  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
35184  * @constructor
35185  * Creates a new Field
35186  * @param {Object} config Configuration options
35187  */
35188 Roo.form.Field = function(config){
35189     Roo.form.Field.superclass.constructor.call(this, config);
35190 };
35191
35192 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
35193     /**
35194      * @cfg {String} fieldLabel Label to use when rendering a form.
35195      */
35196        /**
35197      * @cfg {String} qtip Mouse over tip
35198      */
35199      
35200     /**
35201      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
35202      */
35203     invalidClass : "x-form-invalid",
35204     /**
35205      * @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")
35206      */
35207     invalidText : "The value in this field is invalid",
35208     /**
35209      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
35210      */
35211     focusClass : "x-form-focus",
35212     /**
35213      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
35214       automatic validation (defaults to "keyup").
35215      */
35216     validationEvent : "keyup",
35217     /**
35218      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
35219      */
35220     validateOnBlur : true,
35221     /**
35222      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
35223      */
35224     validationDelay : 250,
35225     /**
35226      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
35227      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
35228      */
35229     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
35230     /**
35231      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
35232      */
35233     fieldClass : "x-form-field",
35234     /**
35235      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
35236      *<pre>
35237 Value         Description
35238 -----------   ----------------------------------------------------------------------
35239 qtip          Display a quick tip when the user hovers over the field
35240 title         Display a default browser title attribute popup
35241 under         Add a block div beneath the field containing the error text
35242 side          Add an error icon to the right of the field with a popup on hover
35243 [element id]  Add the error text directly to the innerHTML of the specified element
35244 </pre>
35245      */
35246     msgTarget : 'qtip',
35247     /**
35248      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
35249      */
35250     msgFx : 'normal',
35251
35252     /**
35253      * @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.
35254      */
35255     readOnly : false,
35256
35257     /**
35258      * @cfg {Boolean} disabled True to disable the field (defaults to false).
35259      */
35260     disabled : false,
35261
35262     /**
35263      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
35264      */
35265     inputType : undefined,
35266     
35267     /**
35268      * @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).
35269          */
35270         tabIndex : undefined,
35271         
35272     // private
35273     isFormField : true,
35274
35275     // private
35276     hasFocus : false,
35277     /**
35278      * @property {Roo.Element} fieldEl
35279      * Element Containing the rendered Field (with label etc.)
35280      */
35281     /**
35282      * @cfg {Mixed} value A value to initialize this field with.
35283      */
35284     value : undefined,
35285
35286     /**
35287      * @cfg {String} name The field's HTML name attribute.
35288      */
35289     /**
35290      * @cfg {String} cls A CSS class to apply to the field's underlying element.
35291      */
35292
35293         // private ??
35294         initComponent : function(){
35295         Roo.form.Field.superclass.initComponent.call(this);
35296         this.addEvents({
35297             /**
35298              * @event focus
35299              * Fires when this field receives input focus.
35300              * @param {Roo.form.Field} this
35301              */
35302             focus : true,
35303             /**
35304              * @event blur
35305              * Fires when this field loses input focus.
35306              * @param {Roo.form.Field} this
35307              */
35308             blur : true,
35309             /**
35310              * @event specialkey
35311              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
35312              * {@link Roo.EventObject#getKey} to determine which key was pressed.
35313              * @param {Roo.form.Field} this
35314              * @param {Roo.EventObject} e The event object
35315              */
35316             specialkey : true,
35317             /**
35318              * @event change
35319              * Fires just before the field blurs if the field value has changed.
35320              * @param {Roo.form.Field} this
35321              * @param {Mixed} newValue The new value
35322              * @param {Mixed} oldValue The original value
35323              */
35324             change : true,
35325             /**
35326              * @event invalid
35327              * Fires after the field has been marked as invalid.
35328              * @param {Roo.form.Field} this
35329              * @param {String} msg The validation message
35330              */
35331             invalid : true,
35332             /**
35333              * @event valid
35334              * Fires after the field has been validated with no errors.
35335              * @param {Roo.form.Field} this
35336              */
35337             valid : true,
35338              /**
35339              * @event keyup
35340              * Fires after the key up
35341              * @param {Roo.form.Field} this
35342              * @param {Roo.EventObject}  e The event Object
35343              */
35344             keyup : true
35345         });
35346     },
35347
35348     /**
35349      * Returns the name attribute of the field if available
35350      * @return {String} name The field name
35351      */
35352     getName: function(){
35353          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
35354     },
35355
35356     // private
35357     onRender : function(ct, position){
35358         Roo.form.Field.superclass.onRender.call(this, ct, position);
35359         if(!this.el){
35360             var cfg = this.getAutoCreate();
35361             if(!cfg.name){
35362                 cfg.name = this.name || this.id;
35363             }
35364             if(this.inputType){
35365                 cfg.type = this.inputType;
35366             }
35367             this.el = ct.createChild(cfg, position);
35368         }
35369         var type = this.el.dom.type;
35370         if(type){
35371             if(type == 'password'){
35372                 type = 'text';
35373             }
35374             this.el.addClass('x-form-'+type);
35375         }
35376         if(this.readOnly){
35377             this.el.dom.readOnly = true;
35378         }
35379         if(this.tabIndex !== undefined){
35380             this.el.dom.setAttribute('tabIndex', this.tabIndex);
35381         }
35382
35383         this.el.addClass([this.fieldClass, this.cls]);
35384         this.initValue();
35385     },
35386
35387     /**
35388      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
35389      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
35390      * @return {Roo.form.Field} this
35391      */
35392     applyTo : function(target){
35393         this.allowDomMove = false;
35394         this.el = Roo.get(target);
35395         this.render(this.el.dom.parentNode);
35396         return this;
35397     },
35398
35399     // private
35400     initValue : function(){
35401         if(this.value !== undefined){
35402             this.setValue(this.value);
35403         }else if(this.el.dom.value.length > 0){
35404             this.setValue(this.el.dom.value);
35405         }
35406     },
35407
35408     /**
35409      * Returns true if this field has been changed since it was originally loaded and is not disabled.
35410      */
35411     isDirty : function() {
35412         if(this.disabled) {
35413             return false;
35414         }
35415         return String(this.getValue()) !== String(this.originalValue);
35416     },
35417
35418     // private
35419     afterRender : function(){
35420         Roo.form.Field.superclass.afterRender.call(this);
35421         this.initEvents();
35422     },
35423
35424     // private
35425     fireKey : function(e){
35426         //Roo.log('field ' + e.getKey());
35427         if(e.isNavKeyPress()){
35428             this.fireEvent("specialkey", this, e);
35429         }
35430     },
35431
35432     /**
35433      * Resets the current field value to the originally loaded value and clears any validation messages
35434      */
35435     reset : function(){
35436         this.setValue(this.originalValue);
35437         this.clearInvalid();
35438     },
35439
35440     // private
35441     initEvents : function(){
35442         // safari killled keypress - so keydown is now used..
35443         this.el.on("keydown" , this.fireKey,  this);
35444         this.el.on("focus", this.onFocus,  this);
35445         this.el.on("blur", this.onBlur,  this);
35446         this.el.relayEvent('keyup', this);
35447
35448         // reference to original value for reset
35449         this.originalValue = this.getValue();
35450     },
35451
35452     // private
35453     onFocus : function(){
35454         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35455             this.el.addClass(this.focusClass);
35456         }
35457         if(!this.hasFocus){
35458             this.hasFocus = true;
35459             this.startValue = this.getValue();
35460             this.fireEvent("focus", this);
35461         }
35462     },
35463
35464     beforeBlur : Roo.emptyFn,
35465
35466     // private
35467     onBlur : function(){
35468         this.beforeBlur();
35469         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
35470             this.el.removeClass(this.focusClass);
35471         }
35472         this.hasFocus = false;
35473         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
35474             this.validate();
35475         }
35476         var v = this.getValue();
35477         if(String(v) !== String(this.startValue)){
35478             this.fireEvent('change', this, v, this.startValue);
35479         }
35480         this.fireEvent("blur", this);
35481     },
35482
35483     /**
35484      * Returns whether or not the field value is currently valid
35485      * @param {Boolean} preventMark True to disable marking the field invalid
35486      * @return {Boolean} True if the value is valid, else false
35487      */
35488     isValid : function(preventMark){
35489         if(this.disabled){
35490             return true;
35491         }
35492         var restore = this.preventMark;
35493         this.preventMark = preventMark === true;
35494         var v = this.validateValue(this.processValue(this.getRawValue()));
35495         this.preventMark = restore;
35496         return v;
35497     },
35498
35499     /**
35500      * Validates the field value
35501      * @return {Boolean} True if the value is valid, else false
35502      */
35503     validate : function(){
35504         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
35505             this.clearInvalid();
35506             return true;
35507         }
35508         return false;
35509     },
35510
35511     processValue : function(value){
35512         return value;
35513     },
35514
35515     // private
35516     // Subclasses should provide the validation implementation by overriding this
35517     validateValue : function(value){
35518         return true;
35519     },
35520
35521     /**
35522      * Mark this field as invalid
35523      * @param {String} msg The validation message
35524      */
35525     markInvalid : function(msg){
35526         if(!this.rendered || this.preventMark){ // not rendered
35527             return;
35528         }
35529         this.el.addClass(this.invalidClass);
35530         msg = msg || this.invalidText;
35531         switch(this.msgTarget){
35532             case 'qtip':
35533                 this.el.dom.qtip = msg;
35534                 this.el.dom.qclass = 'x-form-invalid-tip';
35535                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
35536                     Roo.QuickTips.enable();
35537                 }
35538                 break;
35539             case 'title':
35540                 this.el.dom.title = msg;
35541                 break;
35542             case 'under':
35543                 if(!this.errorEl){
35544                     var elp = this.el.findParent('.x-form-element', 5, true);
35545                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
35546                     this.errorEl.setWidth(elp.getWidth(true)-20);
35547                 }
35548                 this.errorEl.update(msg);
35549                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
35550                 break;
35551             case 'side':
35552                 if(!this.errorIcon){
35553                     var elp = this.el.findParent('.x-form-element', 5, true);
35554                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
35555                 }
35556                 this.alignErrorIcon();
35557                 this.errorIcon.dom.qtip = msg;
35558                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
35559                 this.errorIcon.show();
35560                 this.on('resize', this.alignErrorIcon, this);
35561                 break;
35562             default:
35563                 var t = Roo.getDom(this.msgTarget);
35564                 t.innerHTML = msg;
35565                 t.style.display = this.msgDisplay;
35566                 break;
35567         }
35568         this.fireEvent('invalid', this, msg);
35569     },
35570
35571     // private
35572     alignErrorIcon : function(){
35573         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
35574     },
35575
35576     /**
35577      * Clear any invalid styles/messages for this field
35578      */
35579     clearInvalid : function(){
35580         if(!this.rendered || this.preventMark){ // not rendered
35581             return;
35582         }
35583         this.el.removeClass(this.invalidClass);
35584         switch(this.msgTarget){
35585             case 'qtip':
35586                 this.el.dom.qtip = '';
35587                 break;
35588             case 'title':
35589                 this.el.dom.title = '';
35590                 break;
35591             case 'under':
35592                 if(this.errorEl){
35593                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
35594                 }
35595                 break;
35596             case 'side':
35597                 if(this.errorIcon){
35598                     this.errorIcon.dom.qtip = '';
35599                     this.errorIcon.hide();
35600                     this.un('resize', this.alignErrorIcon, this);
35601                 }
35602                 break;
35603             default:
35604                 var t = Roo.getDom(this.msgTarget);
35605                 t.innerHTML = '';
35606                 t.style.display = 'none';
35607                 break;
35608         }
35609         this.fireEvent('valid', this);
35610     },
35611
35612     /**
35613      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
35614      * @return {Mixed} value The field value
35615      */
35616     getRawValue : function(){
35617         var v = this.el.getValue();
35618         if(v === this.emptyText){
35619             v = '';
35620         }
35621         return v;
35622     },
35623
35624     /**
35625      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
35626      * @return {Mixed} value The field value
35627      */
35628     getValue : function(){
35629         var v = this.el.getValue();
35630         if(v === this.emptyText || v === undefined){
35631             v = '';
35632         }
35633         return v;
35634     },
35635
35636     /**
35637      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
35638      * @param {Mixed} value The value to set
35639      */
35640     setRawValue : function(v){
35641         return this.el.dom.value = (v === null || v === undefined ? '' : v);
35642     },
35643
35644     /**
35645      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
35646      * @param {Mixed} value The value to set
35647      */
35648     setValue : function(v){
35649         this.value = v;
35650         if(this.rendered){
35651             this.el.dom.value = (v === null || v === undefined ? '' : v);
35652             this.validate();
35653         }
35654     },
35655
35656     adjustSize : function(w, h){
35657         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
35658         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
35659         return s;
35660     },
35661
35662     adjustWidth : function(tag, w){
35663         tag = tag.toLowerCase();
35664         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
35665             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
35666                 if(tag == 'input'){
35667                     return w + 2;
35668                 }
35669                 if(tag = 'textarea'){
35670                     return w-2;
35671                 }
35672             }else if(Roo.isOpera){
35673                 if(tag == 'input'){
35674                     return w + 2;
35675                 }
35676                 if(tag = 'textarea'){
35677                     return w-2;
35678                 }
35679             }
35680         }
35681         return w;
35682     }
35683 });
35684
35685
35686 // anything other than normal should be considered experimental
35687 Roo.form.Field.msgFx = {
35688     normal : {
35689         show: function(msgEl, f){
35690             msgEl.setDisplayed('block');
35691         },
35692
35693         hide : function(msgEl, f){
35694             msgEl.setDisplayed(false).update('');
35695         }
35696     },
35697
35698     slide : {
35699         show: function(msgEl, f){
35700             msgEl.slideIn('t', {stopFx:true});
35701         },
35702
35703         hide : function(msgEl, f){
35704             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
35705         }
35706     },
35707
35708     slideRight : {
35709         show: function(msgEl, f){
35710             msgEl.fixDisplay();
35711             msgEl.alignTo(f.el, 'tl-tr');
35712             msgEl.slideIn('l', {stopFx:true});
35713         },
35714
35715         hide : function(msgEl, f){
35716             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
35717         }
35718     }
35719 };/*
35720  * Based on:
35721  * Ext JS Library 1.1.1
35722  * Copyright(c) 2006-2007, Ext JS, LLC.
35723  *
35724  * Originally Released Under LGPL - original licence link has changed is not relivant.
35725  *
35726  * Fork - LGPL
35727  * <script type="text/javascript">
35728  */
35729  
35730
35731 /**
35732  * @class Roo.form.TextField
35733  * @extends Roo.form.Field
35734  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
35735  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
35736  * @constructor
35737  * Creates a new TextField
35738  * @param {Object} config Configuration options
35739  */
35740 Roo.form.TextField = function(config){
35741     Roo.form.TextField.superclass.constructor.call(this, config);
35742     this.addEvents({
35743         /**
35744          * @event autosize
35745          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
35746          * according to the default logic, but this event provides a hook for the developer to apply additional
35747          * logic at runtime to resize the field if needed.
35748              * @param {Roo.form.Field} this This text field
35749              * @param {Number} width The new field width
35750              */
35751         autosize : true
35752     });
35753 };
35754
35755 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
35756     /**
35757      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
35758      */
35759     grow : false,
35760     /**
35761      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
35762      */
35763     growMin : 30,
35764     /**
35765      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
35766      */
35767     growMax : 800,
35768     /**
35769      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
35770      */
35771     vtype : null,
35772     /**
35773      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
35774      */
35775     maskRe : null,
35776     /**
35777      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
35778      */
35779     disableKeyFilter : false,
35780     /**
35781      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
35782      */
35783     allowBlank : true,
35784     /**
35785      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
35786      */
35787     minLength : 0,
35788     /**
35789      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
35790      */
35791     maxLength : Number.MAX_VALUE,
35792     /**
35793      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
35794      */
35795     minLengthText : "The minimum length for this field is {0}",
35796     /**
35797      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
35798      */
35799     maxLengthText : "The maximum length for this field is {0}",
35800     /**
35801      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
35802      */
35803     selectOnFocus : false,
35804     /**
35805      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
35806      */
35807     blankText : "This field is required",
35808     /**
35809      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
35810      * If available, this function will be called only after the basic validators all return true, and will be passed the
35811      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
35812      */
35813     validator : null,
35814     /**
35815      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
35816      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
35817      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
35818      */
35819     regex : null,
35820     /**
35821      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
35822      */
35823     regexText : "",
35824     /**
35825      * @cfg {String} emptyText The default text to display in an empty field (defaults to null).
35826      */
35827     emptyText : null,
35828     /**
35829      * @cfg {String} emptyClass The CSS class to apply to an empty field to style the {@link #emptyText} (defaults to
35830      * 'x-form-empty-field').  This class is automatically added and removed as needed depending on the current field value.
35831      */
35832     emptyClass : 'x-form-empty-field',
35833
35834     // private
35835     initEvents : function(){
35836         Roo.form.TextField.superclass.initEvents.call(this);
35837         if(this.validationEvent == 'keyup'){
35838             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
35839             this.el.on('keyup', this.filterValidation, this);
35840         }
35841         else if(this.validationEvent !== false){
35842             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
35843         }
35844         if(this.selectOnFocus || this.emptyText){
35845             this.on("focus", this.preFocus, this);
35846             if(this.emptyText){
35847                 this.on('blur', this.postBlur, this);
35848                 this.applyEmptyText();
35849             }
35850         }
35851         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
35852             this.el.on("keypress", this.filterKeys, this);
35853         }
35854         if(this.grow){
35855             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
35856             this.el.on("click", this.autoSize,  this);
35857         }
35858     },
35859
35860     processValue : function(value){
35861         if(this.stripCharsRe){
35862             var newValue = value.replace(this.stripCharsRe, '');
35863             if(newValue !== value){
35864                 this.setRawValue(newValue);
35865                 return newValue;
35866             }
35867         }
35868         return value;
35869     },
35870
35871     filterValidation : function(e){
35872         if(!e.isNavKeyPress()){
35873             this.validationTask.delay(this.validationDelay);
35874         }
35875     },
35876
35877     // private
35878     onKeyUp : function(e){
35879         if(!e.isNavKeyPress()){
35880             this.autoSize();
35881         }
35882     },
35883
35884     /**
35885      * Resets the current field value to the originally-loaded value and clears any validation messages.
35886      * Also adds emptyText and emptyClass if the original value was blank.
35887      */
35888     reset : function(){
35889         Roo.form.TextField.superclass.reset.call(this);
35890         this.applyEmptyText();
35891     },
35892
35893     applyEmptyText : function(){
35894         if(this.rendered && this.emptyText && this.getRawValue().length < 1){
35895             this.setRawValue(this.emptyText);
35896             this.el.addClass(this.emptyClass);
35897         }
35898     },
35899
35900     // private
35901     preFocus : function(){
35902         if(this.emptyText){
35903             if(this.el.dom.value == this.emptyText){
35904                 this.setRawValue('');
35905             }
35906             this.el.removeClass(this.emptyClass);
35907         }
35908         if(this.selectOnFocus){
35909             this.el.dom.select();
35910         }
35911     },
35912
35913     // private
35914     postBlur : function(){
35915         this.applyEmptyText();
35916     },
35917
35918     // private
35919     filterKeys : function(e){
35920         var k = e.getKey();
35921         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
35922             return;
35923         }
35924         var c = e.getCharCode(), cc = String.fromCharCode(c);
35925         if(Roo.isIE && (e.isSpecialKey() || !cc)){
35926             return;
35927         }
35928         if(!this.maskRe.test(cc)){
35929             e.stopEvent();
35930         }
35931     },
35932
35933     setValue : function(v){
35934         if(this.emptyText && this.el && v !== undefined && v !== null && v !== ''){
35935             this.el.removeClass(this.emptyClass);
35936         }
35937         Roo.form.TextField.superclass.setValue.apply(this, arguments);
35938         this.applyEmptyText();
35939         this.autoSize();
35940     },
35941
35942     /**
35943      * Validates a value according to the field's validation rules and marks the field as invalid
35944      * if the validation fails
35945      * @param {Mixed} value The value to validate
35946      * @return {Boolean} True if the value is valid, else false
35947      */
35948     validateValue : function(value){
35949         if(value.length < 1 || value === this.emptyText){ // if it's blank
35950              if(this.allowBlank){
35951                 this.clearInvalid();
35952                 return true;
35953              }else{
35954                 this.markInvalid(this.blankText);
35955                 return false;
35956              }
35957         }
35958         if(value.length < this.minLength){
35959             this.markInvalid(String.format(this.minLengthText, this.minLength));
35960             return false;
35961         }
35962         if(value.length > this.maxLength){
35963             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
35964             return false;
35965         }
35966         if(this.vtype){
35967             var vt = Roo.form.VTypes;
35968             if(!vt[this.vtype](value, this)){
35969                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
35970                 return false;
35971             }
35972         }
35973         if(typeof this.validator == "function"){
35974             var msg = this.validator(value);
35975             if(msg !== true){
35976                 this.markInvalid(msg);
35977                 return false;
35978             }
35979         }
35980         if(this.regex && !this.regex.test(value)){
35981             this.markInvalid(this.regexText);
35982             return false;
35983         }
35984         return true;
35985     },
35986
35987     /**
35988      * Selects text in this field
35989      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
35990      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
35991      */
35992     selectText : function(start, end){
35993         var v = this.getRawValue();
35994         if(v.length > 0){
35995             start = start === undefined ? 0 : start;
35996             end = end === undefined ? v.length : end;
35997             var d = this.el.dom;
35998             if(d.setSelectionRange){
35999                 d.setSelectionRange(start, end);
36000             }else if(d.createTextRange){
36001                 var range = d.createTextRange();
36002                 range.moveStart("character", start);
36003                 range.moveEnd("character", v.length-end);
36004                 range.select();
36005             }
36006         }
36007     },
36008
36009     /**
36010      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
36011      * This only takes effect if grow = true, and fires the autosize event.
36012      */
36013     autoSize : function(){
36014         if(!this.grow || !this.rendered){
36015             return;
36016         }
36017         if(!this.metrics){
36018             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
36019         }
36020         var el = this.el;
36021         var v = el.dom.value;
36022         var d = document.createElement('div');
36023         d.appendChild(document.createTextNode(v));
36024         v = d.innerHTML;
36025         d = null;
36026         v += "&#160;";
36027         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
36028         this.el.setWidth(w);
36029         this.fireEvent("autosize", this, w);
36030     }
36031 });/*
36032  * Based on:
36033  * Ext JS Library 1.1.1
36034  * Copyright(c) 2006-2007, Ext JS, LLC.
36035  *
36036  * Originally Released Under LGPL - original licence link has changed is not relivant.
36037  *
36038  * Fork - LGPL
36039  * <script type="text/javascript">
36040  */
36041  
36042 /**
36043  * @class Roo.form.Hidden
36044  * @extends Roo.form.TextField
36045  * Simple Hidden element used on forms 
36046  * 
36047  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
36048  * 
36049  * @constructor
36050  * Creates a new Hidden form element.
36051  * @param {Object} config Configuration options
36052  */
36053
36054
36055
36056 // easy hidden field...
36057 Roo.form.Hidden = function(config){
36058     Roo.form.Hidden.superclass.constructor.call(this, config);
36059 };
36060   
36061 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
36062     fieldLabel:      '',
36063     inputType:      'hidden',
36064     width:          50,
36065     allowBlank:     true,
36066     labelSeparator: '',
36067     hidden:         true,
36068     itemCls :       'x-form-item-display-none'
36069
36070
36071 });
36072
36073
36074 /*
36075  * Based on:
36076  * Ext JS Library 1.1.1
36077  * Copyright(c) 2006-2007, Ext JS, LLC.
36078  *
36079  * Originally Released Under LGPL - original licence link has changed is not relivant.
36080  *
36081  * Fork - LGPL
36082  * <script type="text/javascript">
36083  */
36084  
36085 /**
36086  * @class Roo.form.TriggerField
36087  * @extends Roo.form.TextField
36088  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
36089  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
36090  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
36091  * for which you can provide a custom implementation.  For example:
36092  * <pre><code>
36093 var trigger = new Roo.form.TriggerField();
36094 trigger.onTriggerClick = myTriggerFn;
36095 trigger.applyTo('my-field');
36096 </code></pre>
36097  *
36098  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
36099  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
36100  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
36101  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
36102  * @constructor
36103  * Create a new TriggerField.
36104  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
36105  * to the base TextField)
36106  */
36107 Roo.form.TriggerField = function(config){
36108     this.mimicing = false;
36109     Roo.form.TriggerField.superclass.constructor.call(this, config);
36110 };
36111
36112 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
36113     /**
36114      * @cfg {String} triggerClass A CSS class to apply to the trigger
36115      */
36116     /**
36117      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36118      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
36119      */
36120     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
36121     /**
36122      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
36123      */
36124     hideTrigger:false,
36125
36126     /** @cfg {Boolean} grow @hide */
36127     /** @cfg {Number} growMin @hide */
36128     /** @cfg {Number} growMax @hide */
36129
36130     /**
36131      * @hide 
36132      * @method
36133      */
36134     autoSize: Roo.emptyFn,
36135     // private
36136     monitorTab : true,
36137     // private
36138     deferHeight : true,
36139
36140     
36141     actionMode : 'wrap',
36142     // private
36143     onResize : function(w, h){
36144         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
36145         if(typeof w == 'number'){
36146             var x = w - this.trigger.getWidth();
36147             this.el.setWidth(this.adjustWidth('input', x));
36148             this.trigger.setStyle('left', x+'px');
36149         }
36150     },
36151
36152     // private
36153     adjustSize : Roo.BoxComponent.prototype.adjustSize,
36154
36155     // private
36156     getResizeEl : function(){
36157         return this.wrap;
36158     },
36159
36160     // private
36161     getPositionEl : function(){
36162         return this.wrap;
36163     },
36164
36165     // private
36166     alignErrorIcon : function(){
36167         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
36168     },
36169
36170     // private
36171     onRender : function(ct, position){
36172         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
36173         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
36174         this.trigger = this.wrap.createChild(this.triggerConfig ||
36175                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
36176         if(this.hideTrigger){
36177             this.trigger.setDisplayed(false);
36178         }
36179         this.initTrigger();
36180         if(!this.width){
36181             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
36182         }
36183     },
36184
36185     // private
36186     initTrigger : function(){
36187         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
36188         this.trigger.addClassOnOver('x-form-trigger-over');
36189         this.trigger.addClassOnClick('x-form-trigger-click');
36190     },
36191
36192     // private
36193     onDestroy : function(){
36194         if(this.trigger){
36195             this.trigger.removeAllListeners();
36196             this.trigger.remove();
36197         }
36198         if(this.wrap){
36199             this.wrap.remove();
36200         }
36201         Roo.form.TriggerField.superclass.onDestroy.call(this);
36202     },
36203
36204     // private
36205     onFocus : function(){
36206         Roo.form.TriggerField.superclass.onFocus.call(this);
36207         if(!this.mimicing){
36208             this.wrap.addClass('x-trigger-wrap-focus');
36209             this.mimicing = true;
36210             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
36211             if(this.monitorTab){
36212                 this.el.on("keydown", this.checkTab, this);
36213             }
36214         }
36215     },
36216
36217     // private
36218     checkTab : function(e){
36219         if(e.getKey() == e.TAB){
36220             this.triggerBlur();
36221         }
36222     },
36223
36224     // private
36225     onBlur : function(){
36226         // do nothing
36227     },
36228
36229     // private
36230     mimicBlur : function(e, t){
36231         if(!this.wrap.contains(t) && this.validateBlur()){
36232             this.triggerBlur();
36233         }
36234     },
36235
36236     // private
36237     triggerBlur : function(){
36238         this.mimicing = false;
36239         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
36240         if(this.monitorTab){
36241             this.el.un("keydown", this.checkTab, this);
36242         }
36243         this.wrap.removeClass('x-trigger-wrap-focus');
36244         Roo.form.TriggerField.superclass.onBlur.call(this);
36245     },
36246
36247     // private
36248     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
36249     validateBlur : function(e, t){
36250         return true;
36251     },
36252
36253     // private
36254     onDisable : function(){
36255         Roo.form.TriggerField.superclass.onDisable.call(this);
36256         if(this.wrap){
36257             this.wrap.addClass('x-item-disabled');
36258         }
36259     },
36260
36261     // private
36262     onEnable : function(){
36263         Roo.form.TriggerField.superclass.onEnable.call(this);
36264         if(this.wrap){
36265             this.wrap.removeClass('x-item-disabled');
36266         }
36267     },
36268
36269     // private
36270     onShow : function(){
36271         var ae = this.getActionEl();
36272         
36273         if(ae){
36274             ae.dom.style.display = '';
36275             ae.dom.style.visibility = 'visible';
36276         }
36277     },
36278
36279     // private
36280     
36281     onHide : function(){
36282         var ae = this.getActionEl();
36283         ae.dom.style.display = 'none';
36284     },
36285
36286     /**
36287      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
36288      * by an implementing function.
36289      * @method
36290      * @param {EventObject} e
36291      */
36292     onTriggerClick : Roo.emptyFn
36293 });
36294
36295 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
36296 // to be extended by an implementing class.  For an example of implementing this class, see the custom
36297 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
36298 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
36299     initComponent : function(){
36300         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
36301
36302         this.triggerConfig = {
36303             tag:'span', cls:'x-form-twin-triggers', cn:[
36304             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
36305             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
36306         ]};
36307     },
36308
36309     getTrigger : function(index){
36310         return this.triggers[index];
36311     },
36312
36313     initTrigger : function(){
36314         var ts = this.trigger.select('.x-form-trigger', true);
36315         this.wrap.setStyle('overflow', 'hidden');
36316         var triggerField = this;
36317         ts.each(function(t, all, index){
36318             t.hide = function(){
36319                 var w = triggerField.wrap.getWidth();
36320                 this.dom.style.display = 'none';
36321                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36322             };
36323             t.show = function(){
36324                 var w = triggerField.wrap.getWidth();
36325                 this.dom.style.display = '';
36326                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
36327             };
36328             var triggerIndex = 'Trigger'+(index+1);
36329
36330             if(this['hide'+triggerIndex]){
36331                 t.dom.style.display = 'none';
36332             }
36333             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
36334             t.addClassOnOver('x-form-trigger-over');
36335             t.addClassOnClick('x-form-trigger-click');
36336         }, this);
36337         this.triggers = ts.elements;
36338     },
36339
36340     onTrigger1Click : Roo.emptyFn,
36341     onTrigger2Click : Roo.emptyFn
36342 });/*
36343  * Based on:
36344  * Ext JS Library 1.1.1
36345  * Copyright(c) 2006-2007, Ext JS, LLC.
36346  *
36347  * Originally Released Under LGPL - original licence link has changed is not relivant.
36348  *
36349  * Fork - LGPL
36350  * <script type="text/javascript">
36351  */
36352  
36353 /**
36354  * @class Roo.form.TextArea
36355  * @extends Roo.form.TextField
36356  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
36357  * support for auto-sizing.
36358  * @constructor
36359  * Creates a new TextArea
36360  * @param {Object} config Configuration options
36361  */
36362 Roo.form.TextArea = function(config){
36363     Roo.form.TextArea.superclass.constructor.call(this, config);
36364     // these are provided exchanges for backwards compat
36365     // minHeight/maxHeight were replaced by growMin/growMax to be
36366     // compatible with TextField growing config values
36367     if(this.minHeight !== undefined){
36368         this.growMin = this.minHeight;
36369     }
36370     if(this.maxHeight !== undefined){
36371         this.growMax = this.maxHeight;
36372     }
36373 };
36374
36375 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
36376     /**
36377      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
36378      */
36379     growMin : 60,
36380     /**
36381      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
36382      */
36383     growMax: 1000,
36384     /**
36385      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
36386      * in the field (equivalent to setting overflow: hidden, defaults to false)
36387      */
36388     preventScrollbars: false,
36389     /**
36390      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36391      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
36392      */
36393
36394     // private
36395     onRender : function(ct, position){
36396         if(!this.el){
36397             this.defaultAutoCreate = {
36398                 tag: "textarea",
36399                 style:"width:300px;height:60px;",
36400                 autocomplete: "off"
36401             };
36402         }
36403         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
36404         if(this.grow){
36405             this.textSizeEl = Roo.DomHelper.append(document.body, {
36406                 tag: "pre", cls: "x-form-grow-sizer"
36407             });
36408             if(this.preventScrollbars){
36409                 this.el.setStyle("overflow", "hidden");
36410             }
36411             this.el.setHeight(this.growMin);
36412         }
36413     },
36414
36415     onDestroy : function(){
36416         if(this.textSizeEl){
36417             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
36418         }
36419         Roo.form.TextArea.superclass.onDestroy.call(this);
36420     },
36421
36422     // private
36423     onKeyUp : function(e){
36424         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
36425             this.autoSize();
36426         }
36427     },
36428
36429     /**
36430      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
36431      * This only takes effect if grow = true, and fires the autosize event if the height changes.
36432      */
36433     autoSize : function(){
36434         if(!this.grow || !this.textSizeEl){
36435             return;
36436         }
36437         var el = this.el;
36438         var v = el.dom.value;
36439         var ts = this.textSizeEl;
36440
36441         ts.innerHTML = '';
36442         ts.appendChild(document.createTextNode(v));
36443         v = ts.innerHTML;
36444
36445         Roo.fly(ts).setWidth(this.el.getWidth());
36446         if(v.length < 1){
36447             v = "&#160;&#160;";
36448         }else{
36449             if(Roo.isIE){
36450                 v = v.replace(/\n/g, '<p>&#160;</p>');
36451             }
36452             v += "&#160;\n&#160;";
36453         }
36454         ts.innerHTML = v;
36455         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
36456         if(h != this.lastHeight){
36457             this.lastHeight = h;
36458             this.el.setHeight(h);
36459             this.fireEvent("autosize", this, h);
36460         }
36461     }
36462 });/*
36463  * Based on:
36464  * Ext JS Library 1.1.1
36465  * Copyright(c) 2006-2007, Ext JS, LLC.
36466  *
36467  * Originally Released Under LGPL - original licence link has changed is not relivant.
36468  *
36469  * Fork - LGPL
36470  * <script type="text/javascript">
36471  */
36472  
36473
36474 /**
36475  * @class Roo.form.NumberField
36476  * @extends Roo.form.TextField
36477  * Numeric text field that provides automatic keystroke filtering and numeric validation.
36478  * @constructor
36479  * Creates a new NumberField
36480  * @param {Object} config Configuration options
36481  */
36482 Roo.form.NumberField = function(config){
36483     Roo.form.NumberField.superclass.constructor.call(this, config);
36484 };
36485
36486 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
36487     /**
36488      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
36489      */
36490     fieldClass: "x-form-field x-form-num-field",
36491     /**
36492      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36493      */
36494     allowDecimals : true,
36495     /**
36496      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36497      */
36498     decimalSeparator : ".",
36499     /**
36500      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36501      */
36502     decimalPrecision : 2,
36503     /**
36504      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36505      */
36506     allowNegative : true,
36507     /**
36508      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36509      */
36510     minValue : Number.NEGATIVE_INFINITY,
36511     /**
36512      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36513      */
36514     maxValue : Number.MAX_VALUE,
36515     /**
36516      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36517      */
36518     minText : "The minimum value for this field is {0}",
36519     /**
36520      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36521      */
36522     maxText : "The maximum value for this field is {0}",
36523     /**
36524      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36525      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36526      */
36527     nanText : "{0} is not a valid number",
36528
36529     // private
36530     initEvents : function(){
36531         Roo.form.NumberField.superclass.initEvents.call(this);
36532         var allowed = "0123456789";
36533         if(this.allowDecimals){
36534             allowed += this.decimalSeparator;
36535         }
36536         if(this.allowNegative){
36537             allowed += "-";
36538         }
36539         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36540         var keyPress = function(e){
36541             var k = e.getKey();
36542             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36543                 return;
36544             }
36545             var c = e.getCharCode();
36546             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36547                 e.stopEvent();
36548             }
36549         };
36550         this.el.on("keypress", keyPress, this);
36551     },
36552
36553     // private
36554     validateValue : function(value){
36555         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
36556             return false;
36557         }
36558         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36559              return true;
36560         }
36561         var num = this.parseValue(value);
36562         if(isNaN(num)){
36563             this.markInvalid(String.format(this.nanText, value));
36564             return false;
36565         }
36566         if(num < this.minValue){
36567             this.markInvalid(String.format(this.minText, this.minValue));
36568             return false;
36569         }
36570         if(num > this.maxValue){
36571             this.markInvalid(String.format(this.maxText, this.maxValue));
36572             return false;
36573         }
36574         return true;
36575     },
36576
36577     getValue : function(){
36578         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
36579     },
36580
36581     // private
36582     parseValue : function(value){
36583         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36584         return isNaN(value) ? '' : value;
36585     },
36586
36587     // private
36588     fixPrecision : function(value){
36589         var nan = isNaN(value);
36590         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36591             return nan ? '' : value;
36592         }
36593         return parseFloat(value).toFixed(this.decimalPrecision);
36594     },
36595
36596     setValue : function(v){
36597         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
36598     },
36599
36600     // private
36601     decimalPrecisionFcn : function(v){
36602         return Math.floor(v);
36603     },
36604
36605     beforeBlur : function(){
36606         var v = this.parseValue(this.getRawValue());
36607         if(v){
36608             this.setValue(this.fixPrecision(v));
36609         }
36610     }
36611 });/*
36612  * Based on:
36613  * Ext JS Library 1.1.1
36614  * Copyright(c) 2006-2007, Ext JS, LLC.
36615  *
36616  * Originally Released Under LGPL - original licence link has changed is not relivant.
36617  *
36618  * Fork - LGPL
36619  * <script type="text/javascript">
36620  */
36621  
36622 /**
36623  * @class Roo.form.DateField
36624  * @extends Roo.form.TriggerField
36625  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
36626 * @constructor
36627 * Create a new DateField
36628 * @param {Object} config
36629  */
36630 Roo.form.DateField = function(config){
36631     Roo.form.DateField.superclass.constructor.call(this, config);
36632     
36633       this.addEvents({
36634          
36635         /**
36636          * @event select
36637          * Fires when a date is selected
36638              * @param {Roo.form.DateField} combo This combo box
36639              * @param {Date} date The date selected
36640              */
36641         'select' : true
36642          
36643     });
36644     
36645     
36646     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
36647     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
36648     this.ddMatch = null;
36649     if(this.disabledDates){
36650         var dd = this.disabledDates;
36651         var re = "(?:";
36652         for(var i = 0; i < dd.length; i++){
36653             re += dd[i];
36654             if(i != dd.length-1) re += "|";
36655         }
36656         this.ddMatch = new RegExp(re + ")");
36657     }
36658 };
36659
36660 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
36661     /**
36662      * @cfg {String} format
36663      * The default date format string which can be overriden for localization support.  The format must be
36664      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
36665      */
36666     format : "m/d/y",
36667     /**
36668      * @cfg {String} altFormats
36669      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
36670      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
36671      */
36672     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
36673     /**
36674      * @cfg {Array} disabledDays
36675      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
36676      */
36677     disabledDays : null,
36678     /**
36679      * @cfg {String} disabledDaysText
36680      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
36681      */
36682     disabledDaysText : "Disabled",
36683     /**
36684      * @cfg {Array} disabledDates
36685      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
36686      * expression so they are very powerful. Some examples:
36687      * <ul>
36688      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
36689      * <li>["03/08", "09/16"] would disable those days for every year</li>
36690      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
36691      * <li>["03/../2006"] would disable every day in March 2006</li>
36692      * <li>["^03"] would disable every day in every March</li>
36693      * </ul>
36694      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
36695      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
36696      */
36697     disabledDates : null,
36698     /**
36699      * @cfg {String} disabledDatesText
36700      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
36701      */
36702     disabledDatesText : "Disabled",
36703     /**
36704      * @cfg {Date/String} minValue
36705      * The minimum allowed date. Can be either a Javascript date object or a string date in a
36706      * valid format (defaults to null).
36707      */
36708     minValue : null,
36709     /**
36710      * @cfg {Date/String} maxValue
36711      * The maximum allowed date. Can be either a Javascript date object or a string date in a
36712      * valid format (defaults to null).
36713      */
36714     maxValue : null,
36715     /**
36716      * @cfg {String} minText
36717      * The error text to display when the date in the cell is before minValue (defaults to
36718      * 'The date in this field must be after {minValue}').
36719      */
36720     minText : "The date in this field must be equal to or after {0}",
36721     /**
36722      * @cfg {String} maxText
36723      * The error text to display when the date in the cell is after maxValue (defaults to
36724      * 'The date in this field must be before {maxValue}').
36725      */
36726     maxText : "The date in this field must be equal to or before {0}",
36727     /**
36728      * @cfg {String} invalidText
36729      * The error text to display when the date in the field is invalid (defaults to
36730      * '{value} is not a valid date - it must be in the format {format}').
36731      */
36732     invalidText : "{0} is not a valid date - it must be in the format {1}",
36733     /**
36734      * @cfg {String} triggerClass
36735      * An additional CSS class used to style the trigger button.  The trigger will always get the
36736      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
36737      * which displays a calendar icon).
36738      */
36739     triggerClass : 'x-form-date-trigger',
36740     
36741
36742     /**
36743      * @cfg {bool} useIso
36744      * if enabled, then the date field will use a hidden field to store the 
36745      * real value as iso formated date. default (false)
36746      */ 
36747     useIso : false,
36748     /**
36749      * @cfg {String/Object} autoCreate
36750      * A DomHelper element spec, or true for a default element spec (defaults to
36751      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
36752      */ 
36753     // private
36754     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
36755     
36756     // private
36757     hiddenField: false,
36758     
36759     onRender : function(ct, position)
36760     {
36761         Roo.form.DateField.superclass.onRender.call(this, ct, position);
36762         if (this.useIso) {
36763             this.el.dom.removeAttribute('name'); 
36764             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
36765                     'before', true);
36766             this.hiddenField.value = this.formatDate(this.value, 'Y-m-d');
36767             // prevent input submission
36768             this.hiddenName = this.name;
36769         }
36770             
36771             
36772     },
36773     
36774     // private
36775     validateValue : function(value)
36776     {
36777         value = this.formatDate(value);
36778         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
36779             return false;
36780         }
36781         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
36782              return true;
36783         }
36784         var svalue = value;
36785         value = this.parseDate(value);
36786         if(!value){
36787             this.markInvalid(String.format(this.invalidText, svalue, this.format));
36788             return false;
36789         }
36790         var time = value.getTime();
36791         if(this.minValue && time < this.minValue.getTime()){
36792             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
36793             return false;
36794         }
36795         if(this.maxValue && time > this.maxValue.getTime()){
36796             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
36797             return false;
36798         }
36799         if(this.disabledDays){
36800             var day = value.getDay();
36801             for(var i = 0; i < this.disabledDays.length; i++) {
36802                 if(day === this.disabledDays[i]){
36803                     this.markInvalid(this.disabledDaysText);
36804                     return false;
36805                 }
36806             }
36807         }
36808         var fvalue = this.formatDate(value);
36809         if(this.ddMatch && this.ddMatch.test(fvalue)){
36810             this.markInvalid(String.format(this.disabledDatesText, fvalue));
36811             return false;
36812         }
36813         return true;
36814     },
36815
36816     // private
36817     // Provides logic to override the default TriggerField.validateBlur which just returns true
36818     validateBlur : function(){
36819         return !this.menu || !this.menu.isVisible();
36820     },
36821
36822     /**
36823      * Returns the current date value of the date field.
36824      * @return {Date} The date value
36825      */
36826     getValue : function(){
36827         
36828         return  this.hiddenField ? this.hiddenField.value : this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
36829     },
36830
36831     /**
36832      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
36833      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
36834      * (the default format used is "m/d/y").
36835      * <br />Usage:
36836      * <pre><code>
36837 //All of these calls set the same date value (May 4, 2006)
36838
36839 //Pass a date object:
36840 var dt = new Date('5/4/06');
36841 dateField.setValue(dt);
36842
36843 //Pass a date string (default format):
36844 dateField.setValue('5/4/06');
36845
36846 //Pass a date string (custom format):
36847 dateField.format = 'Y-m-d';
36848 dateField.setValue('2006-5-4');
36849 </code></pre>
36850      * @param {String/Date} date The date or valid date string
36851      */
36852     setValue : function(date){
36853         if (this.hiddenField) {
36854             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
36855         }
36856         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
36857     },
36858
36859     // private
36860     parseDate : function(value){
36861         if(!value || value instanceof Date){
36862             return value;
36863         }
36864         var v = Date.parseDate(value, this.format);
36865         if(!v && this.altFormats){
36866             if(!this.altFormatsArray){
36867                 this.altFormatsArray = this.altFormats.split("|");
36868             }
36869             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
36870                 v = Date.parseDate(value, this.altFormatsArray[i]);
36871             }
36872         }
36873         return v;
36874     },
36875
36876     // private
36877     formatDate : function(date, fmt){
36878         return (!date || !(date instanceof Date)) ?
36879                date : date.dateFormat(fmt || this.format);
36880     },
36881
36882     // private
36883     menuListeners : {
36884         select: function(m, d){
36885             this.setValue(d);
36886             this.fireEvent('select', this, d);
36887         },
36888         show : function(){ // retain focus styling
36889             this.onFocus();
36890         },
36891         hide : function(){
36892             this.focus.defer(10, this);
36893             var ml = this.menuListeners;
36894             this.menu.un("select", ml.select,  this);
36895             this.menu.un("show", ml.show,  this);
36896             this.menu.un("hide", ml.hide,  this);
36897         }
36898     },
36899
36900     // private
36901     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
36902     onTriggerClick : function(){
36903         if(this.disabled){
36904             return;
36905         }
36906         if(this.menu == null){
36907             this.menu = new Roo.menu.DateMenu();
36908         }
36909         Roo.apply(this.menu.picker,  {
36910             showClear: this.allowBlank,
36911             minDate : this.minValue,
36912             maxDate : this.maxValue,
36913             disabledDatesRE : this.ddMatch,
36914             disabledDatesText : this.disabledDatesText,
36915             disabledDays : this.disabledDays,
36916             disabledDaysText : this.disabledDaysText,
36917             format : this.format,
36918             minText : String.format(this.minText, this.formatDate(this.minValue)),
36919             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
36920         });
36921         this.menu.on(Roo.apply({}, this.menuListeners, {
36922             scope:this
36923         }));
36924         this.menu.picker.setValue(this.getValue() || new Date());
36925         this.menu.show(this.el, "tl-bl?");
36926     },
36927
36928     beforeBlur : function(){
36929         var v = this.parseDate(this.getRawValue());
36930         if(v){
36931             this.setValue(v);
36932         }
36933     }
36934
36935     /** @cfg {Boolean} grow @hide */
36936     /** @cfg {Number} growMin @hide */
36937     /** @cfg {Number} growMax @hide */
36938     /**
36939      * @hide
36940      * @method autoSize
36941      */
36942 });/*
36943  * Based on:
36944  * Ext JS Library 1.1.1
36945  * Copyright(c) 2006-2007, Ext JS, LLC.
36946  *
36947  * Originally Released Under LGPL - original licence link has changed is not relivant.
36948  *
36949  * Fork - LGPL
36950  * <script type="text/javascript">
36951  */
36952  
36953
36954 /**
36955  * @class Roo.form.ComboBox
36956  * @extends Roo.form.TriggerField
36957  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
36958  * @constructor
36959  * Create a new ComboBox.
36960  * @param {Object} config Configuration options
36961  */
36962 Roo.form.ComboBox = function(config){
36963     Roo.form.ComboBox.superclass.constructor.call(this, config);
36964     this.addEvents({
36965         /**
36966          * @event expand
36967          * Fires when the dropdown list is expanded
36968              * @param {Roo.form.ComboBox} combo This combo box
36969              */
36970         'expand' : true,
36971         /**
36972          * @event collapse
36973          * Fires when the dropdown list is collapsed
36974              * @param {Roo.form.ComboBox} combo This combo box
36975              */
36976         'collapse' : true,
36977         /**
36978          * @event beforeselect
36979          * Fires before a list item is selected. Return false to cancel the selection.
36980              * @param {Roo.form.ComboBox} combo This combo box
36981              * @param {Roo.data.Record} record The data record returned from the underlying store
36982              * @param {Number} index The index of the selected item in the dropdown list
36983              */
36984         'beforeselect' : true,
36985         /**
36986          * @event select
36987          * Fires when a list item is selected
36988              * @param {Roo.form.ComboBox} combo This combo box
36989              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
36990              * @param {Number} index The index of the selected item in the dropdown list
36991              */
36992         'select' : true,
36993         /**
36994          * @event beforequery
36995          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
36996          * The event object passed has these properties:
36997              * @param {Roo.form.ComboBox} combo This combo box
36998              * @param {String} query The query
36999              * @param {Boolean} forceAll true to force "all" query
37000              * @param {Boolean} cancel true to cancel the query
37001              * @param {Object} e The query event object
37002              */
37003         'beforequery': true,
37004          /**
37005          * @event add
37006          * Fires when the 'add' icon is pressed (add a listener to enable add button)
37007              * @param {Roo.form.ComboBox} combo This combo box
37008              */
37009         'add' : true,
37010         /**
37011          * @event edit
37012          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
37013              * @param {Roo.form.ComboBox} combo This combo box
37014              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
37015              */
37016         'edit' : true
37017         
37018         
37019     });
37020     if(this.transform){
37021         this.allowDomMove = false;
37022         var s = Roo.getDom(this.transform);
37023         if(!this.hiddenName){
37024             this.hiddenName = s.name;
37025         }
37026         if(!this.store){
37027             this.mode = 'local';
37028             var d = [], opts = s.options;
37029             for(var i = 0, len = opts.length;i < len; i++){
37030                 var o = opts[i];
37031                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
37032                 if(o.selected) {
37033                     this.value = value;
37034                 }
37035                 d.push([value, o.text]);
37036             }
37037             this.store = new Roo.data.SimpleStore({
37038                 'id': 0,
37039                 fields: ['value', 'text'],
37040                 data : d
37041             });
37042             this.valueField = 'value';
37043             this.displayField = 'text';
37044         }
37045         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
37046         if(!this.lazyRender){
37047             this.target = true;
37048             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
37049             s.parentNode.removeChild(s); // remove it
37050             this.render(this.el.parentNode);
37051         }else{
37052             s.parentNode.removeChild(s); // remove it
37053         }
37054
37055     }
37056     if (this.store) {
37057         this.store = Roo.factory(this.store, Roo.data);
37058     }
37059     
37060     this.selectedIndex = -1;
37061     if(this.mode == 'local'){
37062         if(config.queryDelay === undefined){
37063             this.queryDelay = 10;
37064         }
37065         if(config.minChars === undefined){
37066             this.minChars = 0;
37067         }
37068     }
37069 };
37070
37071 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
37072     /**
37073      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
37074      */
37075     /**
37076      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
37077      * rendering into an Roo.Editor, defaults to false)
37078      */
37079     /**
37080      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
37081      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
37082      */
37083     /**
37084      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
37085      */
37086     /**
37087      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
37088      * the dropdown list (defaults to undefined, with no header element)
37089      */
37090
37091      /**
37092      * @cfg {String/Roo.Template} tpl The template to use to render the output
37093      */
37094      
37095     // private
37096     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
37097     /**
37098      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
37099      */
37100     listWidth: undefined,
37101     /**
37102      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
37103      * mode = 'remote' or 'text' if mode = 'local')
37104      */
37105     displayField: undefined,
37106     /**
37107      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
37108      * mode = 'remote' or 'value' if mode = 'local'). 
37109      * Note: use of a valueField requires the user make a selection
37110      * in order for a value to be mapped.
37111      */
37112     valueField: undefined,
37113     /**
37114      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
37115      * field's data value (defaults to the underlying DOM element's name)
37116      */
37117     hiddenName: undefined,
37118     /**
37119      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
37120      */
37121     listClass: '',
37122     /**
37123      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
37124      */
37125     selectedClass: 'x-combo-selected',
37126     /**
37127      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37128      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
37129      * which displays a downward arrow icon).
37130      */
37131     triggerClass : 'x-form-arrow-trigger',
37132     /**
37133      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
37134      */
37135     shadow:'sides',
37136     /**
37137      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
37138      * anchor positions (defaults to 'tl-bl')
37139      */
37140     listAlign: 'tl-bl?',
37141     /**
37142      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
37143      */
37144     maxHeight: 300,
37145     /**
37146      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
37147      * query specified by the allQuery config option (defaults to 'query')
37148      */
37149     triggerAction: 'query',
37150     /**
37151      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
37152      * (defaults to 4, does not apply if editable = false)
37153      */
37154     minChars : 4,
37155     /**
37156      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
37157      * delay (typeAheadDelay) if it matches a known value (defaults to false)
37158      */
37159     typeAhead: false,
37160     /**
37161      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
37162      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
37163      */
37164     queryDelay: 500,
37165     /**
37166      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
37167      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
37168      */
37169     pageSize: 0,
37170     /**
37171      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
37172      * when editable = true (defaults to false)
37173      */
37174     selectOnFocus:false,
37175     /**
37176      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
37177      */
37178     queryParam: 'query',
37179     /**
37180      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
37181      * when mode = 'remote' (defaults to 'Loading...')
37182      */
37183     loadingText: 'Loading...',
37184     /**
37185      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
37186      */
37187     resizable: false,
37188     /**
37189      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
37190      */
37191     handleHeight : 8,
37192     /**
37193      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
37194      * traditional select (defaults to true)
37195      */
37196     editable: true,
37197     /**
37198      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
37199      */
37200     allQuery: '',
37201     /**
37202      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
37203      */
37204     mode: 'remote',
37205     /**
37206      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
37207      * listWidth has a higher value)
37208      */
37209     minListWidth : 70,
37210     /**
37211      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
37212      * allow the user to set arbitrary text into the field (defaults to false)
37213      */
37214     forceSelection:false,
37215     /**
37216      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
37217      * if typeAhead = true (defaults to 250)
37218      */
37219     typeAheadDelay : 250,
37220     /**
37221      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
37222      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
37223      */
37224     valueNotFoundText : undefined,
37225     /**
37226      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
37227      */
37228     blockFocus : false,
37229     
37230     /**
37231      * @cfg {Boolean} disableClear Disable showing of clear button.
37232      */
37233     disableClear : false,
37234     /**
37235      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
37236      */
37237     alwaysQuery : false,
37238     
37239     //private
37240     addicon : false,
37241     editicon: false,
37242     
37243     
37244     // private
37245     onRender : function(ct, position){
37246         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
37247         if(this.hiddenName){
37248             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
37249                     'before', true);
37250             this.hiddenField.value =
37251                 this.hiddenValue !== undefined ? this.hiddenValue :
37252                 this.value !== undefined ? this.value : '';
37253
37254             // prevent input submission
37255             this.el.dom.removeAttribute('name');
37256         }
37257         if(Roo.isGecko){
37258             this.el.dom.setAttribute('autocomplete', 'off');
37259         }
37260
37261         var cls = 'x-combo-list';
37262
37263         this.list = new Roo.Layer({
37264             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37265         });
37266
37267         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
37268         this.list.setWidth(lw);
37269         this.list.swallowEvent('mousewheel');
37270         this.assetHeight = 0;
37271
37272         if(this.title){
37273             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
37274             this.assetHeight += this.header.getHeight();
37275         }
37276
37277         this.innerList = this.list.createChild({cls:cls+'-inner'});
37278         this.innerList.on('mouseover', this.onViewOver, this);
37279         this.innerList.on('mousemove', this.onViewMove, this);
37280         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37281         
37282         if(this.allowBlank && !this.pageSize && !this.disableClear){
37283             this.footer = this.list.createChild({cls:cls+'-ft'});
37284             this.pageTb = new Roo.Toolbar(this.footer);
37285            
37286         }
37287         if(this.pageSize){
37288             this.footer = this.list.createChild({cls:cls+'-ft'});
37289             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
37290                     {pageSize: this.pageSize});
37291             
37292         }
37293         
37294         if (this.pageTb && this.allowBlank && !this.disableClear) {
37295             var _this = this;
37296             this.pageTb.add(new Roo.Toolbar.Fill(), {
37297                 cls: 'x-btn-icon x-btn-clear',
37298                 text: '&#160;',
37299                 handler: function()
37300                 {
37301                     _this.collapse();
37302                     _this.clearValue();
37303                     _this.onSelect(false, -1);
37304                 }
37305             });
37306         }
37307         if (this.footer) {
37308             this.assetHeight += this.footer.getHeight();
37309         }
37310         
37311
37312         if(!this.tpl){
37313             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
37314         }
37315
37316         this.view = new Roo.View(this.innerList, this.tpl, {
37317             singleSelect:true, store: this.store, selectedClass: this.selectedClass
37318         });
37319
37320         this.view.on('click', this.onViewClick, this);
37321
37322         this.store.on('beforeload', this.onBeforeLoad, this);
37323         this.store.on('load', this.onLoad, this);
37324         this.store.on('loadexception', this.collapse, this);
37325
37326         if(this.resizable){
37327             this.resizer = new Roo.Resizable(this.list,  {
37328                pinned:true, handles:'se'
37329             });
37330             this.resizer.on('resize', function(r, w, h){
37331                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
37332                 this.listWidth = w;
37333                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
37334                 this.restrictHeight();
37335             }, this);
37336             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
37337         }
37338         if(!this.editable){
37339             this.editable = true;
37340             this.setEditable(false);
37341         }  
37342         
37343         
37344         if (typeof(this.events.add.listeners) != 'undefined') {
37345             
37346             this.addicon = this.wrap.createChild(
37347                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
37348        
37349             this.addicon.on('click', function(e) {
37350                 this.fireEvent('add', this);
37351             }, this);
37352         }
37353         if (typeof(this.events.edit.listeners) != 'undefined') {
37354             
37355             this.editicon = this.wrap.createChild(
37356                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
37357             if (this.addicon) {
37358                 this.editicon.setStyle('margin-left', '40px');
37359             }
37360             this.editicon.on('click', function(e) {
37361                 
37362                 // we fire even  if inothing is selected..
37363                 this.fireEvent('edit', this, this.lastData );
37364                 
37365             }, this);
37366         }
37367         
37368         
37369         
37370     },
37371
37372     // private
37373     initEvents : function(){
37374         Roo.form.ComboBox.superclass.initEvents.call(this);
37375
37376         this.keyNav = new Roo.KeyNav(this.el, {
37377             "up" : function(e){
37378                 this.inKeyMode = true;
37379                 this.selectPrev();
37380             },
37381
37382             "down" : function(e){
37383                 if(!this.isExpanded()){
37384                     this.onTriggerClick();
37385                 }else{
37386                     this.inKeyMode = true;
37387                     this.selectNext();
37388                 }
37389             },
37390
37391             "enter" : function(e){
37392                 this.onViewClick();
37393                 //return true;
37394             },
37395
37396             "esc" : function(e){
37397                 this.collapse();
37398             },
37399
37400             "tab" : function(e){
37401                 this.onViewClick(false);
37402                 return true;
37403             },
37404
37405             scope : this,
37406
37407             doRelay : function(foo, bar, hname){
37408                 if(hname == 'down' || this.scope.isExpanded()){
37409                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
37410                 }
37411                 return true;
37412             },
37413
37414             forceKeyDown: true
37415         });
37416         this.queryDelay = Math.max(this.queryDelay || 10,
37417                 this.mode == 'local' ? 10 : 250);
37418         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
37419         if(this.typeAhead){
37420             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
37421         }
37422         if(this.editable !== false){
37423             this.el.on("keyup", this.onKeyUp, this);
37424         }
37425         if(this.forceSelection){
37426             this.on('blur', this.doForce, this);
37427         }
37428     },
37429
37430     onDestroy : function(){
37431         if(this.view){
37432             this.view.setStore(null);
37433             this.view.el.removeAllListeners();
37434             this.view.el.remove();
37435             this.view.purgeListeners();
37436         }
37437         if(this.list){
37438             this.list.destroy();
37439         }
37440         if(this.store){
37441             this.store.un('beforeload', this.onBeforeLoad, this);
37442             this.store.un('load', this.onLoad, this);
37443             this.store.un('loadexception', this.collapse, this);
37444         }
37445         Roo.form.ComboBox.superclass.onDestroy.call(this);
37446     },
37447
37448     // private
37449     fireKey : function(e){
37450         if(e.isNavKeyPress() && !this.list.isVisible()){
37451             this.fireEvent("specialkey", this, e);
37452         }
37453     },
37454
37455     // private
37456     onResize: function(w, h){
37457         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
37458         
37459         if(typeof w != 'number'){
37460             // we do not handle it!?!?
37461             return;
37462         }
37463         var tw = this.trigger.getWidth();
37464         tw += this.addicon ? this.addicon.getWidth() : 0;
37465         tw += this.editicon ? this.editicon.getWidth() : 0;
37466         var x = w - tw;
37467         this.el.setWidth( this.adjustWidth('input', x));
37468             
37469         this.trigger.setStyle('left', x+'px');
37470         
37471         if(this.list && this.listWidth === undefined){
37472             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
37473             this.list.setWidth(lw);
37474             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
37475         }
37476         
37477     
37478         
37479     },
37480
37481     /**
37482      * Allow or prevent the user from directly editing the field text.  If false is passed,
37483      * the user will only be able to select from the items defined in the dropdown list.  This method
37484      * is the runtime equivalent of setting the 'editable' config option at config time.
37485      * @param {Boolean} value True to allow the user to directly edit the field text
37486      */
37487     setEditable : function(value){
37488         if(value == this.editable){
37489             return;
37490         }
37491         this.editable = value;
37492         if(!value){
37493             this.el.dom.setAttribute('readOnly', true);
37494             this.el.on('mousedown', this.onTriggerClick,  this);
37495             this.el.addClass('x-combo-noedit');
37496         }else{
37497             this.el.dom.setAttribute('readOnly', false);
37498             this.el.un('mousedown', this.onTriggerClick,  this);
37499             this.el.removeClass('x-combo-noedit');
37500         }
37501     },
37502
37503     // private
37504     onBeforeLoad : function(){
37505         if(!this.hasFocus){
37506             return;
37507         }
37508         this.innerList.update(this.loadingText ?
37509                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
37510         this.restrictHeight();
37511         this.selectedIndex = -1;
37512     },
37513
37514     // private
37515     onLoad : function(){
37516         if(!this.hasFocus){
37517             return;
37518         }
37519         if(this.store.getCount() > 0){
37520             this.expand();
37521             this.restrictHeight();
37522             if(this.lastQuery == this.allQuery){
37523                 if(this.editable){
37524                     this.el.dom.select();
37525                 }
37526                 if(!this.selectByValue(this.value, true)){
37527                     this.select(0, true);
37528                 }
37529             }else{
37530                 this.selectNext();
37531                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
37532                     this.taTask.delay(this.typeAheadDelay);
37533                 }
37534             }
37535         }else{
37536             this.onEmptyResults();
37537         }
37538         //this.el.focus();
37539     },
37540
37541     // private
37542     onTypeAhead : function(){
37543         if(this.store.getCount() > 0){
37544             var r = this.store.getAt(0);
37545             var newValue = r.data[this.displayField];
37546             var len = newValue.length;
37547             var selStart = this.getRawValue().length;
37548             if(selStart != len){
37549                 this.setRawValue(newValue);
37550                 this.selectText(selStart, newValue.length);
37551             }
37552         }
37553     },
37554
37555     // private
37556     onSelect : function(record, index){
37557         if(this.fireEvent('beforeselect', this, record, index) !== false){
37558             this.setFromData(index > -1 ? record.data : false);
37559             this.collapse();
37560             this.fireEvent('select', this, record, index);
37561         }
37562     },
37563
37564     /**
37565      * Returns the currently selected field value or empty string if no value is set.
37566      * @return {String} value The selected value
37567      */
37568     getValue : function(){
37569         if(this.valueField){
37570             return typeof this.value != 'undefined' ? this.value : '';
37571         }else{
37572             return Roo.form.ComboBox.superclass.getValue.call(this);
37573         }
37574     },
37575
37576     /**
37577      * Clears any text/value currently set in the field
37578      */
37579     clearValue : function(){
37580         if(this.hiddenField){
37581             this.hiddenField.value = '';
37582         }
37583         this.value = '';
37584         this.setRawValue('');
37585         this.lastSelectionText = '';
37586         this.applyEmptyText();
37587     },
37588
37589     /**
37590      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
37591      * will be displayed in the field.  If the value does not match the data value of an existing item,
37592      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
37593      * Otherwise the field will be blank (although the value will still be set).
37594      * @param {String} value The value to match
37595      */
37596     setValue : function(v){
37597         var text = v;
37598         if(this.valueField){
37599             var r = this.findRecord(this.valueField, v);
37600             if(r){
37601                 text = r.data[this.displayField];
37602             }else if(this.valueNotFoundText !== undefined){
37603                 text = this.valueNotFoundText;
37604             }
37605         }
37606         this.lastSelectionText = text;
37607         if(this.hiddenField){
37608             this.hiddenField.value = v;
37609         }
37610         Roo.form.ComboBox.superclass.setValue.call(this, text);
37611         this.value = v;
37612     },
37613     /**
37614      * @property {Object} the last set data for the element
37615      */
37616     
37617     lastData : false,
37618     /**
37619      * Sets the value of the field based on a object which is related to the record format for the store.
37620      * @param {Object} value the value to set as. or false on reset?
37621      */
37622     setFromData : function(o){
37623         var dv = ''; // display value
37624         var vv = ''; // value value..
37625         this.lastData = o;
37626         if (this.displayField) {
37627             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
37628         } else {
37629             // this is an error condition!!!
37630             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
37631         }
37632         
37633         if(this.valueField){
37634             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
37635         }
37636         if(this.hiddenField){
37637             this.hiddenField.value = vv;
37638             
37639             this.lastSelectionText = dv;
37640             Roo.form.ComboBox.superclass.setValue.call(this, dv);
37641             this.value = vv;
37642             return;
37643         }
37644         // no hidden field.. - we store the value in 'value', but still display
37645         // display field!!!!
37646         this.lastSelectionText = dv;
37647         Roo.form.ComboBox.superclass.setValue.call(this, dv);
37648         this.value = vv;
37649         
37650         
37651     },
37652     // private
37653     reset : function(){
37654         // overridden so that last data is reset..
37655         this.setValue(this.originalValue);
37656         this.clearInvalid();
37657         this.lastData = false;
37658     },
37659     // private
37660     findRecord : function(prop, value){
37661         var record;
37662         if(this.store.getCount() > 0){
37663             this.store.each(function(r){
37664                 if(r.data[prop] == value){
37665                     record = r;
37666                     return false;
37667                 }
37668             });
37669         }
37670         return record;
37671     },
37672
37673     // private
37674     onViewMove : function(e, t){
37675         this.inKeyMode = false;
37676     },
37677
37678     // private
37679     onViewOver : function(e, t){
37680         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
37681             return;
37682         }
37683         var item = this.view.findItemFromChild(t);
37684         if(item){
37685             var index = this.view.indexOf(item);
37686             this.select(index, false);
37687         }
37688     },
37689
37690     // private
37691     onViewClick : function(doFocus){
37692         var index = this.view.getSelectedIndexes()[0];
37693         var r = this.store.getAt(index);
37694         if(r){
37695             this.onSelect(r, index);
37696         }
37697         if(doFocus !== false && !this.blockFocus){
37698             this.el.focus();
37699         }
37700     },
37701
37702     // private
37703     restrictHeight : function(){
37704         this.innerList.dom.style.height = '';
37705         var inner = this.innerList.dom;
37706         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
37707         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
37708         this.list.beginUpdate();
37709         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
37710         this.list.alignTo(this.el, this.listAlign);
37711         this.list.endUpdate();
37712     },
37713
37714     // private
37715     onEmptyResults : function(){
37716         this.collapse();
37717     },
37718
37719     /**
37720      * Returns true if the dropdown list is expanded, else false.
37721      */
37722     isExpanded : function(){
37723         return this.list.isVisible();
37724     },
37725
37726     /**
37727      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
37728      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37729      * @param {String} value The data value of the item to select
37730      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37731      * selected item if it is not currently in view (defaults to true)
37732      * @return {Boolean} True if the value matched an item in the list, else false
37733      */
37734     selectByValue : function(v, scrollIntoView){
37735         if(v !== undefined && v !== null){
37736             var r = this.findRecord(this.valueField || this.displayField, v);
37737             if(r){
37738                 this.select(this.store.indexOf(r), scrollIntoView);
37739                 return true;
37740             }
37741         }
37742         return false;
37743     },
37744
37745     /**
37746      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
37747      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
37748      * @param {Number} index The zero-based index of the list item to select
37749      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
37750      * selected item if it is not currently in view (defaults to true)
37751      */
37752     select : function(index, scrollIntoView){
37753         this.selectedIndex = index;
37754         this.view.select(index);
37755         if(scrollIntoView !== false){
37756             var el = this.view.getNode(index);
37757             if(el){
37758                 this.innerList.scrollChildIntoView(el, false);
37759             }
37760         }
37761     },
37762
37763     // private
37764     selectNext : function(){
37765         var ct = this.store.getCount();
37766         if(ct > 0){
37767             if(this.selectedIndex == -1){
37768                 this.select(0);
37769             }else if(this.selectedIndex < ct-1){
37770                 this.select(this.selectedIndex+1);
37771             }
37772         }
37773     },
37774
37775     // private
37776     selectPrev : function(){
37777         var ct = this.store.getCount();
37778         if(ct > 0){
37779             if(this.selectedIndex == -1){
37780                 this.select(0);
37781             }else if(this.selectedIndex != 0){
37782                 this.select(this.selectedIndex-1);
37783             }
37784         }
37785     },
37786
37787     // private
37788     onKeyUp : function(e){
37789         if(this.editable !== false && !e.isSpecialKey()){
37790             this.lastKey = e.getKey();
37791             this.dqTask.delay(this.queryDelay);
37792         }
37793     },
37794
37795     // private
37796     validateBlur : function(){
37797         return !this.list || !this.list.isVisible();   
37798     },
37799
37800     // private
37801     initQuery : function(){
37802         this.doQuery(this.getRawValue());
37803     },
37804
37805     // private
37806     doForce : function(){
37807         if(this.el.dom.value.length > 0){
37808             this.el.dom.value =
37809                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
37810             this.applyEmptyText();
37811         }
37812     },
37813
37814     /**
37815      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
37816      * query allowing the query action to be canceled if needed.
37817      * @param {String} query The SQL query to execute
37818      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
37819      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
37820      * saved in the current store (defaults to false)
37821      */
37822     doQuery : function(q, forceAll){
37823         if(q === undefined || q === null){
37824             q = '';
37825         }
37826         var qe = {
37827             query: q,
37828             forceAll: forceAll,
37829             combo: this,
37830             cancel:false
37831         };
37832         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
37833             return false;
37834         }
37835         q = qe.query;
37836         forceAll = qe.forceAll;
37837         if(forceAll === true || (q.length >= this.minChars)){
37838             if(this.lastQuery != q || this.alwaysQuery){
37839                 this.lastQuery = q;
37840                 if(this.mode == 'local'){
37841                     this.selectedIndex = -1;
37842                     if(forceAll){
37843                         this.store.clearFilter();
37844                     }else{
37845                         this.store.filter(this.displayField, q);
37846                     }
37847                     this.onLoad();
37848                 }else{
37849                     this.store.baseParams[this.queryParam] = q;
37850                     this.store.load({
37851                         params: this.getParams(q)
37852                     });
37853                     this.expand();
37854                 }
37855             }else{
37856                 this.selectedIndex = -1;
37857                 this.onLoad();   
37858             }
37859         }
37860     },
37861
37862     // private
37863     getParams : function(q){
37864         var p = {};
37865         //p[this.queryParam] = q;
37866         if(this.pageSize){
37867             p.start = 0;
37868             p.limit = this.pageSize;
37869         }
37870         return p;
37871     },
37872
37873     /**
37874      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
37875      */
37876     collapse : function(){
37877         if(!this.isExpanded()){
37878             return;
37879         }
37880         this.list.hide();
37881         Roo.get(document).un('mousedown', this.collapseIf, this);
37882         Roo.get(document).un('mousewheel', this.collapseIf, this);
37883         if (!this.editable) {
37884             Roo.get(document).un('keydown', this.listKeyPress, this);
37885         }
37886         this.fireEvent('collapse', this);
37887     },
37888
37889     // private
37890     collapseIf : function(e){
37891         if(!e.within(this.wrap) && !e.within(this.list)){
37892             this.collapse();
37893         }
37894     },
37895
37896     /**
37897      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
37898      */
37899     expand : function(){
37900         if(this.isExpanded() || !this.hasFocus){
37901             return;
37902         }
37903         this.list.alignTo(this.el, this.listAlign);
37904         this.list.show();
37905         Roo.get(document).on('mousedown', this.collapseIf, this);
37906         Roo.get(document).on('mousewheel', this.collapseIf, this);
37907         if (!this.editable) {
37908             Roo.get(document).on('keydown', this.listKeyPress, this);
37909         }
37910         
37911         this.fireEvent('expand', this);
37912     },
37913
37914     // private
37915     // Implements the default empty TriggerField.onTriggerClick function
37916     onTriggerClick : function(){
37917         if(this.disabled){
37918             return;
37919         }
37920         if(this.isExpanded()){
37921             this.collapse();
37922             if (!this.blockFocus) {
37923                 this.el.focus();
37924             }
37925             
37926         }else {
37927             this.hasFocus = true;
37928             if(this.triggerAction == 'all') {
37929                 this.doQuery(this.allQuery, true);
37930             } else {
37931                 this.doQuery(this.getRawValue());
37932             }
37933             if (!this.blockFocus) {
37934                 this.el.focus();
37935             }
37936         }
37937     },
37938     listKeyPress : function(e)
37939     {
37940         //Roo.log('listkeypress');
37941         // scroll to first matching element based on key pres..
37942         if (e.isSpecialKey()) {
37943             return false;
37944         }
37945         var k = String.fromCharCode(e.getKey()).toUpperCase();
37946         //Roo.log(k);
37947         var match  = false;
37948         var csel = this.view.getSelectedNodes();
37949         var cselitem = false;
37950         if (csel.length) {
37951             var ix = this.view.indexOf(csel[0]);
37952             cselitem  = this.store.getAt(ix);
37953             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
37954                 cselitem = false;
37955             }
37956             
37957         }
37958         
37959         this.store.each(function(v) { 
37960             if (cselitem) {
37961                 // start at existing selection.
37962                 if (cselitem.id == v.id) {
37963                     cselitem = false;
37964                 }
37965                 return;
37966             }
37967                 
37968             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
37969                 match = this.store.indexOf(v);
37970                 return false;
37971             }
37972         }, this);
37973         
37974         if (match === false) {
37975             return true; // no more action?
37976         }
37977         // scroll to?
37978         this.view.select(match);
37979         var sn = Roo.get(this.view.getSelectedNodes()[0])
37980         sn.scrollIntoView(sn.dom.parentNode, false);
37981     }
37982
37983     /** 
37984     * @cfg {Boolean} grow 
37985     * @hide 
37986     */
37987     /** 
37988     * @cfg {Number} growMin 
37989     * @hide 
37990     */
37991     /** 
37992     * @cfg {Number} growMax 
37993     * @hide 
37994     */
37995     /**
37996      * @hide
37997      * @method autoSize
37998      */
37999 });/*
38000  * Based on:
38001  * Ext JS Library 1.1.1
38002  * Copyright(c) 2006-2007, Ext JS, LLC.
38003  *
38004  * Originally Released Under LGPL - original licence link has changed is not relivant.
38005  *
38006  * Fork - LGPL
38007  * <script type="text/javascript">
38008  */
38009 /**
38010  * @class Roo.form.Checkbox
38011  * @extends Roo.form.Field
38012  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
38013  * @constructor
38014  * Creates a new Checkbox
38015  * @param {Object} config Configuration options
38016  */
38017 Roo.form.Checkbox = function(config){
38018     Roo.form.Checkbox.superclass.constructor.call(this, config);
38019     this.addEvents({
38020         /**
38021          * @event check
38022          * Fires when the checkbox is checked or unchecked.
38023              * @param {Roo.form.Checkbox} this This checkbox
38024              * @param {Boolean} checked The new checked value
38025              */
38026         check : true
38027     });
38028 };
38029
38030 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
38031     /**
38032      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
38033      */
38034     focusClass : undefined,
38035     /**
38036      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
38037      */
38038     fieldClass: "x-form-field",
38039     /**
38040      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
38041      */
38042     checked: false,
38043     /**
38044      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38045      * {tag: "input", type: "checkbox", autocomplete: "off"})
38046      */
38047     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
38048     /**
38049      * @cfg {String} boxLabel The text that appears beside the checkbox
38050      */
38051     boxLabel : "",
38052     /**
38053      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
38054      */  
38055     inputValue : '1',
38056     /**
38057      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
38058      */
38059      valueOff: '0', // value when not checked..
38060
38061     actionMode : 'viewEl', 
38062     //
38063     // private
38064     itemCls : 'x-menu-check-item x-form-item',
38065     groupClass : 'x-menu-group-item',
38066     inputType : 'hidden',
38067     
38068     
38069     inSetChecked: false, // check that we are not calling self...
38070     
38071     inputElement: false, // real input element?
38072     basedOn: false, // ????
38073     
38074     isFormField: true, // not sure where this is needed!!!!
38075
38076     onResize : function(){
38077         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
38078         if(!this.boxLabel){
38079             this.el.alignTo(this.wrap, 'c-c');
38080         }
38081     },
38082
38083     initEvents : function(){
38084         Roo.form.Checkbox.superclass.initEvents.call(this);
38085         this.el.on("click", this.onClick,  this);
38086         this.el.on("change", this.onClick,  this);
38087     },
38088
38089
38090     getResizeEl : function(){
38091         return this.wrap;
38092     },
38093
38094     getPositionEl : function(){
38095         return this.wrap;
38096     },
38097
38098     // private
38099     onRender : function(ct, position){
38100         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
38101         /*
38102         if(this.inputValue !== undefined){
38103             this.el.dom.value = this.inputValue;
38104         }
38105         */
38106         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
38107         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
38108         var viewEl = this.wrap.createChild({ 
38109             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
38110         this.viewEl = viewEl;   
38111         this.wrap.on('click', this.onClick,  this); 
38112         
38113         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
38114         this.el.on('propertychange', this.setFromHidden,  this);  //ie
38115         
38116         
38117         
38118         if(this.boxLabel){
38119             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
38120         //    viewEl.on('click', this.onClick,  this); 
38121         }
38122         //if(this.checked){
38123             this.setChecked(this.checked);
38124         //}else{
38125             //this.checked = this.el.dom;
38126         //}
38127
38128     },
38129
38130     // private
38131     initValue : Roo.emptyFn,
38132
38133     /**
38134      * Returns the checked state of the checkbox.
38135      * @return {Boolean} True if checked, else false
38136      */
38137     getValue : function(){
38138         if(this.el){
38139             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
38140         }
38141         return this.valueOff;
38142         
38143     },
38144
38145         // private
38146     onClick : function(){ 
38147         this.setChecked(!this.checked);
38148
38149         //if(this.el.dom.checked != this.checked){
38150         //    this.setValue(this.el.dom.checked);
38151        // }
38152     },
38153
38154     /**
38155      * Sets the checked state of the checkbox.
38156      * On is always based on a string comparison between inputValue and the param.
38157      * @param {Boolean/String} value - the value to set 
38158      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
38159      */
38160     setValue : function(v,suppressEvent){
38161         
38162         
38163         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
38164         //if(this.el && this.el.dom){
38165         //    this.el.dom.checked = this.checked;
38166         //    this.el.dom.defaultChecked = this.checked;
38167         //}
38168         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
38169         //this.fireEvent("check", this, this.checked);
38170     },
38171     // private..
38172     setChecked : function(state,suppressEvent)
38173     {
38174         if (this.inSetChecked) {
38175             this.checked = state;
38176             return;
38177         }
38178         
38179     
38180         if(this.wrap){
38181             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
38182         }
38183         this.checked = state;
38184         if(suppressEvent !== true){
38185             this.fireEvent('check', this, state);
38186         }
38187         this.inSetChecked = true;
38188         this.el.dom.value = state ? this.inputValue : this.valueOff;
38189         this.inSetChecked = false;
38190         
38191     },
38192     // handle setting of hidden value by some other method!!?!?
38193     setFromHidden: function()
38194     {
38195         if(!this.el){
38196             return;
38197         }
38198         //console.log("SET FROM HIDDEN");
38199         //alert('setFrom hidden');
38200         this.setValue(this.el.dom.value);
38201     },
38202     
38203     onDestroy : function()
38204     {
38205         if(this.viewEl){
38206             Roo.get(this.viewEl).remove();
38207         }
38208          
38209         Roo.form.Checkbox.superclass.onDestroy.call(this);
38210     }
38211
38212 });/*
38213  * Based on:
38214  * Ext JS Library 1.1.1
38215  * Copyright(c) 2006-2007, Ext JS, LLC.
38216  *
38217  * Originally Released Under LGPL - original licence link has changed is not relivant.
38218  *
38219  * Fork - LGPL
38220  * <script type="text/javascript">
38221  */
38222  
38223 /**
38224  * @class Roo.form.Radio
38225  * @extends Roo.form.Checkbox
38226  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
38227  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
38228  * @constructor
38229  * Creates a new Radio
38230  * @param {Object} config Configuration options
38231  */
38232 Roo.form.Radio = function(){
38233     Roo.form.Radio.superclass.constructor.apply(this, arguments);
38234 };
38235 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
38236     inputType: 'radio',
38237
38238     /**
38239      * If this radio is part of a group, it will return the selected value
38240      * @return {String}
38241      */
38242     getGroupValue : function(){
38243         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
38244     }
38245 });//<script type="text/javascript">
38246
38247 /*
38248  * Ext JS Library 1.1.1
38249  * Copyright(c) 2006-2007, Ext JS, LLC.
38250  * licensing@extjs.com
38251  * 
38252  * http://www.extjs.com/license
38253  */
38254  
38255  /*
38256   * 
38257   * Known bugs:
38258   * Default CSS appears to render it as fixed text by default (should really be Sans-Serif)
38259   * - IE ? - no idea how much works there.
38260   * 
38261   * 
38262   * 
38263   */
38264  
38265
38266 /**
38267  * @class Ext.form.HtmlEditor
38268  * @extends Ext.form.Field
38269  * Provides a lightweight HTML Editor component.
38270  * WARNING - THIS CURRENTlY ONLY WORKS ON FIREFOX - USE FCKeditor for a cross platform version
38271  * 
38272  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
38273  * supported by this editor.</b><br/><br/>
38274  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
38275  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
38276  */
38277 Roo.form.HtmlEditor = Roo.extend(Roo.form.Field, {
38278       /**
38279      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
38280      */
38281     toolbars : false,
38282     /**
38283      * @cfg {String} createLinkText The default text for the create link prompt
38284      */
38285     createLinkText : 'Please enter the URL for the link:',
38286     /**
38287      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
38288      */
38289     defaultLinkValue : 'http:/'+'/',
38290    
38291     
38292     // id of frame..
38293     frameId: false,
38294     
38295     // private properties
38296     validationEvent : false,
38297     deferHeight: true,
38298     initialized : false,
38299     activated : false,
38300     sourceEditMode : false,
38301     onFocus : Roo.emptyFn,
38302     iframePad:3,
38303     hideMode:'offsets',
38304     defaultAutoCreate : {
38305         tag: "textarea",
38306         style:"width:500px;height:300px;",
38307         autocomplete: "off"
38308     },
38309
38310     // private
38311     initComponent : function(){
38312         this.addEvents({
38313             /**
38314              * @event initialize
38315              * Fires when the editor is fully initialized (including the iframe)
38316              * @param {HtmlEditor} this
38317              */
38318             initialize: true,
38319             /**
38320              * @event activate
38321              * Fires when the editor is first receives the focus. Any insertion must wait
38322              * until after this event.
38323              * @param {HtmlEditor} this
38324              */
38325             activate: true,
38326              /**
38327              * @event beforesync
38328              * Fires before the textarea is updated with content from the editor iframe. Return false
38329              * to cancel the sync.
38330              * @param {HtmlEditor} this
38331              * @param {String} html
38332              */
38333             beforesync: true,
38334              /**
38335              * @event beforepush
38336              * Fires before the iframe editor is updated with content from the textarea. Return false
38337              * to cancel the push.
38338              * @param {HtmlEditor} this
38339              * @param {String} html
38340              */
38341             beforepush: true,
38342              /**
38343              * @event sync
38344              * Fires when the textarea is updated with content from the editor iframe.
38345              * @param {HtmlEditor} this
38346              * @param {String} html
38347              */
38348             sync: true,
38349              /**
38350              * @event push
38351              * Fires when the iframe editor is updated with content from the textarea.
38352              * @param {HtmlEditor} this
38353              * @param {String} html
38354              */
38355             push: true,
38356              /**
38357              * @event editmodechange
38358              * Fires when the editor switches edit modes
38359              * @param {HtmlEditor} this
38360              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
38361              */
38362             editmodechange: true,
38363             /**
38364              * @event editorevent
38365              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
38366              * @param {HtmlEditor} this
38367              */
38368             editorevent: true
38369         })
38370     },
38371
38372     /**
38373      * Protected method that will not generally be called directly. It
38374      * is called when the editor creates its toolbar. Override this method if you need to
38375      * add custom toolbar buttons.
38376      * @param {HtmlEditor} editor
38377      */
38378     createToolbar : function(editor){
38379         if (!editor.toolbars || !editor.toolbars.length) {
38380             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
38381         }
38382         
38383         for (var i =0 ; i < editor.toolbars.length;i++) {
38384             editor.toolbars[i] = Roo.factory(editor.toolbars[i], Roo.form.HtmlEditor);
38385             editor.toolbars[i].init(editor);
38386         }
38387          
38388         
38389     },
38390
38391     /**
38392      * Protected method that will not generally be called directly. It
38393      * is called when the editor initializes the iframe with HTML contents. Override this method if you
38394      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
38395      */
38396     getDocMarkup : function(){
38397         return '<html><head><style type="text/css">body{border:0;margin:0;padding:3px;height:98%;cursor:text;}</style></head><body></body></html>';
38398     },
38399
38400     // private
38401     onRender : function(ct, position){
38402         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
38403         this.el.dom.style.border = '0 none';
38404         this.el.dom.setAttribute('tabIndex', -1);
38405         this.el.addClass('x-hidden');
38406         if(Roo.isIE){ // fix IE 1px bogus margin
38407             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
38408         }
38409         this.wrap = this.el.wrap({
38410             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
38411         });
38412
38413         this.frameId = Roo.id();
38414         this.createToolbar(this);
38415         
38416         
38417         
38418         
38419       
38420         
38421         var iframe = this.wrap.createChild({
38422             tag: 'iframe',
38423             id: this.frameId,
38424             name: this.frameId,
38425             frameBorder : 'no',
38426             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
38427         });
38428         
38429        // console.log(iframe);
38430         //this.wrap.dom.appendChild(iframe);
38431
38432         this.iframe = iframe.dom;
38433
38434          this.assignDocWin();
38435         
38436         this.doc.designMode = 'on';
38437        
38438         this.doc.open();
38439         this.doc.write(this.getDocMarkup());
38440         this.doc.close();
38441
38442         
38443         var task = { // must defer to wait for browser to be ready
38444             run : function(){
38445                 //console.log("run task?" + this.doc.readyState);
38446                 this.assignDocWin();
38447                 if(this.doc.body || this.doc.readyState == 'complete'){
38448                     try {
38449                         this.doc.designMode="on";
38450                     } catch (e) {
38451                         return;
38452                     }
38453                     Roo.TaskMgr.stop(task);
38454                     this.initEditor.defer(10, this);
38455                 }
38456             },
38457             interval : 10,
38458             duration:10000,
38459             scope: this
38460         };
38461         Roo.TaskMgr.start(task);
38462
38463         if(!this.width){
38464             this.setSize(this.el.getSize());
38465         }
38466     },
38467
38468     // private
38469     onResize : function(w, h){
38470         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
38471         if(this.el && this.iframe){
38472             if(typeof w == 'number'){
38473                 var aw = w - this.wrap.getFrameWidth('lr');
38474                 this.el.setWidth(this.adjustWidth('textarea', aw));
38475                 this.iframe.style.width = aw + 'px';
38476             }
38477             if(typeof h == 'number'){
38478                 var tbh = 0;
38479                 for (var i =0; i < this.toolbars.length;i++) {
38480                     // fixme - ask toolbars for heights?
38481                     tbh += this.toolbars[i].tb.el.getHeight();
38482                 }
38483                 
38484                 
38485                 
38486                 
38487                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
38488                 this.el.setHeight(this.adjustWidth('textarea', ah));
38489                 this.iframe.style.height = ah + 'px';
38490                 if(this.doc){
38491                     (this.doc.body || this.doc.documentElement).style.height = (ah - (this.iframePad*2)) + 'px';
38492                 }
38493             }
38494         }
38495     },
38496
38497     /**
38498      * Toggles the editor between standard and source edit mode.
38499      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
38500      */
38501     toggleSourceEdit : function(sourceEditMode){
38502         
38503         this.sourceEditMode = sourceEditMode === true;
38504         
38505         if(this.sourceEditMode){
38506           
38507             this.syncValue();
38508             this.iframe.className = 'x-hidden';
38509             this.el.removeClass('x-hidden');
38510             this.el.dom.removeAttribute('tabIndex');
38511             this.el.focus();
38512         }else{
38513              
38514             this.pushValue();
38515             this.iframe.className = '';
38516             this.el.addClass('x-hidden');
38517             this.el.dom.setAttribute('tabIndex', -1);
38518             this.deferFocus();
38519         }
38520         this.setSize(this.wrap.getSize());
38521         this.fireEvent('editmodechange', this, this.sourceEditMode);
38522     },
38523
38524     // private used internally
38525     createLink : function(){
38526         var url = prompt(this.createLinkText, this.defaultLinkValue);
38527         if(url && url != 'http:/'+'/'){
38528             this.relayCmd('createlink', url);
38529         }
38530     },
38531
38532     // private (for BoxComponent)
38533     adjustSize : Roo.BoxComponent.prototype.adjustSize,
38534
38535     // private (for BoxComponent)
38536     getResizeEl : function(){
38537         return this.wrap;
38538     },
38539
38540     // private (for BoxComponent)
38541     getPositionEl : function(){
38542         return this.wrap;
38543     },
38544
38545     // private
38546     initEvents : function(){
38547         this.originalValue = this.getValue();
38548     },
38549
38550     /**
38551      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38552      * @method
38553      */
38554     markInvalid : Roo.emptyFn,
38555     /**
38556      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
38557      * @method
38558      */
38559     clearInvalid : Roo.emptyFn,
38560
38561     setValue : function(v){
38562         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
38563         this.pushValue();
38564     },
38565
38566     /**
38567      * Protected method that will not generally be called directly. If you need/want
38568      * custom HTML cleanup, this is the method you should override.
38569      * @param {String} html The HTML to be cleaned
38570      * return {String} The cleaned HTML
38571      */
38572     cleanHtml : function(html){
38573         html = String(html);
38574         if(html.length > 5){
38575             if(Roo.isSafari){ // strip safari nonsense
38576                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
38577             }
38578         }
38579         if(html == '&nbsp;'){
38580             html = '';
38581         }
38582         return html;
38583     },
38584
38585     /**
38586      * Protected method that will not generally be called directly. Syncs the contents
38587      * of the editor iframe with the textarea.
38588      */
38589     syncValue : function(){
38590         if(this.initialized){
38591             var bd = (this.doc.body || this.doc.documentElement);
38592             var html = bd.innerHTML;
38593             if(Roo.isSafari){
38594                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
38595                 var m = bs.match(/text-align:(.*?);/i);
38596                 if(m && m[1]){
38597                     html = '<div style="'+m[0]+'">' + html + '</div>';
38598                 }
38599             }
38600             html = this.cleanHtml(html);
38601             if(this.fireEvent('beforesync', this, html) !== false){
38602                 this.el.dom.value = html;
38603                 this.fireEvent('sync', this, html);
38604             }
38605         }
38606     },
38607
38608     /**
38609      * Protected method that will not generally be called directly. Pushes the value of the textarea
38610      * into the iframe editor.
38611      */
38612     pushValue : function(){
38613         if(this.initialized){
38614             var v = this.el.dom.value;
38615             if(v.length < 1){
38616                 v = '&#160;';
38617             }
38618             if(this.fireEvent('beforepush', this, v) !== false){
38619                 (this.doc.body || this.doc.documentElement).innerHTML = v;
38620                 this.fireEvent('push', this, v);
38621             }
38622         }
38623     },
38624
38625     // private
38626     deferFocus : function(){
38627         this.focus.defer(10, this);
38628     },
38629
38630     // doc'ed in Field
38631     focus : function(){
38632         if(this.win && !this.sourceEditMode){
38633             this.win.focus();
38634         }else{
38635             this.el.focus();
38636         }
38637     },
38638     
38639     assignDocWin: function()
38640     {
38641         var iframe = this.iframe;
38642         
38643          if(Roo.isIE){
38644             this.doc = iframe.contentWindow.document;
38645             this.win = iframe.contentWindow;
38646         } else {
38647             if (!Roo.get(this.frameId)) {
38648                 return;
38649             }
38650             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
38651             this.win = Roo.get(this.frameId).dom.contentWindow;
38652         }
38653     },
38654     
38655     // private
38656     initEditor : function(){
38657         //console.log("INIT EDITOR");
38658         this.assignDocWin();
38659         
38660         
38661         
38662         this.doc.designMode="on";
38663         this.doc.open();
38664         this.doc.write(this.getDocMarkup());
38665         this.doc.close();
38666         
38667         var dbody = (this.doc.body || this.doc.documentElement);
38668         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
38669         // this copies styles from the containing element into thsi one..
38670         // not sure why we need all of this..
38671         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
38672         ss['background-attachment'] = 'fixed'; // w3c
38673         dbody.bgProperties = 'fixed'; // ie
38674         Roo.DomHelper.applyStyles(dbody, ss);
38675         Roo.EventManager.on(this.doc, {
38676             'mousedown': this.onEditorEvent,
38677             'dblclick': this.onEditorEvent,
38678             'click': this.onEditorEvent,
38679             'keyup': this.onEditorEvent,
38680             buffer:100,
38681             scope: this
38682         });
38683         if(Roo.isGecko){
38684             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
38685         }
38686         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
38687             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
38688         }
38689         this.initialized = true;
38690
38691         this.fireEvent('initialize', this);
38692         this.pushValue();
38693     },
38694
38695     // private
38696     onDestroy : function(){
38697         
38698         
38699         
38700         if(this.rendered){
38701             
38702             for (var i =0; i < this.toolbars.length;i++) {
38703                 // fixme - ask toolbars for heights?
38704                 this.toolbars[i].onDestroy();
38705             }
38706             
38707             this.wrap.dom.innerHTML = '';
38708             this.wrap.remove();
38709         }
38710     },
38711
38712     // private
38713     onFirstFocus : function(){
38714         
38715         this.assignDocWin();
38716         
38717         
38718         this.activated = true;
38719         for (var i =0; i < this.toolbars.length;i++) {
38720             this.toolbars[i].onFirstFocus();
38721         }
38722        
38723         if(Roo.isGecko){ // prevent silly gecko errors
38724             this.win.focus();
38725             var s = this.win.getSelection();
38726             if(!s.focusNode || s.focusNode.nodeType != 3){
38727                 var r = s.getRangeAt(0);
38728                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
38729                 r.collapse(true);
38730                 this.deferFocus();
38731             }
38732             try{
38733                 this.execCmd('useCSS', true);
38734                 this.execCmd('styleWithCSS', false);
38735             }catch(e){}
38736         }
38737         this.fireEvent('activate', this);
38738     },
38739
38740     // private
38741     adjustFont: function(btn){
38742         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
38743         //if(Roo.isSafari){ // safari
38744         //    adjust *= 2;
38745        // }
38746         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
38747         if(Roo.isSafari){ // safari
38748             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
38749             v =  (v < 10) ? 10 : v;
38750             v =  (v > 48) ? 48 : v;
38751             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
38752             
38753         }
38754         
38755         
38756         v = Math.max(1, v+adjust);
38757         
38758         this.execCmd('FontSize', v  );
38759     },
38760
38761     onEditorEvent : function(e){
38762         this.fireEvent('editorevent', this, e);
38763       //  this.updateToolbar();
38764         this.syncValue();
38765     },
38766
38767     insertTag : function(tg)
38768     {
38769         // could be a bit smarter... -> wrap the current selected tRoo..
38770         
38771         this.execCmd("formatblock",   tg);
38772         
38773     },
38774     
38775     insertText : function(txt)
38776     {
38777         
38778         
38779         range = this.createRange();
38780         range.deleteContents();
38781                //alert(Sender.getAttribute('label'));
38782                
38783         range.insertNode(this.doc.createTextNode(txt));
38784     } ,
38785     
38786     // private
38787     relayBtnCmd : function(btn){
38788         this.relayCmd(btn.cmd);
38789     },
38790
38791     /**
38792      * Executes a Midas editor command on the editor document and performs necessary focus and
38793      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
38794      * @param {String} cmd The Midas command
38795      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38796      */
38797     relayCmd : function(cmd, value){
38798         this.win.focus();
38799         this.execCmd(cmd, value);
38800         this.fireEvent('editorevent', this);
38801         //this.updateToolbar();
38802         this.deferFocus();
38803     },
38804
38805     /**
38806      * Executes a Midas editor command directly on the editor document.
38807      * For visual commands, you should use {@link #relayCmd} instead.
38808      * <b>This should only be called after the editor is initialized.</b>
38809      * @param {String} cmd The Midas command
38810      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
38811      */
38812     execCmd : function(cmd, value){
38813         this.doc.execCommand(cmd, false, value === undefined ? null : value);
38814         this.syncValue();
38815     },
38816
38817    
38818     /**
38819      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
38820      * to insert tRoo.
38821      * @param {String} text
38822      */
38823     insertAtCursor : function(text){
38824         if(!this.activated){
38825             return;
38826         }
38827         if(Roo.isIE){
38828             this.win.focus();
38829             var r = this.doc.selection.createRange();
38830             if(r){
38831                 r.collapse(true);
38832                 r.pasteHTML(text);
38833                 this.syncValue();
38834                 this.deferFocus();
38835             }
38836         }else if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
38837             this.win.focus();
38838             this.execCmd('InsertHTML', text);
38839             this.deferFocus();
38840         }
38841     },
38842  // private
38843     mozKeyPress : function(e){
38844         if(e.ctrlKey){
38845             var c = e.getCharCode(), cmd;
38846           
38847             if(c > 0){
38848                 c = String.fromCharCode(c).toLowerCase();
38849                 switch(c){
38850                     case 'b':
38851                         cmd = 'bold';
38852                     break;
38853                     case 'i':
38854                         cmd = 'italic';
38855                     break;
38856                     case 'u':
38857                         cmd = 'underline';
38858                     case 'v':
38859                         this.cleanUpPaste.defer(100, this);
38860                         return;
38861                     break;
38862                 }
38863                 if(cmd){
38864                     this.win.focus();
38865                     this.execCmd(cmd);
38866                     this.deferFocus();
38867                     e.preventDefault();
38868                 }
38869                 
38870             }
38871         }
38872     },
38873
38874     // private
38875     fixKeys : function(){ // load time branching for fastest keydown performance
38876         if(Roo.isIE){
38877             return function(e){
38878                 var k = e.getKey(), r;
38879                 if(k == e.TAB){
38880                     e.stopEvent();
38881                     r = this.doc.selection.createRange();
38882                     if(r){
38883                         r.collapse(true);
38884                         r.pasteHTML('&#160;&#160;&#160;&#160;');
38885                         this.deferFocus();
38886                     }
38887                     return;
38888                 }
38889                 
38890                 if(k == e.ENTER){
38891                     r = this.doc.selection.createRange();
38892                     if(r){
38893                         var target = r.parentElement();
38894                         if(!target || target.tagName.toLowerCase() != 'li'){
38895                             e.stopEvent();
38896                             r.pasteHTML('<br />');
38897                             r.collapse(false);
38898                             r.select();
38899                         }
38900                     }
38901                 }
38902                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38903                     this.cleanUpPaste.defer(100, this);
38904                     return;
38905                 }
38906                 
38907                 
38908             };
38909         }else if(Roo.isOpera){
38910             return function(e){
38911                 var k = e.getKey();
38912                 if(k == e.TAB){
38913                     e.stopEvent();
38914                     this.win.focus();
38915                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
38916                     this.deferFocus();
38917                 }
38918                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38919                     this.cleanUpPaste.defer(100, this);
38920                     return;
38921                 }
38922                 
38923             };
38924         }else if(Roo.isSafari){
38925             return function(e){
38926                 var k = e.getKey();
38927                 
38928                 if(k == e.TAB){
38929                     e.stopEvent();
38930                     this.execCmd('InsertText','\t');
38931                     this.deferFocus();
38932                     return;
38933                 }
38934                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
38935                     this.cleanUpPaste.defer(100, this);
38936                     return;
38937                 }
38938                 
38939              };
38940         }
38941     }(),
38942     
38943     getAllAncestors: function()
38944     {
38945         var p = this.getSelectedNode();
38946         var a = [];
38947         if (!p) {
38948             a.push(p); // push blank onto stack..
38949             p = this.getParentElement();
38950         }
38951         
38952         
38953         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
38954             a.push(p);
38955             p = p.parentNode;
38956         }
38957         a.push(this.doc.body);
38958         return a;
38959     },
38960     lastSel : false,
38961     lastSelNode : false,
38962     
38963     
38964     getSelection : function() 
38965     {
38966         this.assignDocWin();
38967         return Roo.isIE ? this.doc.selection : this.win.getSelection();
38968     },
38969     
38970     getSelectedNode: function() 
38971     {
38972         // this may only work on Gecko!!!
38973         
38974         // should we cache this!!!!
38975         
38976         
38977         
38978          
38979         var range = this.createRange(this.getSelection());
38980         
38981         if (Roo.isIE) {
38982             var parent = range.parentElement();
38983             while (true) {
38984                 var testRange = range.duplicate();
38985                 testRange.moveToElementText(parent);
38986                 if (testRange.inRange(range)) {
38987                     break;
38988                 }
38989                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
38990                     break;
38991                 }
38992                 parent = parent.parentElement;
38993             }
38994             return parent;
38995         }
38996         
38997         
38998         var ar = range.endContainer.childNodes;
38999         if (!ar.length) {
39000             ar = range.commonAncestorContainer.childNodes;
39001             //alert(ar.length);
39002         }
39003         var nodes = [];
39004         var other_nodes = [];
39005         var has_other_nodes = false;
39006         for (var i=0;i<ar.length;i++) {
39007             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
39008                 continue;
39009             }
39010             // fullly contained node.
39011             
39012             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
39013                 nodes.push(ar[i]);
39014                 continue;
39015             }
39016             
39017             // probably selected..
39018             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
39019                 other_nodes.push(ar[i]);
39020                 continue;
39021             }
39022             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
39023                 continue;
39024             }
39025             
39026             
39027             has_other_nodes = true;
39028         }
39029         if (!nodes.length && other_nodes.length) {
39030             nodes= other_nodes;
39031         }
39032         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
39033             return false;
39034         }
39035         
39036         return nodes[0];
39037     },
39038     createRange: function(sel)
39039     {
39040         // this has strange effects when using with 
39041         // top toolbar - not sure if it's a great idea.
39042         //this.editor.contentWindow.focus();
39043         if (typeof sel != "undefined") {
39044             try {
39045                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
39046             } catch(e) {
39047                 return this.doc.createRange();
39048             }
39049         } else {
39050             return this.doc.createRange();
39051         }
39052     },
39053     getParentElement: function()
39054     {
39055         
39056         this.assignDocWin();
39057         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
39058         
39059         var range = this.createRange(sel);
39060          
39061         try {
39062             var p = range.commonAncestorContainer;
39063             while (p.nodeType == 3) { // text node
39064                 p = p.parentNode;
39065             }
39066             return p;
39067         } catch (e) {
39068             return null;
39069         }
39070     
39071     },
39072     
39073     
39074     
39075     // BC Hacks - cause I cant work out what i was trying to do..
39076     rangeIntersectsNode : function(range, node)
39077     {
39078         var nodeRange = node.ownerDocument.createRange();
39079         try {
39080             nodeRange.selectNode(node);
39081         }
39082         catch (e) {
39083             nodeRange.selectNodeContents(node);
39084         }
39085
39086         return range.compareBoundaryPoints(Range.END_TO_START, nodeRange) == -1 &&
39087                  range.compareBoundaryPoints(Range.START_TO_END, nodeRange) == 1;
39088     },
39089     rangeCompareNode : function(range, node) {
39090         var nodeRange = node.ownerDocument.createRange();
39091         try {
39092             nodeRange.selectNode(node);
39093         } catch (e) {
39094             nodeRange.selectNodeContents(node);
39095         }
39096         var nodeIsBefore = range.compareBoundaryPoints(Range.START_TO_START, nodeRange) == 1;
39097         var nodeIsAfter = range.compareBoundaryPoints(Range.END_TO_END, nodeRange) == -1;
39098
39099         if (nodeIsBefore && !nodeIsAfter)
39100             return 0;
39101         if (!nodeIsBefore && nodeIsAfter)
39102             return 1;
39103         if (nodeIsBefore && nodeIsAfter)
39104             return 2;
39105
39106         return 3;
39107     },
39108
39109     // private? - in a new class?
39110     cleanUpPaste :  function()
39111     {
39112         // cleans up the whole document..
39113       //  console.log('cleanuppaste');
39114         this.cleanUpChildren(this.doc.body)
39115         
39116         
39117     },
39118     cleanUpChildren : function (n)
39119     {
39120         if (!n.childNodes.length) {
39121             return;
39122         }
39123         for (var i = n.childNodes.length-1; i > -1 ; i--) {
39124            this.cleanUpChild(n.childNodes[i]);
39125         }
39126     },
39127     
39128     
39129         
39130     
39131     cleanUpChild : function (node)
39132     {
39133         //console.log(node);
39134         if (node.nodeName == "#text") {
39135             // clean up silly Windows -- stuff?
39136             return; 
39137         }
39138         if (node.nodeName == "#comment") {
39139             node.parentNode.removeChild(node);
39140             // clean up silly Windows -- stuff?
39141             return; 
39142         }
39143         
39144         if (Roo.form.HtmlEditor.black.indexOf(node.tagName.toLowerCase()) > -1) {
39145             // remove node.
39146             node.parentNode.removeChild(node);
39147             return;
39148             
39149         }
39150         if (!node.attributes || !node.attributes.length) {
39151             this.cleanUpChildren(node);
39152             return;
39153         }
39154         
39155         function cleanAttr(n,v)
39156         {
39157             
39158             if (v.match(/^\./) || v.match(/^\//)) {
39159                 return;
39160             }
39161             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
39162                 return;
39163             }
39164             Roo.log("(REMOVE)"+ node.tagName +'.' + n + '=' + v);
39165             node.removeAttribute(n);
39166             
39167         }
39168         
39169         function cleanStyle(n,v)
39170         {
39171             if (v.match(/expression/)) { //XSS?? should we even bother..
39172                 node.removeAttribute(n);
39173                 return;
39174             }
39175             
39176             
39177             var parts = v.split(/;/);
39178             Roo.each(parts, function(p) {
39179                 p = p.replace(/\s+/g,'');
39180                 if (!p.length) {
39181                     return;
39182                 }
39183                 var l = p.split(':').shift().replace(/\s+/g,'');
39184                 
39185                 if (Roo.form.HtmlEditor.cwhite.indexOf(l) < 0) {
39186                     Roo.log('(REMOVE)' + node.tagName +'.' + n + ':'+l + '=' + v);
39187                     node.removeAttribute(n);
39188                     return false;
39189                 }
39190             });
39191             
39192             
39193         }
39194         
39195         
39196         for (var i = node.attributes.length-1; i > -1 ; i--) {
39197             var a = node.attributes[i];
39198             //console.log(a);
39199             if (Roo.form.HtmlEditor.ablack.indexOf(a.name.toLowerCase()) > -1) {
39200                 node.removeAttribute(a.name);
39201                 return;
39202             }
39203             if (Roo.form.HtmlEditor.aclean.indexOf(a.name.toLowerCase()) > -1) {
39204                 cleanAttr(a.name,a.value); // fixme..
39205                 return;
39206             }
39207             if (a.name == 'style') {
39208                 cleanStyle(a.name,a.value);
39209             }
39210             /// clean up MS crap..
39211             if (a.name == 'class') {
39212                 if (a.value.match(/^Mso/)) {
39213                     node.className = '';
39214                 }
39215             }
39216             
39217             // style cleanup!?
39218             // class cleanup?
39219             
39220         }
39221         
39222         
39223         this.cleanUpChildren(node);
39224         
39225         
39226     }
39227     
39228     
39229     // hide stuff that is not compatible
39230     /**
39231      * @event blur
39232      * @hide
39233      */
39234     /**
39235      * @event change
39236      * @hide
39237      */
39238     /**
39239      * @event focus
39240      * @hide
39241      */
39242     /**
39243      * @event specialkey
39244      * @hide
39245      */
39246     /**
39247      * @cfg {String} fieldClass @hide
39248      */
39249     /**
39250      * @cfg {String} focusClass @hide
39251      */
39252     /**
39253      * @cfg {String} autoCreate @hide
39254      */
39255     /**
39256      * @cfg {String} inputType @hide
39257      */
39258     /**
39259      * @cfg {String} invalidClass @hide
39260      */
39261     /**
39262      * @cfg {String} invalidText @hide
39263      */
39264     /**
39265      * @cfg {String} msgFx @hide
39266      */
39267     /**
39268      * @cfg {String} validateOnBlur @hide
39269      */
39270 });
39271
39272 Roo.form.HtmlEditor.white = [
39273         'area', 'br', 'img', 'input', 'hr', 'wbr',
39274         
39275        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
39276        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
39277        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
39278        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
39279        'table',   'ul',         'xmp', 
39280        
39281        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
39282       'thead',   'tr', 
39283      
39284       'dir', 'menu', 'ol', 'ul', 'dl',
39285        
39286       'embed',  'object'
39287 ];
39288
39289
39290 Roo.form.HtmlEditor.black = [
39291     //    'embed',  'object', // enable - backend responsiblity to clean thiese
39292         'applet', // 
39293         'base',   'basefont', 'bgsound', 'blink',  'body', 
39294         'frame',  'frameset', 'head',    'html',   'ilayer', 
39295         'iframe', 'layer',  'link',     'meta',    'object',   
39296         'script', 'style' ,'title',  'xml' // clean later..
39297 ];
39298 Roo.form.HtmlEditor.clean = [
39299     'script', 'style', 'title', 'xml'
39300 ];
39301
39302 // attributes..
39303
39304 Roo.form.HtmlEditor.ablack = [
39305     'on'
39306 ];
39307     
39308 Roo.form.HtmlEditor.aclean = [ 
39309     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
39310 ];
39311
39312 // protocols..
39313 Roo.form.HtmlEditor.pwhite= [
39314         'http',  'https',  'mailto'
39315 ];
39316
39317 Roo.form.HtmlEditor.cwhite= [
39318         'text-align',
39319         'font-size'
39320 ];
39321
39322 // <script type="text/javascript">
39323 /*
39324  * Based on
39325  * Ext JS Library 1.1.1
39326  * Copyright(c) 2006-2007, Ext JS, LLC.
39327  *  
39328  
39329  */
39330
39331 /**
39332  * @class Roo.form.HtmlEditorToolbar1
39333  * Basic Toolbar
39334  * 
39335  * Usage:
39336  *
39337  new Roo.form.HtmlEditor({
39338     ....
39339     toolbars : [
39340         new Roo.form.HtmlEditorToolbar1({
39341             disable : { fonts: 1 , format: 1, ..., ... , ...],
39342             btns : [ .... ]
39343         })
39344     }
39345      
39346  * 
39347  * @cfg {Object} disable List of elements to disable..
39348  * @cfg {Array} btns List of additional buttons.
39349  * 
39350  * 
39351  * NEEDS Extra CSS? 
39352  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
39353  */
39354  
39355 Roo.form.HtmlEditor.ToolbarStandard = function(config)
39356 {
39357     
39358     Roo.apply(this, config);
39359     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39360     // dont call parent... till later.
39361 }
39362
39363 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
39364     
39365     tb: false,
39366     
39367     rendered: false,
39368     
39369     editor : false,
39370     /**
39371      * @cfg {Object} disable  List of toolbar elements to disable
39372          
39373      */
39374     disable : false,
39375       /**
39376      * @cfg {Array} fontFamilies An array of available font families
39377      */
39378     fontFamilies : [
39379         'Arial',
39380         'Courier New',
39381         'Tahoma',
39382         'Times New Roman',
39383         'Verdana'
39384     ],
39385     
39386     specialChars : [
39387            "&#169;",
39388           "&#174;",     
39389           "&#8482;",    
39390           "&#163;" ,    
39391          // "&#8212;",    
39392           "&#8230;",    
39393           "&#247;" ,    
39394         //  "&#225;" ,     ?? a acute?
39395            "&#8364;"    , //Euro
39396        //   "&#8220;"    ,
39397         //  "&#8221;"    ,
39398         //  "&#8226;"    ,
39399           "&#176;"  //   , // degrees
39400
39401          // "&#233;"     , // e ecute
39402          // "&#250;"     , // u ecute?
39403     ],
39404     inputElements : [ 
39405             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
39406             "input:submit", "input:button", "select", "textarea", "label" ],
39407     formats : [
39408         ["p"] ,  
39409         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
39410         ["pre"],[ "code"], 
39411         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"]
39412     ],
39413      /**
39414      * @cfg {String} defaultFont default font to use.
39415      */
39416     defaultFont: 'tahoma',
39417    
39418     fontSelect : false,
39419     
39420     
39421     formatCombo : false,
39422     
39423     init : function(editor)
39424     {
39425         this.editor = editor;
39426         
39427         
39428         var fid = editor.frameId;
39429         var etb = this;
39430         function btn(id, toggle, handler){
39431             var xid = fid + '-'+ id ;
39432             return {
39433                 id : xid,
39434                 cmd : id,
39435                 cls : 'x-btn-icon x-edit-'+id,
39436                 enableToggle:toggle !== false,
39437                 scope: editor, // was editor...
39438                 handler:handler||editor.relayBtnCmd,
39439                 clickEvent:'mousedown',
39440                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
39441                 tabIndex:-1
39442             };
39443         }
39444         
39445         
39446         
39447         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
39448         this.tb = tb;
39449          // stop form submits
39450         tb.el.on('click', function(e){
39451             e.preventDefault(); // what does this do?
39452         });
39453
39454         if(!this.disable.font && !Roo.isSafari){
39455             /* why no safari for fonts
39456             editor.fontSelect = tb.el.createChild({
39457                 tag:'select',
39458                 tabIndex: -1,
39459                 cls:'x-font-select',
39460                 html: editor.createFontOptions()
39461             });
39462             editor.fontSelect.on('change', function(){
39463                 var font = editor.fontSelect.dom.value;
39464                 editor.relayCmd('fontname', font);
39465                 editor.deferFocus();
39466             }, editor);
39467             tb.add(
39468                 editor.fontSelect.dom,
39469                 '-'
39470             );
39471             */
39472         };
39473         if(!this.disable.formats){
39474             this.formatCombo = new Roo.form.ComboBox({
39475                 store: new Roo.data.SimpleStore({
39476                     id : 'tag',
39477                     fields: ['tag'],
39478                     data : this.formats // from states.js
39479                 }),
39480                 blockFocus : true,
39481                 //autoCreate : {tag: "div",  size: "20"},
39482                 displayField:'tag',
39483                 typeAhead: false,
39484                 mode: 'local',
39485                 editable : false,
39486                 triggerAction: 'all',
39487                 emptyText:'Add tag',
39488                 selectOnFocus:true,
39489                 width:135,
39490                 listeners : {
39491                     'select': function(c, r, i) {
39492                         editor.insertTag(r.get('tag'));
39493                         editor.focus();
39494                     }
39495                 }
39496
39497             });
39498             tb.addField(this.formatCombo);
39499             
39500         }
39501         
39502         if(!this.disable.format){
39503             tb.add(
39504                 btn('bold'),
39505                 btn('italic'),
39506                 btn('underline')
39507             );
39508         };
39509         if(!this.disable.fontSize){
39510             tb.add(
39511                 '-',
39512                 
39513                 
39514                 btn('increasefontsize', false, editor.adjustFont),
39515                 btn('decreasefontsize', false, editor.adjustFont)
39516             );
39517         };
39518         
39519         
39520         if(this.disable.colors){
39521             tb.add(
39522                 '-', {
39523                     id:editor.frameId +'-forecolor',
39524                     cls:'x-btn-icon x-edit-forecolor',
39525                     clickEvent:'mousedown',
39526                     tooltip: this.buttonTips['forecolor'] || undefined,
39527                     tabIndex:-1,
39528                     menu : new Roo.menu.ColorMenu({
39529                         allowReselect: true,
39530                         focus: Roo.emptyFn,
39531                         value:'000000',
39532                         plain:true,
39533                         selectHandler: function(cp, color){
39534                             editor.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
39535                             editor.deferFocus();
39536                         },
39537                         scope: editor,
39538                         clickEvent:'mousedown'
39539                     })
39540                 }, {
39541                     id:editor.frameId +'backcolor',
39542                     cls:'x-btn-icon x-edit-backcolor',
39543                     clickEvent:'mousedown',
39544                     tooltip: this.buttonTips['backcolor'] || undefined,
39545                     tabIndex:-1,
39546                     menu : new Roo.menu.ColorMenu({
39547                         focus: Roo.emptyFn,
39548                         value:'FFFFFF',
39549                         plain:true,
39550                         allowReselect: true,
39551                         selectHandler: function(cp, color){
39552                             if(Roo.isGecko){
39553                                 editor.execCmd('useCSS', false);
39554                                 editor.execCmd('hilitecolor', color);
39555                                 editor.execCmd('useCSS', true);
39556                                 editor.deferFocus();
39557                             }else{
39558                                 editor.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
39559                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
39560                                 editor.deferFocus();
39561                             }
39562                         },
39563                         scope:editor,
39564                         clickEvent:'mousedown'
39565                     })
39566                 }
39567             );
39568         };
39569         // now add all the items...
39570         
39571
39572         if(!this.disable.alignments){
39573             tb.add(
39574                 '-',
39575                 btn('justifyleft'),
39576                 btn('justifycenter'),
39577                 btn('justifyright')
39578             );
39579         };
39580
39581         //if(!Roo.isSafari){
39582             if(!this.disable.links){
39583                 tb.add(
39584                     '-',
39585                     btn('createlink', false, editor.createLink)    /// MOVE TO HERE?!!?!?!?!
39586                 );
39587             };
39588
39589             if(!this.disable.lists){
39590                 tb.add(
39591                     '-',
39592                     btn('insertorderedlist'),
39593                     btn('insertunorderedlist')
39594                 );
39595             }
39596             if(!this.disable.sourceEdit){
39597                 tb.add(
39598                     '-',
39599                     btn('sourceedit', true, function(btn){
39600                         this.toggleSourceEdit(btn.pressed);
39601                     })
39602                 );
39603             }
39604         //}
39605         
39606         var smenu = { };
39607         // special menu.. - needs to be tidied up..
39608         if (!this.disable.special) {
39609             smenu = {
39610                 text: "&#169;",
39611                 cls: 'x-edit-none',
39612                 menu : {
39613                     items : []
39614                    }
39615             };
39616             for (var i =0; i < this.specialChars.length; i++) {
39617                 smenu.menu.items.push({
39618                     
39619                     html: this.specialChars[i],
39620                     handler: function(a,b) {
39621                         editor.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
39622                         
39623                     },
39624                     tabIndex:-1
39625                 });
39626             }
39627             
39628             
39629             tb.add(smenu);
39630             
39631             
39632         }
39633         if (this.btns) {
39634             for(var i =0; i< this.btns.length;i++) {
39635                 var b = this.btns[i];
39636                 b.cls =  'x-edit-none';
39637                 b.scope = editor;
39638                 tb.add(b);
39639             }
39640         
39641         }
39642         
39643         
39644         
39645         // disable everything...
39646         
39647         this.tb.items.each(function(item){
39648            if(item.id != editor.frameId+ '-sourceedit'){
39649                 item.disable();
39650             }
39651         });
39652         this.rendered = true;
39653         
39654         // the all the btns;
39655         editor.on('editorevent', this.updateToolbar, this);
39656         // other toolbars need to implement this..
39657         //editor.on('editmodechange', this.updateToolbar, this);
39658     },
39659     
39660     
39661     
39662     /**
39663      * Protected method that will not generally be called directly. It triggers
39664      * a toolbar update by reading the markup state of the current selection in the editor.
39665      */
39666     updateToolbar: function(){
39667
39668         if(!this.editor.activated){
39669             this.editor.onFirstFocus();
39670             return;
39671         }
39672
39673         var btns = this.tb.items.map, 
39674             doc = this.editor.doc,
39675             frameId = this.editor.frameId;
39676
39677         if(!this.disable.font && !Roo.isSafari){
39678             /*
39679             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
39680             if(name != this.fontSelect.dom.value){
39681                 this.fontSelect.dom.value = name;
39682             }
39683             */
39684         }
39685         if(!this.disable.format){
39686             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
39687             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
39688             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
39689         }
39690         if(!this.disable.alignments){
39691             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
39692             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
39693             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
39694         }
39695         if(!Roo.isSafari && !this.disable.lists){
39696             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
39697             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
39698         }
39699         
39700         var ans = this.editor.getAllAncestors();
39701         if (this.formatCombo) {
39702             
39703             
39704             var store = this.formatCombo.store;
39705             this.formatCombo.setValue("");
39706             for (var i =0; i < ans.length;i++) {
39707                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
39708                     // select it..
39709                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
39710                     break;
39711                 }
39712             }
39713         }
39714         
39715         
39716         
39717         // hides menus... - so this cant be on a menu...
39718         Roo.menu.MenuMgr.hideAll();
39719
39720         //this.editorsyncValue();
39721     },
39722    
39723     
39724     createFontOptions : function(){
39725         var buf = [], fs = this.fontFamilies, ff, lc;
39726         for(var i = 0, len = fs.length; i< len; i++){
39727             ff = fs[i];
39728             lc = ff.toLowerCase();
39729             buf.push(
39730                 '<option value="',lc,'" style="font-family:',ff,';"',
39731                     (this.defaultFont == lc ? ' selected="true">' : '>'),
39732                     ff,
39733                 '</option>'
39734             );
39735         }
39736         return buf.join('');
39737     },
39738     
39739     toggleSourceEdit : function(sourceEditMode){
39740         if(sourceEditMode === undefined){
39741             sourceEditMode = !this.sourceEditMode;
39742         }
39743         this.sourceEditMode = sourceEditMode === true;
39744         var btn = this.tb.items.get(this.editor.frameId +'-sourceedit');
39745         // just toggle the button?
39746         if(btn.pressed !== this.editor.sourceEditMode){
39747             btn.toggle(this.editor.sourceEditMode);
39748             return;
39749         }
39750         
39751         if(this.sourceEditMode){
39752             this.tb.items.each(function(item){
39753                 if(item.cmd != 'sourceedit'){
39754                     item.disable();
39755                 }
39756             });
39757           
39758         }else{
39759             if(this.initialized){
39760                 this.tb.items.each(function(item){
39761                     item.enable();
39762                 });
39763             }
39764             
39765         }
39766         // tell the editor that it's been pressed..
39767         this.editor.toggleSourceEdit(sourceEditMode);
39768        
39769     },
39770      /**
39771      * Object collection of toolbar tooltips for the buttons in the editor. The key
39772      * is the command id associated with that button and the value is a valid QuickTips object.
39773      * For example:
39774 <pre><code>
39775 {
39776     bold : {
39777         title: 'Bold (Ctrl+B)',
39778         text: 'Make the selected text bold.',
39779         cls: 'x-html-editor-tip'
39780     },
39781     italic : {
39782         title: 'Italic (Ctrl+I)',
39783         text: 'Make the selected text italic.',
39784         cls: 'x-html-editor-tip'
39785     },
39786     ...
39787 </code></pre>
39788     * @type Object
39789      */
39790     buttonTips : {
39791         bold : {
39792             title: 'Bold (Ctrl+B)',
39793             text: 'Make the selected text bold.',
39794             cls: 'x-html-editor-tip'
39795         },
39796         italic : {
39797             title: 'Italic (Ctrl+I)',
39798             text: 'Make the selected text italic.',
39799             cls: 'x-html-editor-tip'
39800         },
39801         underline : {
39802             title: 'Underline (Ctrl+U)',
39803             text: 'Underline the selected text.',
39804             cls: 'x-html-editor-tip'
39805         },
39806         increasefontsize : {
39807             title: 'Grow Text',
39808             text: 'Increase the font size.',
39809             cls: 'x-html-editor-tip'
39810         },
39811         decreasefontsize : {
39812             title: 'Shrink Text',
39813             text: 'Decrease the font size.',
39814             cls: 'x-html-editor-tip'
39815         },
39816         backcolor : {
39817             title: 'Text Highlight Color',
39818             text: 'Change the background color of the selected text.',
39819             cls: 'x-html-editor-tip'
39820         },
39821         forecolor : {
39822             title: 'Font Color',
39823             text: 'Change the color of the selected text.',
39824             cls: 'x-html-editor-tip'
39825         },
39826         justifyleft : {
39827             title: 'Align Text Left',
39828             text: 'Align text to the left.',
39829             cls: 'x-html-editor-tip'
39830         },
39831         justifycenter : {
39832             title: 'Center Text',
39833             text: 'Center text in the editor.',
39834             cls: 'x-html-editor-tip'
39835         },
39836         justifyright : {
39837             title: 'Align Text Right',
39838             text: 'Align text to the right.',
39839             cls: 'x-html-editor-tip'
39840         },
39841         insertunorderedlist : {
39842             title: 'Bullet List',
39843             text: 'Start a bulleted list.',
39844             cls: 'x-html-editor-tip'
39845         },
39846         insertorderedlist : {
39847             title: 'Numbered List',
39848             text: 'Start a numbered list.',
39849             cls: 'x-html-editor-tip'
39850         },
39851         createlink : {
39852             title: 'Hyperlink',
39853             text: 'Make the selected text a hyperlink.',
39854             cls: 'x-html-editor-tip'
39855         },
39856         sourceedit : {
39857             title: 'Source Edit',
39858             text: 'Switch to source editing mode.',
39859             cls: 'x-html-editor-tip'
39860         }
39861     },
39862     // private
39863     onDestroy : function(){
39864         if(this.rendered){
39865             
39866             this.tb.items.each(function(item){
39867                 if(item.menu){
39868                     item.menu.removeAll();
39869                     if(item.menu.el){
39870                         item.menu.el.destroy();
39871                     }
39872                 }
39873                 item.destroy();
39874             });
39875              
39876         }
39877     },
39878     onFirstFocus: function() {
39879         this.tb.items.each(function(item){
39880            item.enable();
39881         });
39882     }
39883 });
39884
39885
39886
39887
39888 // <script type="text/javascript">
39889 /*
39890  * Based on
39891  * Ext JS Library 1.1.1
39892  * Copyright(c) 2006-2007, Ext JS, LLC.
39893  *  
39894  
39895  */
39896
39897  
39898 /**
39899  * @class Roo.form.HtmlEditor.ToolbarContext
39900  * Context Toolbar
39901  * 
39902  * Usage:
39903  *
39904  new Roo.form.HtmlEditor({
39905     ....
39906     toolbars : [
39907         new Roo.form.HtmlEditor.ToolbarStandard(),
39908         new Roo.form.HtmlEditor.ToolbarContext()
39909         })
39910     }
39911      
39912  * 
39913  * @config : {Object} disable List of elements to disable.. (not done yet.)
39914  * 
39915  * 
39916  */
39917
39918 Roo.form.HtmlEditor.ToolbarContext = function(config)
39919 {
39920     
39921     Roo.apply(this, config);
39922     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
39923     // dont call parent... till later.
39924 }
39925 Roo.form.HtmlEditor.ToolbarContext.types = {
39926     'IMG' : {
39927         width : {
39928             title: "Width",
39929             width: 40
39930         },
39931         height:  {
39932             title: "Height",
39933             width: 40
39934         },
39935         align: {
39936             title: "Align",
39937             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
39938             width : 80
39939             
39940         },
39941         border: {
39942             title: "Border",
39943             width: 40
39944         },
39945         alt: {
39946             title: "Alt",
39947             width: 120
39948         },
39949         src : {
39950             title: "Src",
39951             width: 220
39952         }
39953         
39954     },
39955     'A' : {
39956         name : {
39957             title: "Name",
39958             width: 50
39959         },
39960         href:  {
39961             title: "Href",
39962             width: 220
39963         } // border?
39964         
39965     },
39966     'TABLE' : {
39967         rows : {
39968             title: "Rows",
39969             width: 20
39970         },
39971         cols : {
39972             title: "Cols",
39973             width: 20
39974         },
39975         width : {
39976             title: "Width",
39977             width: 40
39978         },
39979         height : {
39980             title: "Height",
39981             width: 40
39982         },
39983         border : {
39984             title: "Border",
39985             width: 20
39986         }
39987     },
39988     'TD' : {
39989         width : {
39990             title: "Width",
39991             width: 40
39992         },
39993         height : {
39994             title: "Height",
39995             width: 40
39996         },   
39997         align: {
39998             title: "Align",
39999             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
40000             width: 40
40001         },
40002         valign: {
40003             title: "Valign",
40004             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
40005             width: 40
40006         },
40007         colspan: {
40008             title: "Colspan",
40009             width: 20
40010             
40011         }
40012     },
40013     'INPUT' : {
40014         name : {
40015             title: "name",
40016             width: 120
40017         },
40018         value : {
40019             title: "Value",
40020             width: 120
40021         },
40022         width : {
40023             title: "Width",
40024             width: 40
40025         }
40026     },
40027     'LABEL' : {
40028         'for' : {
40029             title: "For",
40030             width: 120
40031         }
40032     },
40033     'TEXTAREA' : {
40034           name : {
40035             title: "name",
40036             width: 120
40037         },
40038         rows : {
40039             title: "Rows",
40040             width: 20
40041         },
40042         cols : {
40043             title: "Cols",
40044             width: 20
40045         }
40046     },
40047     'SELECT' : {
40048         name : {
40049             title: "name",
40050             width: 120
40051         },
40052         selectoptions : {
40053             title: "Options",
40054             width: 200
40055         }
40056     },
40057     'BODY' : {
40058         title : {
40059             title: "title",
40060             width: 120,
40061             disabled : true
40062         }
40063     }
40064 };
40065
40066
40067
40068 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
40069     
40070     tb: false,
40071     
40072     rendered: false,
40073     
40074     editor : false,
40075     /**
40076      * @cfg {Object} disable  List of toolbar elements to disable
40077          
40078      */
40079     disable : false,
40080     
40081     
40082     
40083     toolbars : false,
40084     
40085     init : function(editor)
40086     {
40087         this.editor = editor;
40088         
40089         
40090         var fid = editor.frameId;
40091         var etb = this;
40092         function btn(id, toggle, handler){
40093             var xid = fid + '-'+ id ;
40094             return {
40095                 id : xid,
40096                 cmd : id,
40097                 cls : 'x-btn-icon x-edit-'+id,
40098                 enableToggle:toggle !== false,
40099                 scope: editor, // was editor...
40100                 handler:handler||editor.relayBtnCmd,
40101                 clickEvent:'mousedown',
40102                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
40103                 tabIndex:-1
40104             };
40105         }
40106         // create a new element.
40107         var wdiv = editor.wrap.createChild({
40108                 tag: 'div'
40109             }, editor.wrap.dom.firstChild.nextSibling, true);
40110         
40111         // can we do this more than once??
40112         
40113          // stop form submits
40114       
40115  
40116         // disable everything...
40117         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40118         this.toolbars = {};
40119            
40120         for (var i in  ty) {
40121           
40122             this.toolbars[i] = this.buildToolbar(ty[i],i);
40123         }
40124         this.tb = this.toolbars.BODY;
40125         this.tb.el.show();
40126         
40127          
40128         this.rendered = true;
40129         
40130         // the all the btns;
40131         editor.on('editorevent', this.updateToolbar, this);
40132         // other toolbars need to implement this..
40133         //editor.on('editmodechange', this.updateToolbar, this);
40134     },
40135     
40136     
40137     
40138     /**
40139      * Protected method that will not generally be called directly. It triggers
40140      * a toolbar update by reading the markup state of the current selection in the editor.
40141      */
40142     updateToolbar: function(){
40143
40144         if(!this.editor.activated){
40145             this.editor.onFirstFocus();
40146             return;
40147         }
40148
40149         
40150         var ans = this.editor.getAllAncestors();
40151         
40152         // pick
40153         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
40154         var sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editor.doc.body;
40155         sel = sel ? sel : this.editor.doc.body;
40156         sel = sel.tagName.length ? sel : this.editor.doc.body;
40157         var tn = sel.tagName.toUpperCase();
40158         sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
40159         tn = sel.tagName.toUpperCase();
40160         if (this.tb.name  == tn) {
40161             return; // no change
40162         }
40163         this.tb.el.hide();
40164         ///console.log("show: " + tn);
40165         this.tb =  this.toolbars[tn];
40166         this.tb.el.show();
40167         this.tb.fields.each(function(e) {
40168             e.setValue(sel.getAttribute(e.name));
40169         });
40170         this.tb.selectedNode = sel;
40171         
40172         
40173         Roo.menu.MenuMgr.hideAll();
40174
40175         //this.editorsyncValue();
40176     },
40177    
40178        
40179     // private
40180     onDestroy : function(){
40181         if(this.rendered){
40182             
40183             this.tb.items.each(function(item){
40184                 if(item.menu){
40185                     item.menu.removeAll();
40186                     if(item.menu.el){
40187                         item.menu.el.destroy();
40188                     }
40189                 }
40190                 item.destroy();
40191             });
40192              
40193         }
40194     },
40195     onFirstFocus: function() {
40196         // need to do this for all the toolbars..
40197         this.tb.items.each(function(item){
40198            item.enable();
40199         });
40200     },
40201     buildToolbar: function(tlist, nm)
40202     {
40203         var editor = this.editor;
40204          // create a new element.
40205         var wdiv = editor.wrap.createChild({
40206                 tag: 'div'
40207             }, editor.wrap.dom.firstChild.nextSibling, true);
40208         
40209        
40210         var tb = new Roo.Toolbar(wdiv);
40211         tb.add(nm+ ":&nbsp;");
40212         for (var i in tlist) {
40213             var item = tlist[i];
40214             tb.add(item.title + ":&nbsp;");
40215             if (item.opts) {
40216                 // fixme
40217                 
40218               
40219                 tb.addField( new Roo.form.ComboBox({
40220                     store: new Roo.data.SimpleStore({
40221                         id : 'val',
40222                         fields: ['val'],
40223                         data : item.opts // from states.js
40224                     }),
40225                     name : i,
40226                     displayField:'val',
40227                     typeAhead: false,
40228                     mode: 'local',
40229                     editable : false,
40230                     triggerAction: 'all',
40231                     emptyText:'Select',
40232                     selectOnFocus:true,
40233                     width: item.width ? item.width  : 130,
40234                     listeners : {
40235                         'select': function(c, r, i) {
40236                             tb.selectedNode.setAttribute(c.name, r.get('val'));
40237                         }
40238                     }
40239
40240                 }));
40241                 continue;
40242                     
40243                 
40244                 
40245                 
40246                 
40247                 tb.addField( new Roo.form.TextField({
40248                     name: i,
40249                     width: 100,
40250                     //allowBlank:false,
40251                     value: ''
40252                 }));
40253                 continue;
40254             }
40255             tb.addField( new Roo.form.TextField({
40256                 name: i,
40257                 width: item.width,
40258                 //allowBlank:true,
40259                 value: '',
40260                 listeners: {
40261                     'change' : function(f, nv, ov) {
40262                         tb.selectedNode.setAttribute(f.name, nv);
40263                     }
40264                 }
40265             }));
40266              
40267         }
40268         tb.el.on('click', function(e){
40269             e.preventDefault(); // what does this do?
40270         });
40271         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
40272         tb.el.hide();
40273         tb.name = nm;
40274         // dont need to disable them... as they will get hidden
40275         return tb;
40276          
40277         
40278     }
40279     
40280     
40281     
40282     
40283 });
40284
40285
40286
40287
40288
40289 /*
40290  * Based on:
40291  * Ext JS Library 1.1.1
40292  * Copyright(c) 2006-2007, Ext JS, LLC.
40293  *
40294  * Originally Released Under LGPL - original licence link has changed is not relivant.
40295  *
40296  * Fork - LGPL
40297  * <script type="text/javascript">
40298  */
40299  
40300 /**
40301  * @class Roo.form.BasicForm
40302  * @extends Roo.util.Observable
40303  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
40304  * @constructor
40305  * @param {String/HTMLElement/Roo.Element} el The form element or its id
40306  * @param {Object} config Configuration options
40307  */
40308 Roo.form.BasicForm = function(el, config){
40309     this.allItems = [];
40310     this.childForms = [];
40311     Roo.apply(this, config);
40312     /*
40313      * The Roo.form.Field items in this form.
40314      * @type MixedCollection
40315      */
40316      
40317      
40318     this.items = new Roo.util.MixedCollection(false, function(o){
40319         return o.id || (o.id = Roo.id());
40320     });
40321     this.addEvents({
40322         /**
40323          * @event beforeaction
40324          * Fires before any action is performed. Return false to cancel the action.
40325          * @param {Form} this
40326          * @param {Action} action The action to be performed
40327          */
40328         beforeaction: true,
40329         /**
40330          * @event actionfailed
40331          * Fires when an action fails.
40332          * @param {Form} this
40333          * @param {Action} action The action that failed
40334          */
40335         actionfailed : true,
40336         /**
40337          * @event actioncomplete
40338          * Fires when an action is completed.
40339          * @param {Form} this
40340          * @param {Action} action The action that completed
40341          */
40342         actioncomplete : true
40343     });
40344     if(el){
40345         this.initEl(el);
40346     }
40347     Roo.form.BasicForm.superclass.constructor.call(this);
40348 };
40349
40350 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
40351     /**
40352      * @cfg {String} method
40353      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
40354      */
40355     /**
40356      * @cfg {DataReader} reader
40357      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
40358      * This is optional as there is built-in support for processing JSON.
40359      */
40360     /**
40361      * @cfg {DataReader} errorReader
40362      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
40363      * This is completely optional as there is built-in support for processing JSON.
40364      */
40365     /**
40366      * @cfg {String} url
40367      * The URL to use for form actions if one isn't supplied in the action options.
40368      */
40369     /**
40370      * @cfg {Boolean} fileUpload
40371      * Set to true if this form is a file upload.
40372      */
40373     /**
40374      * @cfg {Object} baseParams
40375      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
40376      */
40377     /**
40378      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
40379      */
40380     timeout: 30,
40381
40382     // private
40383     activeAction : null,
40384
40385     /**
40386      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
40387      * or setValues() data instead of when the form was first created.
40388      */
40389     trackResetOnLoad : false,
40390     
40391     
40392     /**
40393      * childForms - used for multi-tab forms
40394      * @type {Array}
40395      */
40396     childForms : false,
40397     
40398     /**
40399      * allItems - full list of fields.
40400      * @type {Array}
40401      */
40402     allItems : false,
40403     
40404     /**
40405      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
40406      * element by passing it or its id or mask the form itself by passing in true.
40407      * @type Mixed
40408      */
40409     waitMsgTarget : undefined,
40410
40411     // private
40412     initEl : function(el){
40413         this.el = Roo.get(el);
40414         this.id = this.el.id || Roo.id();
40415         this.el.on('submit', this.onSubmit, this);
40416         this.el.addClass('x-form');
40417     },
40418
40419     // private
40420     onSubmit : function(e){
40421         e.stopEvent();
40422     },
40423
40424     /**
40425      * Returns true if client-side validation on the form is successful.
40426      * @return Boolean
40427      */
40428     isValid : function(){
40429         var valid = true;
40430         this.items.each(function(f){
40431            if(!f.validate()){
40432                valid = false;
40433            }
40434         });
40435         return valid;
40436     },
40437
40438     /**
40439      * Returns true if any fields in this form have changed since their original load.
40440      * @return Boolean
40441      */
40442     isDirty : function(){
40443         var dirty = false;
40444         this.items.each(function(f){
40445            if(f.isDirty()){
40446                dirty = true;
40447                return false;
40448            }
40449         });
40450         return dirty;
40451     },
40452
40453     /**
40454      * Performs a predefined action (submit or load) or custom actions you define on this form.
40455      * @param {String} actionName The name of the action type
40456      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
40457      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
40458      * accept other config options):
40459      * <pre>
40460 Property          Type             Description
40461 ----------------  ---------------  ----------------------------------------------------------------------------------
40462 url               String           The url for the action (defaults to the form's url)
40463 method            String           The form method to use (defaults to the form's method, or POST if not defined)
40464 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
40465 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
40466                                    validate the form on the client (defaults to false)
40467      * </pre>
40468      * @return {BasicForm} this
40469      */
40470     doAction : function(action, options){
40471         if(typeof action == 'string'){
40472             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
40473         }
40474         if(this.fireEvent('beforeaction', this, action) !== false){
40475             this.beforeAction(action);
40476             action.run.defer(100, action);
40477         }
40478         return this;
40479     },
40480
40481     /**
40482      * Shortcut to do a submit action.
40483      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40484      * @return {BasicForm} this
40485      */
40486     submit : function(options){
40487         this.doAction('submit', options);
40488         return this;
40489     },
40490
40491     /**
40492      * Shortcut to do a load action.
40493      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
40494      * @return {BasicForm} this
40495      */
40496     load : function(options){
40497         this.doAction('load', options);
40498         return this;
40499     },
40500
40501     /**
40502      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
40503      * @param {Record} record The record to edit
40504      * @return {BasicForm} this
40505      */
40506     updateRecord : function(record){
40507         record.beginEdit();
40508         var fs = record.fields;
40509         fs.each(function(f){
40510             var field = this.findField(f.name);
40511             if(field){
40512                 record.set(f.name, field.getValue());
40513             }
40514         }, this);
40515         record.endEdit();
40516         return this;
40517     },
40518
40519     /**
40520      * Loads an Roo.data.Record into this form.
40521      * @param {Record} record The record to load
40522      * @return {BasicForm} this
40523      */
40524     loadRecord : function(record){
40525         this.setValues(record.data);
40526         return this;
40527     },
40528
40529     // private
40530     beforeAction : function(action){
40531         var o = action.options;
40532         if(o.waitMsg){
40533             if(this.waitMsgTarget === true){
40534                 this.el.mask(o.waitMsg, 'x-mask-loading');
40535             }else if(this.waitMsgTarget){
40536                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
40537                 this.waitMsgTarget.mask(o.waitMsg, 'x-mask-loading');
40538             }else{
40539                 Roo.MessageBox.wait(o.waitMsg, o.waitTitle || this.waitTitle || 'Please Wait...');
40540             }
40541         }
40542     },
40543
40544     // private
40545     afterAction : function(action, success){
40546         this.activeAction = null;
40547         var o = action.options;
40548         if(o.waitMsg){
40549             if(this.waitMsgTarget === true){
40550                 this.el.unmask();
40551             }else if(this.waitMsgTarget){
40552                 this.waitMsgTarget.unmask();
40553             }else{
40554                 Roo.MessageBox.updateProgress(1);
40555                 Roo.MessageBox.hide();
40556             }
40557         }
40558         if(success){
40559             if(o.reset){
40560                 this.reset();
40561             }
40562             Roo.callback(o.success, o.scope, [this, action]);
40563             this.fireEvent('actioncomplete', this, action);
40564         }else{
40565             Roo.callback(o.failure, o.scope, [this, action]);
40566             this.fireEvent('actionfailed', this, action);
40567         }
40568     },
40569
40570     /**
40571      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
40572      * @param {String} id The value to search for
40573      * @return Field
40574      */
40575     findField : function(id){
40576         var field = this.items.get(id);
40577         if(!field){
40578             this.items.each(function(f){
40579                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
40580                     field = f;
40581                     return false;
40582                 }
40583             });
40584         }
40585         return field || null;
40586     },
40587
40588     /**
40589      * Add a secondary form to this one, 
40590      * Used to provide tabbed forms. One form is primary, with hidden values 
40591      * which mirror the elements from the other forms.
40592      * 
40593      * @param {Roo.form.Form} form to add.
40594      * 
40595      */
40596     addForm : function(form)
40597     {
40598        
40599         if (this.childForms.indexOf(form) > -1) {
40600             // already added..
40601             return;
40602         }
40603         this.childForms.push(form);
40604         var n = '';
40605         Roo.each(form.allItems, function (fe) {
40606             
40607             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
40608             if (this.findField(n)) { // already added..
40609                 return;
40610             }
40611             var add = new Roo.form.Hidden({
40612                 name : n
40613             });
40614             add.render(this.el);
40615             
40616             this.add( add );
40617         }, this);
40618         
40619     },
40620     /**
40621      * Mark fields in this form invalid in bulk.
40622      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
40623      * @return {BasicForm} this
40624      */
40625     markInvalid : function(errors){
40626         if(errors instanceof Array){
40627             for(var i = 0, len = errors.length; i < len; i++){
40628                 var fieldError = errors[i];
40629                 var f = this.findField(fieldError.id);
40630                 if(f){
40631                     f.markInvalid(fieldError.msg);
40632                 }
40633             }
40634         }else{
40635             var field, id;
40636             for(id in errors){
40637                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
40638                     field.markInvalid(errors[id]);
40639                 }
40640             }
40641         }
40642         Roo.each(this.childForms || [], function (f) {
40643             f.markInvalid(errors);
40644         });
40645         
40646         return this;
40647     },
40648
40649     /**
40650      * Set values for fields in this form in bulk.
40651      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
40652      * @return {BasicForm} this
40653      */
40654     setValues : function(values){
40655         if(values instanceof Array){ // array of objects
40656             for(var i = 0, len = values.length; i < len; i++){
40657                 var v = values[i];
40658                 var f = this.findField(v.id);
40659                 if(f){
40660                     f.setValue(v.value);
40661                     if(this.trackResetOnLoad){
40662                         f.originalValue = f.getValue();
40663                     }
40664                 }
40665             }
40666         }else{ // object hash
40667             var field, id;
40668             for(id in values){
40669                 if(typeof values[id] != 'function' && (field = this.findField(id))){
40670                     
40671                     if (field.setFromData && 
40672                         field.valueField && 
40673                         field.displayField &&
40674                         // combos' with local stores can 
40675                         // be queried via setValue()
40676                         // to set their value..
40677                         (field.store && !field.store.isLocal)
40678                         ) {
40679                         // it's a combo
40680                         var sd = { };
40681                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
40682                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
40683                         field.setFromData(sd);
40684                         
40685                     } else {
40686                         field.setValue(values[id]);
40687                     }
40688                     
40689                     
40690                     if(this.trackResetOnLoad){
40691                         field.originalValue = field.getValue();
40692                     }
40693                 }
40694             }
40695         }
40696          
40697         Roo.each(this.childForms || [], function (f) {
40698             f.setValues(values);
40699         });
40700                 
40701         return this;
40702     },
40703
40704     /**
40705      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
40706      * they are returned as an array.
40707      * @param {Boolean} asString
40708      * @return {Object}
40709      */
40710     getValues : function(asString){
40711         if (this.childForms) {
40712             // copy values from the child forms
40713             Roo.each(this.childForms, function (f) {
40714                 this.setValues(f.getValues());
40715             }, this);
40716         }
40717         
40718         
40719         
40720         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
40721         if(asString === true){
40722             return fs;
40723         }
40724         return Roo.urlDecode(fs);
40725     },
40726     
40727     /**
40728      * Returns the fields in this form as an object with key/value pairs. 
40729      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
40730      * @return {Object}
40731      */
40732     getFieldValues : function()
40733     {
40734         if (this.childForms) {
40735             // copy values from the child forms
40736             Roo.each(this.childForms, function (f) {
40737                 this.setValues(f.getValues());
40738             }, this);
40739         }
40740         
40741         var ret = {};
40742         this.items.each(function(f){
40743             if (!f.getName()) {
40744                 return;
40745             }
40746             var v = f.getValue();
40747             if ((typeof(v) == 'object') && f.getRawValue) {
40748                 v = f.getRawValue() ; // dates..
40749             }
40750             ret[f.getName()] = v;
40751         });
40752         
40753         return ret;
40754     },
40755
40756     /**
40757      * Clears all invalid messages in this form.
40758      * @return {BasicForm} this
40759      */
40760     clearInvalid : function(){
40761         this.items.each(function(f){
40762            f.clearInvalid();
40763         });
40764         
40765         Roo.each(this.childForms || [], function (f) {
40766             f.clearInvalid();
40767         });
40768         
40769         
40770         return this;
40771     },
40772
40773     /**
40774      * Resets this form.
40775      * @return {BasicForm} this
40776      */
40777     reset : function(){
40778         this.items.each(function(f){
40779             f.reset();
40780         });
40781         
40782         Roo.each(this.childForms || [], function (f) {
40783             f.reset();
40784         });
40785        
40786         
40787         return this;
40788     },
40789
40790     /**
40791      * Add Roo.form components to this form.
40792      * @param {Field} field1
40793      * @param {Field} field2 (optional)
40794      * @param {Field} etc (optional)
40795      * @return {BasicForm} this
40796      */
40797     add : function(){
40798         this.items.addAll(Array.prototype.slice.call(arguments, 0));
40799         return this;
40800     },
40801
40802
40803     /**
40804      * Removes a field from the items collection (does NOT remove its markup).
40805      * @param {Field} field
40806      * @return {BasicForm} this
40807      */
40808     remove : function(field){
40809         this.items.remove(field);
40810         return this;
40811     },
40812
40813     /**
40814      * Looks at the fields in this form, checks them for an id attribute,
40815      * and calls applyTo on the existing dom element with that id.
40816      * @return {BasicForm} this
40817      */
40818     render : function(){
40819         this.items.each(function(f){
40820             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
40821                 f.applyTo(f.id);
40822             }
40823         });
40824         return this;
40825     },
40826
40827     /**
40828      * Calls {@link Ext#apply} for all fields in this form with the passed object.
40829      * @param {Object} values
40830      * @return {BasicForm} this
40831      */
40832     applyToFields : function(o){
40833         this.items.each(function(f){
40834            Roo.apply(f, o);
40835         });
40836         return this;
40837     },
40838
40839     /**
40840      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
40841      * @param {Object} values
40842      * @return {BasicForm} this
40843      */
40844     applyIfToFields : function(o){
40845         this.items.each(function(f){
40846            Roo.applyIf(f, o);
40847         });
40848         return this;
40849     }
40850 });
40851
40852 // back compat
40853 Roo.BasicForm = Roo.form.BasicForm;/*
40854  * Based on:
40855  * Ext JS Library 1.1.1
40856  * Copyright(c) 2006-2007, Ext JS, LLC.
40857  *
40858  * Originally Released Under LGPL - original licence link has changed is not relivant.
40859  *
40860  * Fork - LGPL
40861  * <script type="text/javascript">
40862  */
40863
40864 /**
40865  * @class Roo.form.Form
40866  * @extends Roo.form.BasicForm
40867  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
40868  * @constructor
40869  * @param {Object} config Configuration options
40870  */
40871 Roo.form.Form = function(config){
40872     var xitems =  [];
40873     if (config.items) {
40874         xitems = config.items;
40875         delete config.items;
40876     }
40877    
40878     
40879     Roo.form.Form.superclass.constructor.call(this, null, config);
40880     this.url = this.url || this.action;
40881     if(!this.root){
40882         this.root = new Roo.form.Layout(Roo.applyIf({
40883             id: Roo.id()
40884         }, config));
40885     }
40886     this.active = this.root;
40887     /**
40888      * Array of all the buttons that have been added to this form via {@link addButton}
40889      * @type Array
40890      */
40891     this.buttons = [];
40892     this.allItems = [];
40893     this.addEvents({
40894         /**
40895          * @event clientvalidation
40896          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
40897          * @param {Form} this
40898          * @param {Boolean} valid true if the form has passed client-side validation
40899          */
40900         clientvalidation: true,
40901         /**
40902          * @event rendered
40903          * Fires when the form is rendered
40904          * @param {Roo.form.Form} form
40905          */
40906         rendered : true
40907     });
40908     
40909     if (this.progressUrl) {
40910             // push a hidden field onto the list of fields..
40911             this.addxtype( {
40912                     xns: Roo.form, 
40913                     xtype : 'Hidden', 
40914                     name : 'UPLOAD_IDENTIFIER' 
40915             });
40916         }
40917         
40918     
40919     Roo.each(xitems, this.addxtype, this);
40920     
40921     
40922     
40923 };
40924
40925 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
40926     /**
40927      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
40928      */
40929     /**
40930      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
40931      */
40932     /**
40933      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
40934      */
40935     buttonAlign:'center',
40936
40937     /**
40938      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
40939      */
40940     minButtonWidth:75,
40941
40942     /**
40943      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
40944      * This property cascades to child containers if not set.
40945      */
40946     labelAlign:'left',
40947
40948     /**
40949      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
40950      * fires a looping event with that state. This is required to bind buttons to the valid
40951      * state using the config value formBind:true on the button.
40952      */
40953     monitorValid : false,
40954
40955     /**
40956      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
40957      */
40958     monitorPoll : 200,
40959     
40960     /**
40961      * @cfg {String} progressUrl - Url to return progress data 
40962      */
40963     
40964     progressUrl : false,
40965   
40966     /**
40967      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
40968      * fields are added and the column is closed. If no fields are passed the column remains open
40969      * until end() is called.
40970      * @param {Object} config The config to pass to the column
40971      * @param {Field} field1 (optional)
40972      * @param {Field} field2 (optional)
40973      * @param {Field} etc (optional)
40974      * @return Column The column container object
40975      */
40976     column : function(c){
40977         var col = new Roo.form.Column(c);
40978         this.start(col);
40979         if(arguments.length > 1){ // duplicate code required because of Opera
40980             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
40981             this.end();
40982         }
40983         return col;
40984     },
40985
40986     /**
40987      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
40988      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
40989      * until end() is called.
40990      * @param {Object} config The config to pass to the fieldset
40991      * @param {Field} field1 (optional)
40992      * @param {Field} field2 (optional)
40993      * @param {Field} etc (optional)
40994      * @return FieldSet The fieldset container object
40995      */
40996     fieldset : function(c){
40997         var fs = new Roo.form.FieldSet(c);
40998         this.start(fs);
40999         if(arguments.length > 1){ // duplicate code required because of Opera
41000             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41001             this.end();
41002         }
41003         return fs;
41004     },
41005
41006     /**
41007      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
41008      * fields are added and the container is closed. If no fields are passed the container remains open
41009      * until end() is called.
41010      * @param {Object} config The config to pass to the Layout
41011      * @param {Field} field1 (optional)
41012      * @param {Field} field2 (optional)
41013      * @param {Field} etc (optional)
41014      * @return Layout The container object
41015      */
41016     container : function(c){
41017         var l = new Roo.form.Layout(c);
41018         this.start(l);
41019         if(arguments.length > 1){ // duplicate code required because of Opera
41020             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
41021             this.end();
41022         }
41023         return l;
41024     },
41025
41026     /**
41027      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
41028      * @param {Object} container A Roo.form.Layout or subclass of Layout
41029      * @return {Form} this
41030      */
41031     start : function(c){
41032         // cascade label info
41033         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
41034         this.active.stack.push(c);
41035         c.ownerCt = this.active;
41036         this.active = c;
41037         return this;
41038     },
41039
41040     /**
41041      * Closes the current open container
41042      * @return {Form} this
41043      */
41044     end : function(){
41045         if(this.active == this.root){
41046             return this;
41047         }
41048         this.active = this.active.ownerCt;
41049         return this;
41050     },
41051
41052     /**
41053      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
41054      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
41055      * as the label of the field.
41056      * @param {Field} field1
41057      * @param {Field} field2 (optional)
41058      * @param {Field} etc. (optional)
41059      * @return {Form} this
41060      */
41061     add : function(){
41062         this.active.stack.push.apply(this.active.stack, arguments);
41063         this.allItems.push.apply(this.allItems,arguments);
41064         var r = [];
41065         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
41066             if(a[i].isFormField){
41067                 r.push(a[i]);
41068             }
41069         }
41070         if(r.length > 0){
41071             Roo.form.Form.superclass.add.apply(this, r);
41072         }
41073         return this;
41074     },
41075     
41076
41077     
41078     
41079     
41080      /**
41081      * Find any element that has been added to a form, using it's ID or name
41082      * This can include framesets, columns etc. along with regular fields..
41083      * @param {String} id - id or name to find.
41084      
41085      * @return {Element} e - or false if nothing found.
41086      */
41087     findbyId : function(id)
41088     {
41089         var ret = false;
41090         if (!id) {
41091             return ret;
41092         }
41093         Ext.each(this.allItems, function(f){
41094             if (f.id == id || f.name == id ){
41095                 ret = f;
41096                 return false;
41097             }
41098         });
41099         return ret;
41100     },
41101
41102     
41103     
41104     /**
41105      * Render this form into the passed container. This should only be called once!
41106      * @param {String/HTMLElement/Element} container The element this component should be rendered into
41107      * @return {Form} this
41108      */
41109     render : function(ct)
41110     {
41111         
41112         
41113         
41114         ct = Roo.get(ct);
41115         var o = this.autoCreate || {
41116             tag: 'form',
41117             method : this.method || 'POST',
41118             id : this.id || Roo.id()
41119         };
41120         this.initEl(ct.createChild(o));
41121
41122         this.root.render(this.el);
41123         
41124        
41125              
41126         this.items.each(function(f){
41127             f.render('x-form-el-'+f.id);
41128         });
41129
41130         if(this.buttons.length > 0){
41131             // tables are required to maintain order and for correct IE layout
41132             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
41133                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
41134                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
41135             }}, null, true);
41136             var tr = tb.getElementsByTagName('tr')[0];
41137             for(var i = 0, len = this.buttons.length; i < len; i++) {
41138                 var b = this.buttons[i];
41139                 var td = document.createElement('td');
41140                 td.className = 'x-form-btn-td';
41141                 b.render(tr.appendChild(td));
41142             }
41143         }
41144         if(this.monitorValid){ // initialize after render
41145             this.startMonitoring();
41146         }
41147         this.fireEvent('rendered', this);
41148         return this;
41149     },
41150
41151     /**
41152      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
41153      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
41154      * object or a valid Roo.DomHelper element config
41155      * @param {Function} handler The function called when the button is clicked
41156      * @param {Object} scope (optional) The scope of the handler function
41157      * @return {Roo.Button}
41158      */
41159     addButton : function(config, handler, scope){
41160         var bc = {
41161             handler: handler,
41162             scope: scope,
41163             minWidth: this.minButtonWidth,
41164             hideParent:true
41165         };
41166         if(typeof config == "string"){
41167             bc.text = config;
41168         }else{
41169             Roo.apply(bc, config);
41170         }
41171         var btn = new Roo.Button(null, bc);
41172         this.buttons.push(btn);
41173         return btn;
41174     },
41175
41176      /**
41177      * Adds a series of form elements (using the xtype property as the factory method.
41178      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
41179      * @param {Object} config 
41180      */
41181     
41182     addxtype : function()
41183     {
41184         var ar = Array.prototype.slice.call(arguments, 0);
41185         var ret = false;
41186         for(var i = 0; i < ar.length; i++) {
41187             if (!ar[i]) {
41188                 continue; // skip -- if this happends something invalid got sent, we 
41189                 // should ignore it, as basically that interface element will not show up
41190                 // and that should be pretty obvious!!
41191             }
41192             
41193             if (Roo.form[ar[i].xtype]) {
41194                 ar[i].form = this;
41195                 var fe = Roo.factory(ar[i], Roo.form);
41196                 if (!ret) {
41197                     ret = fe;
41198                 }
41199                 fe.form = this;
41200                 if (fe.store) {
41201                     fe.store.form = this;
41202                 }
41203                 if (fe.isLayout) {  
41204                          
41205                     this.start(fe);
41206                     this.allItems.push(fe);
41207                     if (fe.items && fe.addxtype) {
41208                         fe.addxtype.apply(fe, fe.items);
41209                         delete fe.items;
41210                     }
41211                      this.end();
41212                     continue;
41213                 }
41214                 
41215                 
41216                  
41217                 this.add(fe);
41218               //  console.log('adding ' + ar[i].xtype);
41219             }
41220             if (ar[i].xtype == 'Button') {  
41221                 //console.log('adding button');
41222                 //console.log(ar[i]);
41223                 this.addButton(ar[i]);
41224                 this.allItems.push(fe);
41225                 continue;
41226             }
41227             
41228             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
41229                 alert('end is not supported on xtype any more, use items');
41230             //    this.end();
41231             //    //console.log('adding end');
41232             }
41233             
41234         }
41235         return ret;
41236     },
41237     
41238     /**
41239      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
41240      * option "monitorValid"
41241      */
41242     startMonitoring : function(){
41243         if(!this.bound){
41244             this.bound = true;
41245             Roo.TaskMgr.start({
41246                 run : this.bindHandler,
41247                 interval : this.monitorPoll || 200,
41248                 scope: this
41249             });
41250         }
41251     },
41252
41253     /**
41254      * Stops monitoring of the valid state of this form
41255      */
41256     stopMonitoring : function(){
41257         this.bound = false;
41258     },
41259
41260     // private
41261     bindHandler : function(){
41262         if(!this.bound){
41263             return false; // stops binding
41264         }
41265         var valid = true;
41266         this.items.each(function(f){
41267             if(!f.isValid(true)){
41268                 valid = false;
41269                 return false;
41270             }
41271         });
41272         for(var i = 0, len = this.buttons.length; i < len; i++){
41273             var btn = this.buttons[i];
41274             if(btn.formBind === true && btn.disabled === valid){
41275                 btn.setDisabled(!valid);
41276             }
41277         }
41278         this.fireEvent('clientvalidation', this, valid);
41279     }
41280     
41281     
41282     
41283     
41284     
41285     
41286     
41287     
41288 });
41289
41290
41291 // back compat
41292 Roo.Form = Roo.form.Form;
41293 /*
41294  * Based on:
41295  * Ext JS Library 1.1.1
41296  * Copyright(c) 2006-2007, Ext JS, LLC.
41297  *
41298  * Originally Released Under LGPL - original licence link has changed is not relivant.
41299  *
41300  * Fork - LGPL
41301  * <script type="text/javascript">
41302  */
41303  
41304  /**
41305  * @class Roo.form.Action
41306  * Internal Class used to handle form actions
41307  * @constructor
41308  * @param {Roo.form.BasicForm} el The form element or its id
41309  * @param {Object} config Configuration options
41310  */
41311  
41312  
41313 // define the action interface
41314 Roo.form.Action = function(form, options){
41315     this.form = form;
41316     this.options = options || {};
41317 };
41318 /**
41319  * Client Validation Failed
41320  * @const 
41321  */
41322 Roo.form.Action.CLIENT_INVALID = 'client';
41323 /**
41324  * Server Validation Failed
41325  * @const 
41326  */
41327  Roo.form.Action.SERVER_INVALID = 'server';
41328  /**
41329  * Connect to Server Failed
41330  * @const 
41331  */
41332 Roo.form.Action.CONNECT_FAILURE = 'connect';
41333 /**
41334  * Reading Data from Server Failed
41335  * @const 
41336  */
41337 Roo.form.Action.LOAD_FAILURE = 'load';
41338
41339 Roo.form.Action.prototype = {
41340     type : 'default',
41341     failureType : undefined,
41342     response : undefined,
41343     result : undefined,
41344
41345     // interface method
41346     run : function(options){
41347
41348     },
41349
41350     // interface method
41351     success : function(response){
41352
41353     },
41354
41355     // interface method
41356     handleResponse : function(response){
41357
41358     },
41359
41360     // default connection failure
41361     failure : function(response){
41362         this.response = response;
41363         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41364         this.form.afterAction(this, false);
41365     },
41366
41367     processResponse : function(response){
41368         this.response = response;
41369         if(!response.responseText){
41370             return true;
41371         }
41372         this.result = this.handleResponse(response);
41373         return this.result;
41374     },
41375
41376     // utility functions used internally
41377     getUrl : function(appendParams){
41378         var url = this.options.url || this.form.url || this.form.el.dom.action;
41379         if(appendParams){
41380             var p = this.getParams();
41381             if(p){
41382                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
41383             }
41384         }
41385         return url;
41386     },
41387
41388     getMethod : function(){
41389         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
41390     },
41391
41392     getParams : function(){
41393         var bp = this.form.baseParams;
41394         var p = this.options.params;
41395         if(p){
41396             if(typeof p == "object"){
41397                 p = Roo.urlEncode(Roo.applyIf(p, bp));
41398             }else if(typeof p == 'string' && bp){
41399                 p += '&' + Roo.urlEncode(bp);
41400             }
41401         }else if(bp){
41402             p = Roo.urlEncode(bp);
41403         }
41404         return p;
41405     },
41406
41407     createCallback : function(){
41408         return {
41409             success: this.success,
41410             failure: this.failure,
41411             scope: this,
41412             timeout: (this.form.timeout*1000),
41413             upload: this.form.fileUpload ? this.success : undefined
41414         };
41415     }
41416 };
41417
41418 Roo.form.Action.Submit = function(form, options){
41419     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
41420 };
41421
41422 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
41423     type : 'submit',
41424
41425     haveProgress : false,
41426     uploadComplete : false,
41427     
41428     // uploadProgress indicator.
41429     uploadProgress : function()
41430     {
41431         if (!this.form.progressUrl) {
41432             return;
41433         }
41434         
41435         if (!this.haveProgress) {
41436             Roo.MessageBox.progress("Uploading", "Uploading");
41437         }
41438         if (this.uploadComplete) {
41439            Roo.MessageBox.hide();
41440            return;
41441         }
41442         
41443         this.haveProgress = true;
41444    
41445         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
41446         
41447         var c = new Roo.data.Connection();
41448         c.request({
41449             url : this.form.progressUrl,
41450             params: {
41451                 id : uid
41452             },
41453             method: 'GET',
41454             success : function(req){
41455                //console.log(data);
41456                 var rdata = false;
41457                 var edata;
41458                 try  {
41459                    rdata = Roo.decode(req.responseText)
41460                 } catch (e) {
41461                     Roo.log("Invalid data from server..");
41462                     Roo.log(edata);
41463                     return;
41464                 }
41465                 if (!rdata || !rdata.success) {
41466                     Roo.log(rdata);
41467                     return;
41468                 }
41469                 var data = rdata.data;
41470                 
41471                 if (this.uploadComplete) {
41472                    Roo.MessageBox.hide();
41473                    return;
41474                 }
41475                    
41476                 if (data){
41477                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
41478                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
41479                     );
41480                 }
41481                 this.uploadProgress.defer(2000,this);
41482             },
41483        
41484             failure: function(data) {
41485                 Roo.log('progress url failed ');
41486                 Roo.log(data);
41487             },
41488             scope : this
41489         });
41490            
41491     },
41492     
41493     
41494     run : function()
41495     {
41496         // run get Values on the form, so it syncs any secondary forms.
41497         this.form.getValues();
41498         
41499         var o = this.options;
41500         var method = this.getMethod();
41501         var isPost = method == 'POST';
41502         if(o.clientValidation === false || this.form.isValid()){
41503             
41504             if (this.form.progressUrl) {
41505                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
41506                     (new Date() * 1) + '' + Math.random());
41507                     
41508             } 
41509             
41510             Roo.Ajax.request(Roo.apply(this.createCallback(), {
41511                 form:this.form.el.dom,
41512                 url:this.getUrl(!isPost),
41513                 method: method,
41514                 params:isPost ? this.getParams() : null,
41515                 isUpload: this.form.fileUpload
41516             }));
41517             
41518             this.uploadProgress();
41519
41520         }else if (o.clientValidation !== false){ // client validation failed
41521             this.failureType = Roo.form.Action.CLIENT_INVALID;
41522             this.form.afterAction(this, false);
41523         }
41524     },
41525
41526     success : function(response)
41527     {
41528         this.uploadComplete= true;
41529         if (this.haveProgress) {
41530             Roo.MessageBox.hide();
41531         }
41532         
41533         var result = this.processResponse(response);
41534         if(result === true || result.success){
41535             this.form.afterAction(this, true);
41536             return;
41537         }
41538         if(result.errors){
41539             this.form.markInvalid(result.errors);
41540             this.failureType = Roo.form.Action.SERVER_INVALID;
41541         }
41542         this.form.afterAction(this, false);
41543     },
41544     failure : function(response)
41545     {
41546         this.uploadComplete= true;
41547         if (this.haveProgress) {
41548             Roo.MessageBox.hide();
41549         }
41550         
41551         this.response = response;
41552         this.failureType = Roo.form.Action.CONNECT_FAILURE;
41553         this.form.afterAction(this, false);
41554     },
41555     
41556     handleResponse : function(response){
41557         if(this.form.errorReader){
41558             var rs = this.form.errorReader.read(response);
41559             var errors = [];
41560             if(rs.records){
41561                 for(var i = 0, len = rs.records.length; i < len; i++) {
41562                     var r = rs.records[i];
41563                     errors[i] = r.data;
41564                 }
41565             }
41566             if(errors.length < 1){
41567                 errors = null;
41568             }
41569             return {
41570                 success : rs.success,
41571                 errors : errors
41572             };
41573         }
41574         var ret = false;
41575         try {
41576             ret = Roo.decode(response.responseText);
41577         } catch (e) {
41578             ret = {
41579                 success: false,
41580                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
41581                 errors : []
41582             };
41583         }
41584         return ret;
41585         
41586     }
41587 });
41588
41589
41590 Roo.form.Action.Load = function(form, options){
41591     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
41592     this.reader = this.form.reader;
41593 };
41594
41595 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
41596     type : 'load',
41597
41598     run : function(){
41599         Roo.Ajax.request(Roo.apply(
41600                 this.createCallback(), {
41601                     method:this.getMethod(),
41602                     url:this.getUrl(false),
41603                     params:this.getParams()
41604         }));
41605     },
41606
41607     success : function(response){
41608         var result = this.processResponse(response);
41609         if(result === true || !result.success || !result.data){
41610             this.failureType = Roo.form.Action.LOAD_FAILURE;
41611             this.form.afterAction(this, false);
41612             return;
41613         }
41614         this.form.clearInvalid();
41615         this.form.setValues(result.data);
41616         this.form.afterAction(this, true);
41617     },
41618
41619     handleResponse : function(response){
41620         if(this.form.reader){
41621             var rs = this.form.reader.read(response);
41622             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
41623             return {
41624                 success : rs.success,
41625                 data : data
41626             };
41627         }
41628         return Roo.decode(response.responseText);
41629     }
41630 });
41631
41632 Roo.form.Action.ACTION_TYPES = {
41633     'load' : Roo.form.Action.Load,
41634     'submit' : Roo.form.Action.Submit
41635 };/*
41636  * Based on:
41637  * Ext JS Library 1.1.1
41638  * Copyright(c) 2006-2007, Ext JS, LLC.
41639  *
41640  * Originally Released Under LGPL - original licence link has changed is not relivant.
41641  *
41642  * Fork - LGPL
41643  * <script type="text/javascript">
41644  */
41645  
41646 /**
41647  * @class Roo.form.Layout
41648  * @extends Roo.Component
41649  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
41650  * @constructor
41651  * @param {Object} config Configuration options
41652  */
41653 Roo.form.Layout = function(config){
41654     var xitems = [];
41655     if (config.items) {
41656         xitems = config.items;
41657         delete config.items;
41658     }
41659     Roo.form.Layout.superclass.constructor.call(this, config);
41660     this.stack = [];
41661     Roo.each(xitems, this.addxtype, this);
41662      
41663 };
41664
41665 Roo.extend(Roo.form.Layout, Roo.Component, {
41666     /**
41667      * @cfg {String/Object} autoCreate
41668      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
41669      */
41670     /**
41671      * @cfg {String/Object/Function} style
41672      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
41673      * a function which returns such a specification.
41674      */
41675     /**
41676      * @cfg {String} labelAlign
41677      * Valid values are "left," "top" and "right" (defaults to "left")
41678      */
41679     /**
41680      * @cfg {Number} labelWidth
41681      * Fixed width in pixels of all field labels (defaults to undefined)
41682      */
41683     /**
41684      * @cfg {Boolean} clear
41685      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
41686      */
41687     clear : true,
41688     /**
41689      * @cfg {String} labelSeparator
41690      * The separator to use after field labels (defaults to ':')
41691      */
41692     labelSeparator : ':',
41693     /**
41694      * @cfg {Boolean} hideLabels
41695      * True to suppress the display of field labels in this layout (defaults to false)
41696      */
41697     hideLabels : false,
41698
41699     // private
41700     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
41701     
41702     isLayout : true,
41703     
41704     // private
41705     onRender : function(ct, position){
41706         if(this.el){ // from markup
41707             this.el = Roo.get(this.el);
41708         }else {  // generate
41709             var cfg = this.getAutoCreate();
41710             this.el = ct.createChild(cfg, position);
41711         }
41712         if(this.style){
41713             this.el.applyStyles(this.style);
41714         }
41715         if(this.labelAlign){
41716             this.el.addClass('x-form-label-'+this.labelAlign);
41717         }
41718         if(this.hideLabels){
41719             this.labelStyle = "display:none";
41720             this.elementStyle = "padding-left:0;";
41721         }else{
41722             if(typeof this.labelWidth == 'number'){
41723                 this.labelStyle = "width:"+this.labelWidth+"px;";
41724                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
41725             }
41726             if(this.labelAlign == 'top'){
41727                 this.labelStyle = "width:auto;";
41728                 this.elementStyle = "padding-left:0;";
41729             }
41730         }
41731         var stack = this.stack;
41732         var slen = stack.length;
41733         if(slen > 0){
41734             if(!this.fieldTpl){
41735                 var t = new Roo.Template(
41736                     '<div class="x-form-item {5}">',
41737                         '<label for="{0}" style="{2}">{1}{4}</label>',
41738                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41739                         '</div>',
41740                     '</div><div class="x-form-clear-left"></div>'
41741                 );
41742                 t.disableFormats = true;
41743                 t.compile();
41744                 Roo.form.Layout.prototype.fieldTpl = t;
41745             }
41746             for(var i = 0; i < slen; i++) {
41747                 if(stack[i].isFormField){
41748                     this.renderField(stack[i]);
41749                 }else{
41750                     this.renderComponent(stack[i]);
41751                 }
41752             }
41753         }
41754         if(this.clear){
41755             this.el.createChild({cls:'x-form-clear'});
41756         }
41757     },
41758
41759     // private
41760     renderField : function(f){
41761         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
41762                f.id, //0
41763                f.fieldLabel, //1
41764                f.labelStyle||this.labelStyle||'', //2
41765                this.elementStyle||'', //3
41766                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
41767                f.itemCls||this.itemCls||''  //5
41768        ], true).getPrevSibling());
41769     },
41770
41771     // private
41772     renderComponent : function(c){
41773         c.render(c.isLayout ? this.el : this.el.createChild());    
41774     },
41775     /**
41776      * Adds a object form elements (using the xtype property as the factory method.)
41777      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
41778      * @param {Object} config 
41779      */
41780     addxtype : function(o)
41781     {
41782         // create the lement.
41783         o.form = this.form;
41784         var fe = Roo.factory(o, Roo.form);
41785         this.form.allItems.push(fe);
41786         this.stack.push(fe);
41787         
41788         if (fe.isFormField) {
41789             this.form.items.add(fe);
41790         }
41791          
41792         return fe;
41793     }
41794 });
41795
41796 /**
41797  * @class Roo.form.Column
41798  * @extends Roo.form.Layout
41799  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
41800  * @constructor
41801  * @param {Object} config Configuration options
41802  */
41803 Roo.form.Column = function(config){
41804     Roo.form.Column.superclass.constructor.call(this, config);
41805 };
41806
41807 Roo.extend(Roo.form.Column, Roo.form.Layout, {
41808     /**
41809      * @cfg {Number/String} width
41810      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41811      */
41812     /**
41813      * @cfg {String/Object} autoCreate
41814      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
41815      */
41816
41817     // private
41818     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
41819
41820     // private
41821     onRender : function(ct, position){
41822         Roo.form.Column.superclass.onRender.call(this, ct, position);
41823         if(this.width){
41824             this.el.setWidth(this.width);
41825         }
41826     }
41827 });
41828
41829
41830 /**
41831  * @class Roo.form.Row
41832  * @extends Roo.form.Layout
41833  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
41834  * @constructor
41835  * @param {Object} config Configuration options
41836  */
41837
41838  
41839 Roo.form.Row = function(config){
41840     Roo.form.Row.superclass.constructor.call(this, config);
41841 };
41842  
41843 Roo.extend(Roo.form.Row, Roo.form.Layout, {
41844       /**
41845      * @cfg {Number/String} width
41846      * The fixed width of the column in pixels or CSS value (defaults to "auto")
41847      */
41848     /**
41849      * @cfg {Number/String} height
41850      * The fixed height of the column in pixels or CSS value (defaults to "auto")
41851      */
41852     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
41853     
41854     padWidth : 20,
41855     // private
41856     onRender : function(ct, position){
41857         //console.log('row render');
41858         if(!this.rowTpl){
41859             var t = new Roo.Template(
41860                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
41861                     '<label for="{0}" style="{2}">{1}{4}</label>',
41862                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
41863                     '</div>',
41864                 '</div>'
41865             );
41866             t.disableFormats = true;
41867             t.compile();
41868             Roo.form.Layout.prototype.rowTpl = t;
41869         }
41870         this.fieldTpl = this.rowTpl;
41871         
41872         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
41873         var labelWidth = 100;
41874         
41875         if ((this.labelAlign != 'top')) {
41876             if (typeof this.labelWidth == 'number') {
41877                 labelWidth = this.labelWidth
41878             }
41879             this.padWidth =  20 + labelWidth;
41880             
41881         }
41882         
41883         Roo.form.Column.superclass.onRender.call(this, ct, position);
41884         if(this.width){
41885             this.el.setWidth(this.width);
41886         }
41887         if(this.height){
41888             this.el.setHeight(this.height);
41889         }
41890     },
41891     
41892     // private
41893     renderField : function(f){
41894         f.fieldEl = this.fieldTpl.append(this.el, [
41895                f.id, f.fieldLabel,
41896                f.labelStyle||this.labelStyle||'',
41897                this.elementStyle||'',
41898                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
41899                f.itemCls||this.itemCls||'',
41900                f.width ? f.width + this.padWidth : 160 + this.padWidth
41901        ],true);
41902     }
41903 });
41904  
41905
41906 /**
41907  * @class Roo.form.FieldSet
41908  * @extends Roo.form.Layout
41909  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
41910  * @constructor
41911  * @param {Object} config Configuration options
41912  */
41913 Roo.form.FieldSet = function(config){
41914     Roo.form.FieldSet.superclass.constructor.call(this, config);
41915 };
41916
41917 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
41918     /**
41919      * @cfg {String} legend
41920      * The text to display as the legend for the FieldSet (defaults to '')
41921      */
41922     /**
41923      * @cfg {String/Object} autoCreate
41924      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
41925      */
41926
41927     // private
41928     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
41929
41930     // private
41931     onRender : function(ct, position){
41932         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
41933         if(this.legend){
41934             this.setLegend(this.legend);
41935         }
41936     },
41937
41938     // private
41939     setLegend : function(text){
41940         if(this.rendered){
41941             this.el.child('legend').update(text);
41942         }
41943     }
41944 });/*
41945  * Based on:
41946  * Ext JS Library 1.1.1
41947  * Copyright(c) 2006-2007, Ext JS, LLC.
41948  *
41949  * Originally Released Under LGPL - original licence link has changed is not relivant.
41950  *
41951  * Fork - LGPL
41952  * <script type="text/javascript">
41953  */
41954 /**
41955  * @class Roo.form.VTypes
41956  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
41957  * @singleton
41958  */
41959 Roo.form.VTypes = function(){
41960     // closure these in so they are only created once.
41961     var alpha = /^[a-zA-Z_]+$/;
41962     var alphanum = /^[a-zA-Z0-9_]+$/;
41963     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
41964     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
41965
41966     // All these messages and functions are configurable
41967     return {
41968         /**
41969          * The function used to validate email addresses
41970          * @param {String} value The email address
41971          */
41972         'email' : function(v){
41973             return email.test(v);
41974         },
41975         /**
41976          * The error text to display when the email validation function returns false
41977          * @type String
41978          */
41979         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
41980         /**
41981          * The keystroke filter mask to be applied on email input
41982          * @type RegExp
41983          */
41984         'emailMask' : /[a-z0-9_\.\-@]/i,
41985
41986         /**
41987          * The function used to validate URLs
41988          * @param {String} value The URL
41989          */
41990         'url' : function(v){
41991             return url.test(v);
41992         },
41993         /**
41994          * The error text to display when the url validation function returns false
41995          * @type String
41996          */
41997         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
41998         
41999         /**
42000          * The function used to validate alpha values
42001          * @param {String} value The value
42002          */
42003         'alpha' : function(v){
42004             return alpha.test(v);
42005         },
42006         /**
42007          * The error text to display when the alpha validation function returns false
42008          * @type String
42009          */
42010         'alphaText' : 'This field should only contain letters and _',
42011         /**
42012          * The keystroke filter mask to be applied on alpha input
42013          * @type RegExp
42014          */
42015         'alphaMask' : /[a-z_]/i,
42016
42017         /**
42018          * The function used to validate alphanumeric values
42019          * @param {String} value The value
42020          */
42021         'alphanum' : function(v){
42022             return alphanum.test(v);
42023         },
42024         /**
42025          * The error text to display when the alphanumeric validation function returns false
42026          * @type String
42027          */
42028         'alphanumText' : 'This field should only contain letters, numbers and _',
42029         /**
42030          * The keystroke filter mask to be applied on alphanumeric input
42031          * @type RegExp
42032          */
42033         'alphanumMask' : /[a-z0-9_]/i
42034     };
42035 }();//<script type="text/javascript">
42036
42037 /**
42038  * @class Roo.form.FCKeditor
42039  * @extends Roo.form.TextArea
42040  * Wrapper around the FCKEditor http://www.fckeditor.net
42041  * @constructor
42042  * Creates a new FCKeditor
42043  * @param {Object} config Configuration options
42044  */
42045 Roo.form.FCKeditor = function(config){
42046     Roo.form.FCKeditor.superclass.constructor.call(this, config);
42047     this.addEvents({
42048          /**
42049          * @event editorinit
42050          * Fired when the editor is initialized - you can add extra handlers here..
42051          * @param {FCKeditor} this
42052          * @param {Object} the FCK object.
42053          */
42054         editorinit : true
42055     });
42056     
42057     
42058 };
42059 Roo.form.FCKeditor.editors = { };
42060 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
42061 {
42062     //defaultAutoCreate : {
42063     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
42064     //},
42065     // private
42066     /**
42067      * @cfg {Object} fck options - see fck manual for details.
42068      */
42069     fckconfig : false,
42070     
42071     /**
42072      * @cfg {Object} fck toolbar set (Basic or Default)
42073      */
42074     toolbarSet : 'Basic',
42075     /**
42076      * @cfg {Object} fck BasePath
42077      */ 
42078     basePath : '/fckeditor/',
42079     
42080     
42081     frame : false,
42082     
42083     value : '',
42084     
42085    
42086     onRender : function(ct, position)
42087     {
42088         if(!this.el){
42089             this.defaultAutoCreate = {
42090                 tag: "textarea",
42091                 style:"width:300px;height:60px;",
42092                 autocomplete: "off"
42093             };
42094         }
42095         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
42096         /*
42097         if(this.grow){
42098             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
42099             if(this.preventScrollbars){
42100                 this.el.setStyle("overflow", "hidden");
42101             }
42102             this.el.setHeight(this.growMin);
42103         }
42104         */
42105         //console.log('onrender' + this.getId() );
42106         Roo.form.FCKeditor.editors[this.getId()] = this;
42107          
42108
42109         this.replaceTextarea() ;
42110         
42111     },
42112     
42113     getEditor : function() {
42114         return this.fckEditor;
42115     },
42116     /**
42117      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
42118      * @param {Mixed} value The value to set
42119      */
42120     
42121     
42122     setValue : function(value)
42123     {
42124         //console.log('setValue: ' + value);
42125         
42126         if(typeof(value) == 'undefined') { // not sure why this is happending...
42127             return;
42128         }
42129         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42130         
42131         //if(!this.el || !this.getEditor()) {
42132         //    this.value = value;
42133             //this.setValue.defer(100,this,[value]);    
42134         //    return;
42135         //} 
42136         
42137         if(!this.getEditor()) {
42138             return;
42139         }
42140         
42141         this.getEditor().SetData(value);
42142         
42143         //
42144
42145     },
42146
42147     /**
42148      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
42149      * @return {Mixed} value The field value
42150      */
42151     getValue : function()
42152     {
42153         
42154         if (this.frame && this.frame.dom.style.display == 'none') {
42155             return Roo.form.FCKeditor.superclass.getValue.call(this);
42156         }
42157         
42158         if(!this.el || !this.getEditor()) {
42159            
42160            // this.getValue.defer(100,this); 
42161             return this.value;
42162         }
42163        
42164         
42165         var value=this.getEditor().GetData();
42166         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
42167         return Roo.form.FCKeditor.superclass.getValue.call(this);
42168         
42169
42170     },
42171
42172     /**
42173      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
42174      * @return {Mixed} value The field value
42175      */
42176     getRawValue : function()
42177     {
42178         if (this.frame && this.frame.dom.style.display == 'none') {
42179             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42180         }
42181         
42182         if(!this.el || !this.getEditor()) {
42183             //this.getRawValue.defer(100,this); 
42184             return this.value;
42185             return;
42186         }
42187         
42188         
42189         
42190         var value=this.getEditor().GetData();
42191         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
42192         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
42193          
42194     },
42195     
42196     setSize : function(w,h) {
42197         
42198         
42199         
42200         //if (this.frame && this.frame.dom.style.display == 'none') {
42201         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42202         //    return;
42203         //}
42204         //if(!this.el || !this.getEditor()) {
42205         //    this.setSize.defer(100,this, [w,h]); 
42206         //    return;
42207         //}
42208         
42209         
42210         
42211         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
42212         
42213         this.frame.dom.setAttribute('width', w);
42214         this.frame.dom.setAttribute('height', h);
42215         this.frame.setSize(w,h);
42216         
42217     },
42218     
42219     toggleSourceEdit : function(value) {
42220         
42221       
42222          
42223         this.el.dom.style.display = value ? '' : 'none';
42224         this.frame.dom.style.display = value ?  'none' : '';
42225         
42226     },
42227     
42228     
42229     focus: function(tag)
42230     {
42231         if (this.frame.dom.style.display == 'none') {
42232             return Roo.form.FCKeditor.superclass.focus.call(this);
42233         }
42234         if(!this.el || !this.getEditor()) {
42235             this.focus.defer(100,this, [tag]); 
42236             return;
42237         }
42238         
42239         
42240         
42241         
42242         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
42243         this.getEditor().Focus();
42244         if (tgs.length) {
42245             if (!this.getEditor().Selection.GetSelection()) {
42246                 this.focus.defer(100,this, [tag]); 
42247                 return;
42248             }
42249             
42250             
42251             var r = this.getEditor().EditorDocument.createRange();
42252             r.setStart(tgs[0],0);
42253             r.setEnd(tgs[0],0);
42254             this.getEditor().Selection.GetSelection().removeAllRanges();
42255             this.getEditor().Selection.GetSelection().addRange(r);
42256             this.getEditor().Focus();
42257         }
42258         
42259     },
42260     
42261     
42262     
42263     replaceTextarea : function()
42264     {
42265         if ( document.getElementById( this.getId() + '___Frame' ) )
42266             return ;
42267         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
42268         //{
42269             // We must check the elements firstly using the Id and then the name.
42270         var oTextarea = document.getElementById( this.getId() );
42271         
42272         var colElementsByName = document.getElementsByName( this.getId() ) ;
42273          
42274         oTextarea.style.display = 'none' ;
42275
42276         if ( oTextarea.tabIndex ) {            
42277             this.TabIndex = oTextarea.tabIndex ;
42278         }
42279         
42280         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
42281         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
42282         this.frame = Roo.get(this.getId() + '___Frame')
42283     },
42284     
42285     _getConfigHtml : function()
42286     {
42287         var sConfig = '' ;
42288
42289         for ( var o in this.fckconfig ) {
42290             sConfig += sConfig.length > 0  ? '&amp;' : '';
42291             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
42292         }
42293
42294         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
42295     },
42296     
42297     
42298     _getIFrameHtml : function()
42299     {
42300         var sFile = 'fckeditor.html' ;
42301         /* no idea what this is about..
42302         try
42303         {
42304             if ( (/fcksource=true/i).test( window.top.location.search ) )
42305                 sFile = 'fckeditor.original.html' ;
42306         }
42307         catch (e) { 
42308         */
42309
42310         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
42311         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
42312         
42313         
42314         var html = '<iframe id="' + this.getId() +
42315             '___Frame" src="' + sLink +
42316             '" width="' + this.width +
42317             '" height="' + this.height + '"' +
42318             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
42319             ' frameborder="0" scrolling="no"></iframe>' ;
42320
42321         return html ;
42322     },
42323     
42324     _insertHtmlBefore : function( html, element )
42325     {
42326         if ( element.insertAdjacentHTML )       {
42327             // IE
42328             element.insertAdjacentHTML( 'beforeBegin', html ) ;
42329         } else { // Gecko
42330             var oRange = document.createRange() ;
42331             oRange.setStartBefore( element ) ;
42332             var oFragment = oRange.createContextualFragment( html );
42333             element.parentNode.insertBefore( oFragment, element ) ;
42334         }
42335     }
42336     
42337     
42338   
42339     
42340     
42341     
42342     
42343
42344 });
42345
42346 //Roo.reg('fckeditor', Roo.form.FCKeditor);
42347
42348 function FCKeditor_OnComplete(editorInstance){
42349     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
42350     f.fckEditor = editorInstance;
42351     //console.log("loaded");
42352     f.fireEvent('editorinit', f, editorInstance);
42353
42354   
42355
42356  
42357
42358
42359
42360
42361
42362
42363
42364
42365
42366
42367
42368
42369
42370
42371
42372 //<script type="text/javascript">
42373 /**
42374  * @class Roo.form.GridField
42375  * @extends Roo.form.Field
42376  * Embed a grid (or editable grid into a form)
42377  * STATUS ALPHA
42378  * 
42379  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
42380  * it needs 
42381  * xgrid.store = Roo.data.Store
42382  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
42383  * xgrid.store.reader = Roo.data.JsonReader 
42384  * 
42385  * 
42386  * @constructor
42387  * Creates a new GridField
42388  * @param {Object} config Configuration options
42389  */
42390 Roo.form.GridField = function(config){
42391     Roo.form.GridField.superclass.constructor.call(this, config);
42392      
42393 };
42394
42395 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
42396     /**
42397      * @cfg {Number} width  - used to restrict width of grid..
42398      */
42399     width : 100,
42400     /**
42401      * @cfg {Number} height - used to restrict height of grid..
42402      */
42403     height : 50,
42404      /**
42405      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
42406          * 
42407          *}
42408      */
42409     xgrid : false, 
42410     /**
42411      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42412      * {tag: "input", type: "checkbox", autocomplete: "off"})
42413      */
42414    // defaultAutoCreate : { tag: 'div' },
42415     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42416     /**
42417      * @cfg {String} addTitle Text to include for adding a title.
42418      */
42419     addTitle : false,
42420     //
42421     onResize : function(){
42422         Roo.form.Field.superclass.onResize.apply(this, arguments);
42423     },
42424
42425     initEvents : function(){
42426         // Roo.form.Checkbox.superclass.initEvents.call(this);
42427         // has no events...
42428        
42429     },
42430
42431
42432     getResizeEl : function(){
42433         return this.wrap;
42434     },
42435
42436     getPositionEl : function(){
42437         return this.wrap;
42438     },
42439
42440     // private
42441     onRender : function(ct, position){
42442         
42443         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
42444         var style = this.style;
42445         delete this.style;
42446         
42447         Roo.form.GridField.superclass.onRender.call(this, ct, position);
42448         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
42449         this.viewEl = this.wrap.createChild({ tag: 'div' });
42450         if (style) {
42451             this.viewEl.applyStyles(style);
42452         }
42453         if (this.width) {
42454             this.viewEl.setWidth(this.width);
42455         }
42456         if (this.height) {
42457             this.viewEl.setHeight(this.height);
42458         }
42459         //if(this.inputValue !== undefined){
42460         //this.setValue(this.value);
42461         
42462         
42463         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
42464         
42465         
42466         this.grid.render();
42467         this.grid.getDataSource().on('remove', this.refreshValue, this);
42468         this.grid.getDataSource().on('update', this.refreshValue, this);
42469         this.grid.on('afteredit', this.refreshValue, this);
42470  
42471     },
42472      
42473     
42474     /**
42475      * Sets the value of the item. 
42476      * @param {String} either an object  or a string..
42477      */
42478     setValue : function(v){
42479         //this.value = v;
42480         v = v || []; // empty set..
42481         // this does not seem smart - it really only affects memoryproxy grids..
42482         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
42483             var ds = this.grid.getDataSource();
42484             // assumes a json reader..
42485             var data = {}
42486             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
42487             ds.loadData( data);
42488         }
42489         Roo.form.GridField.superclass.setValue.call(this, v);
42490         this.refreshValue();
42491         // should load data in the grid really....
42492     },
42493     
42494     // private
42495     refreshValue: function() {
42496          var val = [];
42497         this.grid.getDataSource().each(function(r) {
42498             val.push(r.data);
42499         });
42500         this.el.dom.value = Roo.encode(val);
42501     }
42502     
42503      
42504     
42505     
42506 });/*
42507  * Based on:
42508  * Ext JS Library 1.1.1
42509  * Copyright(c) 2006-2007, Ext JS, LLC.
42510  *
42511  * Originally Released Under LGPL - original licence link has changed is not relivant.
42512  *
42513  * Fork - LGPL
42514  * <script type="text/javascript">
42515  */
42516 /**
42517  * @class Roo.form.DisplayField
42518  * @extends Roo.form.Field
42519  * A generic Field to display non-editable data.
42520  * @constructor
42521  * Creates a new Display Field item.
42522  * @param {Object} config Configuration options
42523  */
42524 Roo.form.DisplayField = function(config){
42525     Roo.form.DisplayField.superclass.constructor.call(this, config);
42526     
42527 };
42528
42529 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
42530     inputType:      'hidden',
42531     allowBlank:     true,
42532     readOnly:         true,
42533     
42534  
42535     /**
42536      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
42537      */
42538     focusClass : undefined,
42539     /**
42540      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
42541      */
42542     fieldClass: 'x-form-field',
42543     
42544      /**
42545      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
42546      */
42547     valueRenderer: undefined,
42548     
42549     width: 100,
42550     /**
42551      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
42552      * {tag: "input", type: "checkbox", autocomplete: "off"})
42553      */
42554      
42555  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
42556
42557     onResize : function(){
42558         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
42559         
42560     },
42561
42562     initEvents : function(){
42563         // Roo.form.Checkbox.superclass.initEvents.call(this);
42564         // has no events...
42565        
42566     },
42567
42568
42569     getResizeEl : function(){
42570         return this.wrap;
42571     },
42572
42573     getPositionEl : function(){
42574         return this.wrap;
42575     },
42576
42577     // private
42578     onRender : function(ct, position){
42579         
42580         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
42581         //if(this.inputValue !== undefined){
42582         this.wrap = this.el.wrap();
42583         
42584         this.viewEl = this.wrap.createChild({ tag: 'div'});
42585         
42586         if (this.bodyStyle) {
42587             this.viewEl.applyStyles(this.bodyStyle);
42588         }
42589         //this.viewEl.setStyle('padding', '2px');
42590         
42591         this.setValue(this.value);
42592         
42593     },
42594 /*
42595     // private
42596     initValue : Roo.emptyFn,
42597
42598   */
42599
42600         // private
42601     onClick : function(){
42602         
42603     },
42604
42605     /**
42606      * Sets the checked state of the checkbox.
42607      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
42608      */
42609     setValue : function(v){
42610         this.value = v;
42611         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
42612         // this might be called before we have a dom element..
42613         if (!this.viewEl) {
42614             return;
42615         }
42616         this.viewEl.dom.innerHTML = html;
42617         Roo.form.DisplayField.superclass.setValue.call(this, v);
42618
42619     }
42620 });//<script type="text/javasscript">
42621  
42622
42623 /**
42624  * @class Roo.DDView
42625  * A DnD enabled version of Roo.View.
42626  * @param {Element/String} container The Element in which to create the View.
42627  * @param {String} tpl The template string used to create the markup for each element of the View
42628  * @param {Object} config The configuration properties. These include all the config options of
42629  * {@link Roo.View} plus some specific to this class.<br>
42630  * <p>
42631  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
42632  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
42633  * <p>
42634  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
42635 .x-view-drag-insert-above {
42636         border-top:1px dotted #3366cc;
42637 }
42638 .x-view-drag-insert-below {
42639         border-bottom:1px dotted #3366cc;
42640 }
42641 </code></pre>
42642  * 
42643  */
42644  
42645 Roo.DDView = function(container, tpl, config) {
42646     Roo.DDView.superclass.constructor.apply(this, arguments);
42647     this.getEl().setStyle("outline", "0px none");
42648     this.getEl().unselectable();
42649     if (this.dragGroup) {
42650                 this.setDraggable(this.dragGroup.split(","));
42651     }
42652     if (this.dropGroup) {
42653                 this.setDroppable(this.dropGroup.split(","));
42654     }
42655     if (this.deletable) {
42656         this.setDeletable();
42657     }
42658     this.isDirtyFlag = false;
42659         this.addEvents({
42660                 "drop" : true
42661         });
42662 };
42663
42664 Roo.extend(Roo.DDView, Roo.View, {
42665 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
42666 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
42667 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
42668 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
42669
42670         isFormField: true,
42671
42672         reset: Roo.emptyFn,
42673         
42674         clearInvalid: Roo.form.Field.prototype.clearInvalid,
42675
42676         validate: function() {
42677                 return true;
42678         },
42679         
42680         destroy: function() {
42681                 this.purgeListeners();
42682                 this.getEl.removeAllListeners();
42683                 this.getEl().remove();
42684                 if (this.dragZone) {
42685                         if (this.dragZone.destroy) {
42686                                 this.dragZone.destroy();
42687                         }
42688                 }
42689                 if (this.dropZone) {
42690                         if (this.dropZone.destroy) {
42691                                 this.dropZone.destroy();
42692                         }
42693                 }
42694         },
42695
42696 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
42697         getName: function() {
42698                 return this.name;
42699         },
42700
42701 /**     Loads the View from a JSON string representing the Records to put into the Store. */
42702         setValue: function(v) {
42703                 if (!this.store) {
42704                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
42705                 }
42706                 var data = {};
42707                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
42708                 this.store.proxy = new Roo.data.MemoryProxy(data);
42709                 this.store.load();
42710         },
42711
42712 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
42713         getValue: function() {
42714                 var result = '(';
42715                 this.store.each(function(rec) {
42716                         result += rec.id + ',';
42717                 });
42718                 return result.substr(0, result.length - 1) + ')';
42719         },
42720         
42721         getIds: function() {
42722                 var i = 0, result = new Array(this.store.getCount());
42723                 this.store.each(function(rec) {
42724                         result[i++] = rec.id;
42725                 });
42726                 return result;
42727         },
42728         
42729         isDirty: function() {
42730                 return this.isDirtyFlag;
42731         },
42732
42733 /**
42734  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
42735  *      whole Element becomes the target, and this causes the drop gesture to append.
42736  */
42737     getTargetFromEvent : function(e) {
42738                 var target = e.getTarget();
42739                 while ((target !== null) && (target.parentNode != this.el.dom)) {
42740                 target = target.parentNode;
42741                 }
42742                 if (!target) {
42743                         target = this.el.dom.lastChild || this.el.dom;
42744                 }
42745                 return target;
42746     },
42747
42748 /**
42749  *      Create the drag data which consists of an object which has the property "ddel" as
42750  *      the drag proxy element. 
42751  */
42752     getDragData : function(e) {
42753         var target = this.findItemFromChild(e.getTarget());
42754                 if(target) {
42755                         this.handleSelection(e);
42756                         var selNodes = this.getSelectedNodes();
42757             var dragData = {
42758                 source: this,
42759                 copy: this.copy || (this.allowCopy && e.ctrlKey),
42760                 nodes: selNodes,
42761                 records: []
42762                         };
42763                         var selectedIndices = this.getSelectedIndexes();
42764                         for (var i = 0; i < selectedIndices.length; i++) {
42765                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
42766                         }
42767                         if (selNodes.length == 1) {
42768                                 dragData.ddel = target.cloneNode(true); // the div element
42769                         } else {
42770                                 var div = document.createElement('div'); // create the multi element drag "ghost"
42771                                 div.className = 'multi-proxy';
42772                                 for (var i = 0, len = selNodes.length; i < len; i++) {
42773                                         div.appendChild(selNodes[i].cloneNode(true));
42774                                 }
42775                                 dragData.ddel = div;
42776                         }
42777             //console.log(dragData)
42778             //console.log(dragData.ddel.innerHTML)
42779                         return dragData;
42780                 }
42781         //console.log('nodragData')
42782                 return false;
42783     },
42784     
42785 /**     Specify to which ddGroup items in this DDView may be dragged. */
42786     setDraggable: function(ddGroup) {
42787         if (ddGroup instanceof Array) {
42788                 Roo.each(ddGroup, this.setDraggable, this);
42789                 return;
42790         }
42791         if (this.dragZone) {
42792                 this.dragZone.addToGroup(ddGroup);
42793         } else {
42794                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
42795                                 containerScroll: true,
42796                                 ddGroup: ddGroup 
42797
42798                         });
42799 //                      Draggability implies selection. DragZone's mousedown selects the element.
42800                         if (!this.multiSelect) { this.singleSelect = true; }
42801
42802 //                      Wire the DragZone's handlers up to methods in *this*
42803                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
42804                 }
42805     },
42806
42807 /**     Specify from which ddGroup this DDView accepts drops. */
42808     setDroppable: function(ddGroup) {
42809         if (ddGroup instanceof Array) {
42810                 Roo.each(ddGroup, this.setDroppable, this);
42811                 return;
42812         }
42813         if (this.dropZone) {
42814                 this.dropZone.addToGroup(ddGroup);
42815         } else {
42816                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
42817                                 containerScroll: true,
42818                                 ddGroup: ddGroup
42819                         });
42820
42821 //                      Wire the DropZone's handlers up to methods in *this*
42822                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
42823                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
42824                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
42825                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
42826                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
42827                 }
42828     },
42829
42830 /**     Decide whether to drop above or below a View node. */
42831     getDropPoint : function(e, n, dd){
42832         if (n == this.el.dom) { return "above"; }
42833                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
42834                 var c = t + (b - t) / 2;
42835                 var y = Roo.lib.Event.getPageY(e);
42836                 if(y <= c) {
42837                         return "above";
42838                 }else{
42839                         return "below";
42840                 }
42841     },
42842
42843     onNodeEnter : function(n, dd, e, data){
42844                 return false;
42845     },
42846     
42847     onNodeOver : function(n, dd, e, data){
42848                 var pt = this.getDropPoint(e, n, dd);
42849                 // set the insert point style on the target node
42850                 var dragElClass = this.dropNotAllowed;
42851                 if (pt) {
42852                         var targetElClass;
42853                         if (pt == "above"){
42854                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
42855                                 targetElClass = "x-view-drag-insert-above";
42856                         } else {
42857                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
42858                                 targetElClass = "x-view-drag-insert-below";
42859                         }
42860                         if (this.lastInsertClass != targetElClass){
42861                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
42862                                 this.lastInsertClass = targetElClass;
42863                         }
42864                 }
42865                 return dragElClass;
42866         },
42867
42868     onNodeOut : function(n, dd, e, data){
42869                 this.removeDropIndicators(n);
42870     },
42871
42872     onNodeDrop : function(n, dd, e, data){
42873         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
42874                 return false;
42875         }
42876         var pt = this.getDropPoint(e, n, dd);
42877                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
42878                 if (pt == "below") { insertAt++; }
42879                 for (var i = 0; i < data.records.length; i++) {
42880                         var r = data.records[i];
42881                         var dup = this.store.getById(r.id);
42882                         if (dup && (dd != this.dragZone)) {
42883                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
42884                         } else {
42885                                 if (data.copy) {
42886                                         this.store.insert(insertAt++, r.copy());
42887                                 } else {
42888                                         data.source.isDirtyFlag = true;
42889                                         r.store.remove(r);
42890                                         this.store.insert(insertAt++, r);
42891                                 }
42892                                 this.isDirtyFlag = true;
42893                         }
42894                 }
42895                 this.dragZone.cachedTarget = null;
42896                 return true;
42897     },
42898
42899     removeDropIndicators : function(n){
42900                 if(n){
42901                         Roo.fly(n).removeClass([
42902                                 "x-view-drag-insert-above",
42903                                 "x-view-drag-insert-below"]);
42904                         this.lastInsertClass = "_noclass";
42905                 }
42906     },
42907
42908 /**
42909  *      Utility method. Add a delete option to the DDView's context menu.
42910  *      @param {String} imageUrl The URL of the "delete" icon image.
42911  */
42912         setDeletable: function(imageUrl) {
42913                 if (!this.singleSelect && !this.multiSelect) {
42914                         this.singleSelect = true;
42915                 }
42916                 var c = this.getContextMenu();
42917                 this.contextMenu.on("itemclick", function(item) {
42918                         switch (item.id) {
42919                                 case "delete":
42920                                         this.remove(this.getSelectedIndexes());
42921                                         break;
42922                         }
42923                 }, this);
42924                 this.contextMenu.add({
42925                         icon: imageUrl,
42926                         id: "delete",
42927                         text: 'Delete'
42928                 });
42929         },
42930         
42931 /**     Return the context menu for this DDView. */
42932         getContextMenu: function() {
42933                 if (!this.contextMenu) {
42934 //                      Create the View's context menu
42935                         this.contextMenu = new Roo.menu.Menu({
42936                                 id: this.id + "-contextmenu"
42937                         });
42938                         this.el.on("contextmenu", this.showContextMenu, this);
42939                 }
42940                 return this.contextMenu;
42941         },
42942         
42943         disableContextMenu: function() {
42944                 if (this.contextMenu) {
42945                         this.el.un("contextmenu", this.showContextMenu, this);
42946                 }
42947         },
42948
42949         showContextMenu: function(e, item) {
42950         item = this.findItemFromChild(e.getTarget());
42951                 if (item) {
42952                         e.stopEvent();
42953                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
42954                         this.contextMenu.showAt(e.getXY());
42955             }
42956     },
42957
42958 /**
42959  *      Remove {@link Roo.data.Record}s at the specified indices.
42960  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
42961  */
42962     remove: function(selectedIndices) {
42963                 selectedIndices = [].concat(selectedIndices);
42964                 for (var i = 0; i < selectedIndices.length; i++) {
42965                         var rec = this.store.getAt(selectedIndices[i]);
42966                         this.store.remove(rec);
42967                 }
42968     },
42969
42970 /**
42971  *      Double click fires the event, but also, if this is draggable, and there is only one other
42972  *      related DropZone, it transfers the selected node.
42973  */
42974     onDblClick : function(e){
42975         var item = this.findItemFromChild(e.getTarget());
42976         if(item){
42977             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
42978                 return false;
42979             }
42980             if (this.dragGroup) {
42981                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
42982                     while (targets.indexOf(this.dropZone) > -1) {
42983                             targets.remove(this.dropZone);
42984                                 }
42985                     if (targets.length == 1) {
42986                                         this.dragZone.cachedTarget = null;
42987                         var el = Roo.get(targets[0].getEl());
42988                         var box = el.getBox(true);
42989                         targets[0].onNodeDrop(el.dom, {
42990                                 target: el.dom,
42991                                 xy: [box.x, box.y + box.height - 1]
42992                         }, null, this.getDragData(e));
42993                     }
42994                 }
42995         }
42996     },
42997     
42998     handleSelection: function(e) {
42999                 this.dragZone.cachedTarget = null;
43000         var item = this.findItemFromChild(e.getTarget());
43001         if (!item) {
43002                 this.clearSelections(true);
43003                 return;
43004         }
43005                 if (item && (this.multiSelect || this.singleSelect)){
43006                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
43007                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
43008                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
43009                                 this.unselect(item);
43010                         } else {
43011                                 this.select(item, this.multiSelect && e.ctrlKey);
43012                                 this.lastSelection = item;
43013                         }
43014                 }
43015     },
43016
43017     onItemClick : function(item, index, e){
43018                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
43019                         return false;
43020                 }
43021                 return true;
43022     },
43023
43024     unselect : function(nodeInfo, suppressEvent){
43025                 var node = this.getNode(nodeInfo);
43026                 if(node && this.isSelected(node)){
43027                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
43028                                 Roo.fly(node).removeClass(this.selectedClass);
43029                                 this.selections.remove(node);
43030                                 if(!suppressEvent){
43031                                         this.fireEvent("selectionchange", this, this.selections);
43032                                 }
43033                         }
43034                 }
43035     }
43036 });
43037 /*
43038  * Based on:
43039  * Ext JS Library 1.1.1
43040  * Copyright(c) 2006-2007, Ext JS, LLC.
43041  *
43042  * Originally Released Under LGPL - original licence link has changed is not relivant.
43043  *
43044  * Fork - LGPL
43045  * <script type="text/javascript">
43046  */
43047  
43048 /**
43049  * @class Roo.LayoutManager
43050  * @extends Roo.util.Observable
43051  * Base class for layout managers.
43052  */
43053 Roo.LayoutManager = function(container, config){
43054     Roo.LayoutManager.superclass.constructor.call(this);
43055     this.el = Roo.get(container);
43056     // ie scrollbar fix
43057     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
43058         document.body.scroll = "no";
43059     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
43060         this.el.position('relative');
43061     }
43062     this.id = this.el.id;
43063     this.el.addClass("x-layout-container");
43064     /** false to disable window resize monitoring @type Boolean */
43065     this.monitorWindowResize = true;
43066     this.regions = {};
43067     this.addEvents({
43068         /**
43069          * @event layout
43070          * Fires when a layout is performed. 
43071          * @param {Roo.LayoutManager} this
43072          */
43073         "layout" : true,
43074         /**
43075          * @event regionresized
43076          * Fires when the user resizes a region. 
43077          * @param {Roo.LayoutRegion} region The resized region
43078          * @param {Number} newSize The new size (width for east/west, height for north/south)
43079          */
43080         "regionresized" : true,
43081         /**
43082          * @event regioncollapsed
43083          * Fires when a region is collapsed. 
43084          * @param {Roo.LayoutRegion} region The collapsed region
43085          */
43086         "regioncollapsed" : true,
43087         /**
43088          * @event regionexpanded
43089          * Fires when a region is expanded.  
43090          * @param {Roo.LayoutRegion} region The expanded region
43091          */
43092         "regionexpanded" : true
43093     });
43094     this.updating = false;
43095     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
43096 };
43097
43098 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
43099     /**
43100      * Returns true if this layout is currently being updated
43101      * @return {Boolean}
43102      */
43103     isUpdating : function(){
43104         return this.updating; 
43105     },
43106     
43107     /**
43108      * Suspend the LayoutManager from doing auto-layouts while
43109      * making multiple add or remove calls
43110      */
43111     beginUpdate : function(){
43112         this.updating = true;    
43113     },
43114     
43115     /**
43116      * Restore auto-layouts and optionally disable the manager from performing a layout
43117      * @param {Boolean} noLayout true to disable a layout update 
43118      */
43119     endUpdate : function(noLayout){
43120         this.updating = false;
43121         if(!noLayout){
43122             this.layout();
43123         }    
43124     },
43125     
43126     layout: function(){
43127         
43128     },
43129     
43130     onRegionResized : function(region, newSize){
43131         this.fireEvent("regionresized", region, newSize);
43132         this.layout();
43133     },
43134     
43135     onRegionCollapsed : function(region){
43136         this.fireEvent("regioncollapsed", region);
43137     },
43138     
43139     onRegionExpanded : function(region){
43140         this.fireEvent("regionexpanded", region);
43141     },
43142         
43143     /**
43144      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
43145      * performs box-model adjustments.
43146      * @return {Object} The size as an object {width: (the width), height: (the height)}
43147      */
43148     getViewSize : function(){
43149         var size;
43150         if(this.el.dom != document.body){
43151             size = this.el.getSize();
43152         }else{
43153             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
43154         }
43155         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
43156         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
43157         return size;
43158     },
43159     
43160     /**
43161      * Returns the Element this layout is bound to.
43162      * @return {Roo.Element}
43163      */
43164     getEl : function(){
43165         return this.el;
43166     },
43167     
43168     /**
43169      * Returns the specified region.
43170      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
43171      * @return {Roo.LayoutRegion}
43172      */
43173     getRegion : function(target){
43174         return this.regions[target.toLowerCase()];
43175     },
43176     
43177     onWindowResize : function(){
43178         if(this.monitorWindowResize){
43179             this.layout();
43180         }
43181     }
43182 });/*
43183  * Based on:
43184  * Ext JS Library 1.1.1
43185  * Copyright(c) 2006-2007, Ext JS, LLC.
43186  *
43187  * Originally Released Under LGPL - original licence link has changed is not relivant.
43188  *
43189  * Fork - LGPL
43190  * <script type="text/javascript">
43191  */
43192 /**
43193  * @class Roo.BorderLayout
43194  * @extends Roo.LayoutManager
43195  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
43196  * please see: <br><br>
43197  * <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>
43198  * <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>
43199  * Example:
43200  <pre><code>
43201  var layout = new Roo.BorderLayout(document.body, {
43202     north: {
43203         initialSize: 25,
43204         titlebar: false
43205     },
43206     west: {
43207         split:true,
43208         initialSize: 200,
43209         minSize: 175,
43210         maxSize: 400,
43211         titlebar: true,
43212         collapsible: true
43213     },
43214     east: {
43215         split:true,
43216         initialSize: 202,
43217         minSize: 175,
43218         maxSize: 400,
43219         titlebar: true,
43220         collapsible: true
43221     },
43222     south: {
43223         split:true,
43224         initialSize: 100,
43225         minSize: 100,
43226         maxSize: 200,
43227         titlebar: true,
43228         collapsible: true
43229     },
43230     center: {
43231         titlebar: true,
43232         autoScroll:true,
43233         resizeTabs: true,
43234         minTabWidth: 50,
43235         preferredTabWidth: 150
43236     }
43237 });
43238
43239 // shorthand
43240 var CP = Roo.ContentPanel;
43241
43242 layout.beginUpdate();
43243 layout.add("north", new CP("north", "North"));
43244 layout.add("south", new CP("south", {title: "South", closable: true}));
43245 layout.add("west", new CP("west", {title: "West"}));
43246 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
43247 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
43248 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
43249 layout.getRegion("center").showPanel("center1");
43250 layout.endUpdate();
43251 </code></pre>
43252
43253 <b>The container the layout is rendered into can be either the body element or any other element.
43254 If it is not the body element, the container needs to either be an absolute positioned element,
43255 or you will need to add "position:relative" to the css of the container.  You will also need to specify
43256 the container size if it is not the body element.</b>
43257
43258 * @constructor
43259 * Create a new BorderLayout
43260 * @param {String/HTMLElement/Element} container The container this layout is bound to
43261 * @param {Object} config Configuration options
43262  */
43263 Roo.BorderLayout = function(container, config){
43264     config = config || {};
43265     Roo.BorderLayout.superclass.constructor.call(this, container, config);
43266     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
43267     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
43268         var target = this.factory.validRegions[i];
43269         if(config[target]){
43270             this.addRegion(target, config[target]);
43271         }
43272     }
43273 };
43274
43275 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
43276     /**
43277      * Creates and adds a new region if it doesn't already exist.
43278      * @param {String} target The target region key (north, south, east, west or center).
43279      * @param {Object} config The regions config object
43280      * @return {BorderLayoutRegion} The new region
43281      */
43282     addRegion : function(target, config){
43283         if(!this.regions[target]){
43284             var r = this.factory.create(target, this, config);
43285             this.bindRegion(target, r);
43286         }
43287         return this.regions[target];
43288     },
43289
43290     // private (kinda)
43291     bindRegion : function(name, r){
43292         this.regions[name] = r;
43293         r.on("visibilitychange", this.layout, this);
43294         r.on("paneladded", this.layout, this);
43295         r.on("panelremoved", this.layout, this);
43296         r.on("invalidated", this.layout, this);
43297         r.on("resized", this.onRegionResized, this);
43298         r.on("collapsed", this.onRegionCollapsed, this);
43299         r.on("expanded", this.onRegionExpanded, this);
43300     },
43301
43302     /**
43303      * Performs a layout update.
43304      */
43305     layout : function(){
43306         if(this.updating) return;
43307         var size = this.getViewSize();
43308         var w = size.width;
43309         var h = size.height;
43310         var centerW = w;
43311         var centerH = h;
43312         var centerY = 0;
43313         var centerX = 0;
43314         //var x = 0, y = 0;
43315
43316         var rs = this.regions;
43317         var north = rs["north"];
43318         var south = rs["south"]; 
43319         var west = rs["west"];
43320         var east = rs["east"];
43321         var center = rs["center"];
43322         //if(this.hideOnLayout){ // not supported anymore
43323             //c.el.setStyle("display", "none");
43324         //}
43325         if(north && north.isVisible()){
43326             var b = north.getBox();
43327             var m = north.getMargins();
43328             b.width = w - (m.left+m.right);
43329             b.x = m.left;
43330             b.y = m.top;
43331             centerY = b.height + b.y + m.bottom;
43332             centerH -= centerY;
43333             north.updateBox(this.safeBox(b));
43334         }
43335         if(south && south.isVisible()){
43336             var b = south.getBox();
43337             var m = south.getMargins();
43338             b.width = w - (m.left+m.right);
43339             b.x = m.left;
43340             var totalHeight = (b.height + m.top + m.bottom);
43341             b.y = h - totalHeight + m.top;
43342             centerH -= totalHeight;
43343             south.updateBox(this.safeBox(b));
43344         }
43345         if(west && west.isVisible()){
43346             var b = west.getBox();
43347             var m = west.getMargins();
43348             b.height = centerH - (m.top+m.bottom);
43349             b.x = m.left;
43350             b.y = centerY + m.top;
43351             var totalWidth = (b.width + m.left + m.right);
43352             centerX += totalWidth;
43353             centerW -= totalWidth;
43354             west.updateBox(this.safeBox(b));
43355         }
43356         if(east && east.isVisible()){
43357             var b = east.getBox();
43358             var m = east.getMargins();
43359             b.height = centerH - (m.top+m.bottom);
43360             var totalWidth = (b.width + m.left + m.right);
43361             b.x = w - totalWidth + m.left;
43362             b.y = centerY + m.top;
43363             centerW -= totalWidth;
43364             east.updateBox(this.safeBox(b));
43365         }
43366         if(center){
43367             var m = center.getMargins();
43368             var centerBox = {
43369                 x: centerX + m.left,
43370                 y: centerY + m.top,
43371                 width: centerW - (m.left+m.right),
43372                 height: centerH - (m.top+m.bottom)
43373             };
43374             //if(this.hideOnLayout){
43375                 //center.el.setStyle("display", "block");
43376             //}
43377             center.updateBox(this.safeBox(centerBox));
43378         }
43379         this.el.repaint();
43380         this.fireEvent("layout", this);
43381     },
43382
43383     // private
43384     safeBox : function(box){
43385         box.width = Math.max(0, box.width);
43386         box.height = Math.max(0, box.height);
43387         return box;
43388     },
43389
43390     /**
43391      * Adds a ContentPanel (or subclass) to this layout.
43392      * @param {String} target The target region key (north, south, east, west or center).
43393      * @param {Roo.ContentPanel} panel The panel to add
43394      * @return {Roo.ContentPanel} The added panel
43395      */
43396     add : function(target, panel){
43397          
43398         target = target.toLowerCase();
43399         return this.regions[target].add(panel);
43400     },
43401
43402     /**
43403      * Remove a ContentPanel (or subclass) to this layout.
43404      * @param {String} target The target region key (north, south, east, west or center).
43405      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
43406      * @return {Roo.ContentPanel} The removed panel
43407      */
43408     remove : function(target, panel){
43409         target = target.toLowerCase();
43410         return this.regions[target].remove(panel);
43411     },
43412
43413     /**
43414      * Searches all regions for a panel with the specified id
43415      * @param {String} panelId
43416      * @return {Roo.ContentPanel} The panel or null if it wasn't found
43417      */
43418     findPanel : function(panelId){
43419         var rs = this.regions;
43420         for(var target in rs){
43421             if(typeof rs[target] != "function"){
43422                 var p = rs[target].getPanel(panelId);
43423                 if(p){
43424                     return p;
43425                 }
43426             }
43427         }
43428         return null;
43429     },
43430
43431     /**
43432      * Searches all regions for a panel with the specified id and activates (shows) it.
43433      * @param {String/ContentPanel} panelId The panels id or the panel itself
43434      * @return {Roo.ContentPanel} The shown panel or null
43435      */
43436     showPanel : function(panelId) {
43437       var rs = this.regions;
43438       for(var target in rs){
43439          var r = rs[target];
43440          if(typeof r != "function"){
43441             if(r.hasPanel(panelId)){
43442                return r.showPanel(panelId);
43443             }
43444          }
43445       }
43446       return null;
43447    },
43448
43449    /**
43450      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
43451      * @param {Roo.state.Provider} provider (optional) An alternate state provider
43452      */
43453     restoreState : function(provider){
43454         if(!provider){
43455             provider = Roo.state.Manager;
43456         }
43457         var sm = new Roo.LayoutStateManager();
43458         sm.init(this, provider);
43459     },
43460
43461     /**
43462      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
43463      * object should contain properties for each region to add ContentPanels to, and each property's value should be
43464      * a valid ContentPanel config object.  Example:
43465      * <pre><code>
43466 // Create the main layout
43467 var layout = new Roo.BorderLayout('main-ct', {
43468     west: {
43469         split:true,
43470         minSize: 175,
43471         titlebar: true
43472     },
43473     center: {
43474         title:'Components'
43475     }
43476 }, 'main-ct');
43477
43478 // Create and add multiple ContentPanels at once via configs
43479 layout.batchAdd({
43480    west: {
43481        id: 'source-files',
43482        autoCreate:true,
43483        title:'Ext Source Files',
43484        autoScroll:true,
43485        fitToFrame:true
43486    },
43487    center : {
43488        el: cview,
43489        autoScroll:true,
43490        fitToFrame:true,
43491        toolbar: tb,
43492        resizeEl:'cbody'
43493    }
43494 });
43495 </code></pre>
43496      * @param {Object} regions An object containing ContentPanel configs by region name
43497      */
43498     batchAdd : function(regions){
43499         this.beginUpdate();
43500         for(var rname in regions){
43501             var lr = this.regions[rname];
43502             if(lr){
43503                 this.addTypedPanels(lr, regions[rname]);
43504             }
43505         }
43506         this.endUpdate();
43507     },
43508
43509     // private
43510     addTypedPanels : function(lr, ps){
43511         if(typeof ps == 'string'){
43512             lr.add(new Roo.ContentPanel(ps));
43513         }
43514         else if(ps instanceof Array){
43515             for(var i =0, len = ps.length; i < len; i++){
43516                 this.addTypedPanels(lr, ps[i]);
43517             }
43518         }
43519         else if(!ps.events){ // raw config?
43520             var el = ps.el;
43521             delete ps.el; // prevent conflict
43522             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
43523         }
43524         else {  // panel object assumed!
43525             lr.add(ps);
43526         }
43527     },
43528     /**
43529      * Adds a xtype elements to the layout.
43530      * <pre><code>
43531
43532 layout.addxtype({
43533        xtype : 'ContentPanel',
43534        region: 'west',
43535        items: [ .... ]
43536    }
43537 );
43538
43539 layout.addxtype({
43540         xtype : 'NestedLayoutPanel',
43541         region: 'west',
43542         layout: {
43543            center: { },
43544            west: { }   
43545         },
43546         items : [ ... list of content panels or nested layout panels.. ]
43547    }
43548 );
43549 </code></pre>
43550      * @param {Object} cfg Xtype definition of item to add.
43551      */
43552     addxtype : function(cfg)
43553     {
43554         // basically accepts a pannel...
43555         // can accept a layout region..!?!?
43556        // console.log('BorderLayout add ' + cfg.xtype)
43557         
43558         if (!cfg.xtype.match(/Panel$/)) {
43559             return false;
43560         }
43561         var ret = false;
43562         var region = cfg.region;
43563         delete cfg.region;
43564         
43565           
43566         var xitems = [];
43567         if (cfg.items) {
43568             xitems = cfg.items;
43569             delete cfg.items;
43570         }
43571         
43572         
43573         switch(cfg.xtype) 
43574         {
43575             case 'ContentPanel':  // ContentPanel (el, cfg)
43576             case 'ScrollPanel':  // ContentPanel (el, cfg)
43577                 if(cfg.autoCreate) {
43578                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43579                 } else {
43580                     var el = this.el.createChild();
43581                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
43582                 }
43583                 
43584                 this.add(region, ret);
43585                 break;
43586             
43587             
43588             case 'TreePanel': // our new panel!
43589                 cfg.el = this.el.createChild();
43590                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
43591                 this.add(region, ret);
43592                 break;
43593             
43594             case 'NestedLayoutPanel': 
43595                 // create a new Layout (which is  a Border Layout...
43596                 var el = this.el.createChild();
43597                 var clayout = cfg.layout;
43598                 delete cfg.layout;
43599                 clayout.items   = clayout.items  || [];
43600                 // replace this exitems with the clayout ones..
43601                 xitems = clayout.items;
43602                  
43603                 
43604                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
43605                     cfg.background = false;
43606                 }
43607                 var layout = new Roo.BorderLayout(el, clayout);
43608                 
43609                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
43610                 //console.log('adding nested layout panel '  + cfg.toSource());
43611                 this.add(region, ret);
43612                 
43613                 break;
43614                 
43615             case 'GridPanel': 
43616             
43617                 // needs grid and region
43618                 
43619                 //var el = this.getRegion(region).el.createChild();
43620                 var el = this.el.createChild();
43621                 // create the grid first...
43622                 
43623                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
43624                 delete cfg.grid;
43625                 if (region == 'center' && this.active ) {
43626                     cfg.background = false;
43627                 }
43628                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
43629                 
43630                 this.add(region, ret);
43631                 if (cfg.background) {
43632                     ret.on('activate', function(gp) {
43633                         if (!gp.grid.rendered) {
43634                             gp.grid.render();
43635                         }
43636                     });
43637                 } else {
43638                     grid.render();
43639                 }
43640                 break;
43641            
43642                
43643                 
43644                 
43645             default: 
43646                 alert("Can not add '" + cfg.xtype + "' to BorderLayout");
43647                 return;
43648              // GridPanel (grid, cfg)
43649             
43650         }
43651         this.beginUpdate();
43652         // add children..
43653         Roo.each(xitems, function(i)  {
43654             ret.addxtype(i);
43655         });
43656         this.endUpdate();
43657         return ret;
43658         
43659     }
43660 });
43661
43662 /**
43663  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
43664  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
43665  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
43666  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
43667  * <pre><code>
43668 // shorthand
43669 var CP = Roo.ContentPanel;
43670
43671 var layout = Roo.BorderLayout.create({
43672     north: {
43673         initialSize: 25,
43674         titlebar: false,
43675         panels: [new CP("north", "North")]
43676     },
43677     west: {
43678         split:true,
43679         initialSize: 200,
43680         minSize: 175,
43681         maxSize: 400,
43682         titlebar: true,
43683         collapsible: true,
43684         panels: [new CP("west", {title: "West"})]
43685     },
43686     east: {
43687         split:true,
43688         initialSize: 202,
43689         minSize: 175,
43690         maxSize: 400,
43691         titlebar: true,
43692         collapsible: true,
43693         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
43694     },
43695     south: {
43696         split:true,
43697         initialSize: 100,
43698         minSize: 100,
43699         maxSize: 200,
43700         titlebar: true,
43701         collapsible: true,
43702         panels: [new CP("south", {title: "South", closable: true})]
43703     },
43704     center: {
43705         titlebar: true,
43706         autoScroll:true,
43707         resizeTabs: true,
43708         minTabWidth: 50,
43709         preferredTabWidth: 150,
43710         panels: [
43711             new CP("center1", {title: "Close Me", closable: true}),
43712             new CP("center2", {title: "Center Panel", closable: false})
43713         ]
43714     }
43715 }, document.body);
43716
43717 layout.getRegion("center").showPanel("center1");
43718 </code></pre>
43719  * @param config
43720  * @param targetEl
43721  */
43722 Roo.BorderLayout.create = function(config, targetEl){
43723     var layout = new Roo.BorderLayout(targetEl || document.body, config);
43724     layout.beginUpdate();
43725     var regions = Roo.BorderLayout.RegionFactory.validRegions;
43726     for(var j = 0, jlen = regions.length; j < jlen; j++){
43727         var lr = regions[j];
43728         if(layout.regions[lr] && config[lr].panels){
43729             var r = layout.regions[lr];
43730             var ps = config[lr].panels;
43731             layout.addTypedPanels(r, ps);
43732         }
43733     }
43734     layout.endUpdate();
43735     return layout;
43736 };
43737
43738 // private
43739 Roo.BorderLayout.RegionFactory = {
43740     // private
43741     validRegions : ["north","south","east","west","center"],
43742
43743     // private
43744     create : function(target, mgr, config){
43745         target = target.toLowerCase();
43746         if(config.lightweight || config.basic){
43747             return new Roo.BasicLayoutRegion(mgr, config, target);
43748         }
43749         switch(target){
43750             case "north":
43751                 return new Roo.NorthLayoutRegion(mgr, config);
43752             case "south":
43753                 return new Roo.SouthLayoutRegion(mgr, config);
43754             case "east":
43755                 return new Roo.EastLayoutRegion(mgr, config);
43756             case "west":
43757                 return new Roo.WestLayoutRegion(mgr, config);
43758             case "center":
43759                 return new Roo.CenterLayoutRegion(mgr, config);
43760         }
43761         throw 'Layout region "'+target+'" not supported.';
43762     }
43763 };/*
43764  * Based on:
43765  * Ext JS Library 1.1.1
43766  * Copyright(c) 2006-2007, Ext JS, LLC.
43767  *
43768  * Originally Released Under LGPL - original licence link has changed is not relivant.
43769  *
43770  * Fork - LGPL
43771  * <script type="text/javascript">
43772  */
43773  
43774 /**
43775  * @class Roo.BasicLayoutRegion
43776  * @extends Roo.util.Observable
43777  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
43778  * and does not have a titlebar, tabs or any other features. All it does is size and position 
43779  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
43780  */
43781 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
43782     this.mgr = mgr;
43783     this.position  = pos;
43784     this.events = {
43785         /**
43786          * @scope Roo.BasicLayoutRegion
43787          */
43788         
43789         /**
43790          * @event beforeremove
43791          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
43792          * @param {Roo.LayoutRegion} this
43793          * @param {Roo.ContentPanel} panel The panel
43794          * @param {Object} e The cancel event object
43795          */
43796         "beforeremove" : true,
43797         /**
43798          * @event invalidated
43799          * Fires when the layout for this region is changed.
43800          * @param {Roo.LayoutRegion} this
43801          */
43802         "invalidated" : true,
43803         /**
43804          * @event visibilitychange
43805          * Fires when this region is shown or hidden 
43806          * @param {Roo.LayoutRegion} this
43807          * @param {Boolean} visibility true or false
43808          */
43809         "visibilitychange" : true,
43810         /**
43811          * @event paneladded
43812          * Fires when a panel is added. 
43813          * @param {Roo.LayoutRegion} this
43814          * @param {Roo.ContentPanel} panel The panel
43815          */
43816         "paneladded" : true,
43817         /**
43818          * @event panelremoved
43819          * Fires when a panel is removed. 
43820          * @param {Roo.LayoutRegion} this
43821          * @param {Roo.ContentPanel} panel The panel
43822          */
43823         "panelremoved" : true,
43824         /**
43825          * @event collapsed
43826          * Fires when this region is collapsed.
43827          * @param {Roo.LayoutRegion} this
43828          */
43829         "collapsed" : true,
43830         /**
43831          * @event expanded
43832          * Fires when this region is expanded.
43833          * @param {Roo.LayoutRegion} this
43834          */
43835         "expanded" : true,
43836         /**
43837          * @event slideshow
43838          * Fires when this region is slid into view.
43839          * @param {Roo.LayoutRegion} this
43840          */
43841         "slideshow" : true,
43842         /**
43843          * @event slidehide
43844          * Fires when this region slides out of view. 
43845          * @param {Roo.LayoutRegion} this
43846          */
43847         "slidehide" : true,
43848         /**
43849          * @event panelactivated
43850          * Fires when a panel is activated. 
43851          * @param {Roo.LayoutRegion} this
43852          * @param {Roo.ContentPanel} panel The activated panel
43853          */
43854         "panelactivated" : true,
43855         /**
43856          * @event resized
43857          * Fires when the user resizes this region. 
43858          * @param {Roo.LayoutRegion} this
43859          * @param {Number} newSize The new size (width for east/west, height for north/south)
43860          */
43861         "resized" : true
43862     };
43863     /** A collection of panels in this region. @type Roo.util.MixedCollection */
43864     this.panels = new Roo.util.MixedCollection();
43865     this.panels.getKey = this.getPanelId.createDelegate(this);
43866     this.box = null;
43867     this.activePanel = null;
43868     // ensure listeners are added...
43869     
43870     if (config.listeners || config.events) {
43871         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
43872             listeners : config.listeners || {},
43873             events : config.events || {}
43874         });
43875     }
43876     
43877     if(skipConfig !== true){
43878         this.applyConfig(config);
43879     }
43880 };
43881
43882 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
43883     getPanelId : function(p){
43884         return p.getId();
43885     },
43886     
43887     applyConfig : function(config){
43888         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
43889         this.config = config;
43890         
43891     },
43892     
43893     /**
43894      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
43895      * the width, for horizontal (north, south) the height.
43896      * @param {Number} newSize The new width or height
43897      */
43898     resizeTo : function(newSize){
43899         var el = this.el ? this.el :
43900                  (this.activePanel ? this.activePanel.getEl() : null);
43901         if(el){
43902             switch(this.position){
43903                 case "east":
43904                 case "west":
43905                     el.setWidth(newSize);
43906                     this.fireEvent("resized", this, newSize);
43907                 break;
43908                 case "north":
43909                 case "south":
43910                     el.setHeight(newSize);
43911                     this.fireEvent("resized", this, newSize);
43912                 break;                
43913             }
43914         }
43915     },
43916     
43917     getBox : function(){
43918         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
43919     },
43920     
43921     getMargins : function(){
43922         return this.margins;
43923     },
43924     
43925     updateBox : function(box){
43926         this.box = box;
43927         var el = this.activePanel.getEl();
43928         el.dom.style.left = box.x + "px";
43929         el.dom.style.top = box.y + "px";
43930         this.activePanel.setSize(box.width, box.height);
43931     },
43932     
43933     /**
43934      * Returns the container element for this region.
43935      * @return {Roo.Element}
43936      */
43937     getEl : function(){
43938         return this.activePanel;
43939     },
43940     
43941     /**
43942      * Returns true if this region is currently visible.
43943      * @return {Boolean}
43944      */
43945     isVisible : function(){
43946         return this.activePanel ? true : false;
43947     },
43948     
43949     setActivePanel : function(panel){
43950         panel = this.getPanel(panel);
43951         if(this.activePanel && this.activePanel != panel){
43952             this.activePanel.setActiveState(false);
43953             this.activePanel.getEl().setLeftTop(-10000,-10000);
43954         }
43955         this.activePanel = panel;
43956         panel.setActiveState(true);
43957         if(this.box){
43958             panel.setSize(this.box.width, this.box.height);
43959         }
43960         this.fireEvent("panelactivated", this, panel);
43961         this.fireEvent("invalidated");
43962     },
43963     
43964     /**
43965      * Show the specified panel.
43966      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
43967      * @return {Roo.ContentPanel} The shown panel or null
43968      */
43969     showPanel : function(panel){
43970         if(panel = this.getPanel(panel)){
43971             this.setActivePanel(panel);
43972         }
43973         return panel;
43974     },
43975     
43976     /**
43977      * Get the active panel for this region.
43978      * @return {Roo.ContentPanel} The active panel or null
43979      */
43980     getActivePanel : function(){
43981         return this.activePanel;
43982     },
43983     
43984     /**
43985      * Add the passed ContentPanel(s)
43986      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
43987      * @return {Roo.ContentPanel} The panel added (if only one was added)
43988      */
43989     add : function(panel){
43990         if(arguments.length > 1){
43991             for(var i = 0, len = arguments.length; i < len; i++) {
43992                 this.add(arguments[i]);
43993             }
43994             return null;
43995         }
43996         if(this.hasPanel(panel)){
43997             this.showPanel(panel);
43998             return panel;
43999         }
44000         var el = panel.getEl();
44001         if(el.dom.parentNode != this.mgr.el.dom){
44002             this.mgr.el.dom.appendChild(el.dom);
44003         }
44004         if(panel.setRegion){
44005             panel.setRegion(this);
44006         }
44007         this.panels.add(panel);
44008         el.setStyle("position", "absolute");
44009         if(!panel.background){
44010             this.setActivePanel(panel);
44011             if(this.config.initialSize && this.panels.getCount()==1){
44012                 this.resizeTo(this.config.initialSize);
44013             }
44014         }
44015         this.fireEvent("paneladded", this, panel);
44016         return panel;
44017     },
44018     
44019     /**
44020      * Returns true if the panel is in this region.
44021      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44022      * @return {Boolean}
44023      */
44024     hasPanel : function(panel){
44025         if(typeof panel == "object"){ // must be panel obj
44026             panel = panel.getId();
44027         }
44028         return this.getPanel(panel) ? true : false;
44029     },
44030     
44031     /**
44032      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44033      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44034      * @param {Boolean} preservePanel Overrides the config preservePanel option
44035      * @return {Roo.ContentPanel} The panel that was removed
44036      */
44037     remove : function(panel, preservePanel){
44038         panel = this.getPanel(panel);
44039         if(!panel){
44040             return null;
44041         }
44042         var e = {};
44043         this.fireEvent("beforeremove", this, panel, e);
44044         if(e.cancel === true){
44045             return null;
44046         }
44047         var panelId = panel.getId();
44048         this.panels.removeKey(panelId);
44049         return panel;
44050     },
44051     
44052     /**
44053      * Returns the panel specified or null if it's not in this region.
44054      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
44055      * @return {Roo.ContentPanel}
44056      */
44057     getPanel : function(id){
44058         if(typeof id == "object"){ // must be panel obj
44059             return id;
44060         }
44061         return this.panels.get(id);
44062     },
44063     
44064     /**
44065      * Returns this regions position (north/south/east/west/center).
44066      * @return {String} 
44067      */
44068     getPosition: function(){
44069         return this.position;    
44070     }
44071 });/*
44072  * Based on:
44073  * Ext JS Library 1.1.1
44074  * Copyright(c) 2006-2007, Ext JS, LLC.
44075  *
44076  * Originally Released Under LGPL - original licence link has changed is not relivant.
44077  *
44078  * Fork - LGPL
44079  * <script type="text/javascript">
44080  */
44081  
44082 /**
44083  * @class Roo.LayoutRegion
44084  * @extends Roo.BasicLayoutRegion
44085  * This class represents a region in a layout manager.
44086  * @cfg {Boolean} collapsible False to disable collapsing (defaults to true)
44087  * @cfg {Boolean} collapsed True to set the initial display to collapsed (defaults to false)
44088  * @cfg {Boolean} floatable False to disable floating (defaults to true)
44089  * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
44090  * @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})
44091  * @cfg {String} tabPosition "top" or "bottom" (defaults to "bottom")
44092  * @cfg {String} collapsedTitle Optional string message to display in the collapsed block of a north or south region
44093  * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
44094  * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
44095  * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
44096  * @cfg {String} title The title for the region (overrides panel titles)
44097  * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
44098  * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
44099  * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
44100  * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
44101  * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
44102  * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
44103  * the space available, similar to FireFox 1.5 tabs (defaults to false)
44104  * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
44105  * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
44106  * @cfg {Boolean} showPin True to show a pin button
44107 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
44108 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
44109 * @cfg {Boolean} disableTabTips True to disable tab tooltips
44110 * @cfg {Number} width  For East/West panels
44111 * @cfg {Number} height For North/South panels
44112 * @cfg {Boolean} split To show the splitter
44113  */
44114 Roo.LayoutRegion = function(mgr, config, pos){
44115     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
44116     var dh = Roo.DomHelper;
44117     /** This region's container element 
44118     * @type Roo.Element */
44119     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
44120     /** This region's title element 
44121     * @type Roo.Element */
44122
44123     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
44124         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
44125         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
44126     ]}, true);
44127     this.titleEl.enableDisplayMode();
44128     /** This region's title text element 
44129     * @type HTMLElement */
44130     this.titleTextEl = this.titleEl.dom.firstChild;
44131     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
44132     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
44133     this.closeBtn.enableDisplayMode();
44134     this.closeBtn.on("click", this.closeClicked, this);
44135     this.closeBtn.hide();
44136
44137     this.createBody(config);
44138     this.visible = true;
44139     this.collapsed = false;
44140
44141     if(config.hideWhenEmpty){
44142         this.hide();
44143         this.on("paneladded", this.validateVisibility, this);
44144         this.on("panelremoved", this.validateVisibility, this);
44145     }
44146     this.applyConfig(config);
44147 };
44148
44149 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
44150
44151     createBody : function(){
44152         /** This region's body element 
44153         * @type Roo.Element */
44154         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
44155     },
44156
44157     applyConfig : function(c){
44158         if(c.collapsible && this.position != "center" && !this.collapsedEl){
44159             var dh = Roo.DomHelper;
44160             if(c.titlebar !== false){
44161                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
44162                 this.collapseBtn.on("click", this.collapse, this);
44163                 this.collapseBtn.enableDisplayMode();
44164
44165                 if(c.showPin === true || this.showPin){
44166                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
44167                     this.stickBtn.enableDisplayMode();
44168                     this.stickBtn.on("click", this.expand, this);
44169                     this.stickBtn.hide();
44170                 }
44171             }
44172             /** This region's collapsed element
44173             * @type Roo.Element */
44174             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
44175                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
44176             ]}, true);
44177             if(c.floatable !== false){
44178                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
44179                this.collapsedEl.on("click", this.collapseClick, this);
44180             }
44181
44182             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
44183                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
44184                    id: "message", unselectable: "on", style:{"float":"left"}});
44185                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
44186              }
44187             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
44188             this.expandBtn.on("click", this.expand, this);
44189         }
44190         if(this.collapseBtn){
44191             this.collapseBtn.setVisible(c.collapsible == true);
44192         }
44193         this.cmargins = c.cmargins || this.cmargins ||
44194                          (this.position == "west" || this.position == "east" ?
44195                              {top: 0, left: 2, right:2, bottom: 0} :
44196                              {top: 2, left: 0, right:0, bottom: 2});
44197         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
44198         this.bottomTabs = c.tabPosition != "top";
44199         this.autoScroll = c.autoScroll || false;
44200         if(this.autoScroll){
44201             this.bodyEl.setStyle("overflow", "auto");
44202         }else{
44203             this.bodyEl.setStyle("overflow", "hidden");
44204         }
44205         //if(c.titlebar !== false){
44206             if((!c.titlebar && !c.title) || c.titlebar === false){
44207                 this.titleEl.hide();
44208             }else{
44209                 this.titleEl.show();
44210                 if(c.title){
44211                     this.titleTextEl.innerHTML = c.title;
44212                 }
44213             }
44214         //}
44215         this.duration = c.duration || .30;
44216         this.slideDuration = c.slideDuration || .45;
44217         this.config = c;
44218         if(c.collapsed){
44219             this.collapse(true);
44220         }
44221         if(c.hidden){
44222             this.hide();
44223         }
44224     },
44225     /**
44226      * Returns true if this region is currently visible.
44227      * @return {Boolean}
44228      */
44229     isVisible : function(){
44230         return this.visible;
44231     },
44232
44233     /**
44234      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
44235      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
44236      */
44237     setCollapsedTitle : function(title){
44238         title = title || "&#160;";
44239         if(this.collapsedTitleTextEl){
44240             this.collapsedTitleTextEl.innerHTML = title;
44241         }
44242     },
44243
44244     getBox : function(){
44245         var b;
44246         if(!this.collapsed){
44247             b = this.el.getBox(false, true);
44248         }else{
44249             b = this.collapsedEl.getBox(false, true);
44250         }
44251         return b;
44252     },
44253
44254     getMargins : function(){
44255         return this.collapsed ? this.cmargins : this.margins;
44256     },
44257
44258     highlight : function(){
44259         this.el.addClass("x-layout-panel-dragover");
44260     },
44261
44262     unhighlight : function(){
44263         this.el.removeClass("x-layout-panel-dragover");
44264     },
44265
44266     updateBox : function(box){
44267         this.box = box;
44268         if(!this.collapsed){
44269             this.el.dom.style.left = box.x + "px";
44270             this.el.dom.style.top = box.y + "px";
44271             this.updateBody(box.width, box.height);
44272         }else{
44273             this.collapsedEl.dom.style.left = box.x + "px";
44274             this.collapsedEl.dom.style.top = box.y + "px";
44275             this.collapsedEl.setSize(box.width, box.height);
44276         }
44277         if(this.tabs){
44278             this.tabs.autoSizeTabs();
44279         }
44280     },
44281
44282     updateBody : function(w, h){
44283         if(w !== null){
44284             this.el.setWidth(w);
44285             w -= this.el.getBorderWidth("rl");
44286             if(this.config.adjustments){
44287                 w += this.config.adjustments[0];
44288             }
44289         }
44290         if(h !== null){
44291             this.el.setHeight(h);
44292             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
44293             h -= this.el.getBorderWidth("tb");
44294             if(this.config.adjustments){
44295                 h += this.config.adjustments[1];
44296             }
44297             this.bodyEl.setHeight(h);
44298             if(this.tabs){
44299                 h = this.tabs.syncHeight(h);
44300             }
44301         }
44302         if(this.panelSize){
44303             w = w !== null ? w : this.panelSize.width;
44304             h = h !== null ? h : this.panelSize.height;
44305         }
44306         if(this.activePanel){
44307             var el = this.activePanel.getEl();
44308             w = w !== null ? w : el.getWidth();
44309             h = h !== null ? h : el.getHeight();
44310             this.panelSize = {width: w, height: h};
44311             this.activePanel.setSize(w, h);
44312         }
44313         if(Roo.isIE && this.tabs){
44314             this.tabs.el.repaint();
44315         }
44316     },
44317
44318     /**
44319      * Returns the container element for this region.
44320      * @return {Roo.Element}
44321      */
44322     getEl : function(){
44323         return this.el;
44324     },
44325
44326     /**
44327      * Hides this region.
44328      */
44329     hide : function(){
44330         if(!this.collapsed){
44331             this.el.dom.style.left = "-2000px";
44332             this.el.hide();
44333         }else{
44334             this.collapsedEl.dom.style.left = "-2000px";
44335             this.collapsedEl.hide();
44336         }
44337         this.visible = false;
44338         this.fireEvent("visibilitychange", this, false);
44339     },
44340
44341     /**
44342      * Shows this region if it was previously hidden.
44343      */
44344     show : function(){
44345         if(!this.collapsed){
44346             this.el.show();
44347         }else{
44348             this.collapsedEl.show();
44349         }
44350         this.visible = true;
44351         this.fireEvent("visibilitychange", this, true);
44352     },
44353
44354     closeClicked : function(){
44355         if(this.activePanel){
44356             this.remove(this.activePanel);
44357         }
44358     },
44359
44360     collapseClick : function(e){
44361         if(this.isSlid){
44362            e.stopPropagation();
44363            this.slideIn();
44364         }else{
44365            e.stopPropagation();
44366            this.slideOut();
44367         }
44368     },
44369
44370     /**
44371      * Collapses this region.
44372      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
44373      */
44374     collapse : function(skipAnim){
44375         if(this.collapsed) return;
44376         this.collapsed = true;
44377         if(this.split){
44378             this.split.el.hide();
44379         }
44380         if(this.config.animate && skipAnim !== true){
44381             this.fireEvent("invalidated", this);
44382             this.animateCollapse();
44383         }else{
44384             this.el.setLocation(-20000,-20000);
44385             this.el.hide();
44386             this.collapsedEl.show();
44387             this.fireEvent("collapsed", this);
44388             this.fireEvent("invalidated", this);
44389         }
44390     },
44391
44392     animateCollapse : function(){
44393         // overridden
44394     },
44395
44396     /**
44397      * Expands this region if it was previously collapsed.
44398      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
44399      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
44400      */
44401     expand : function(e, skipAnim){
44402         if(e) e.stopPropagation();
44403         if(!this.collapsed || this.el.hasActiveFx()) return;
44404         if(this.isSlid){
44405             this.afterSlideIn();
44406             skipAnim = true;
44407         }
44408         this.collapsed = false;
44409         if(this.config.animate && skipAnim !== true){
44410             this.animateExpand();
44411         }else{
44412             this.el.show();
44413             if(this.split){
44414                 this.split.el.show();
44415             }
44416             this.collapsedEl.setLocation(-2000,-2000);
44417             this.collapsedEl.hide();
44418             this.fireEvent("invalidated", this);
44419             this.fireEvent("expanded", this);
44420         }
44421     },
44422
44423     animateExpand : function(){
44424         // overridden
44425     },
44426
44427     initTabs : function(){
44428         this.bodyEl.setStyle("overflow", "hidden");
44429         var ts = new Roo.TabPanel(this.bodyEl.dom, {
44430             tabPosition: this.bottomTabs ? 'bottom' : 'top',
44431             disableTooltips: this.config.disableTabTips
44432         });
44433         if(this.config.hideTabs){
44434             ts.stripWrap.setDisplayed(false);
44435         }
44436         this.tabs = ts;
44437         ts.resizeTabs = this.config.resizeTabs === true;
44438         ts.minTabWidth = this.config.minTabWidth || 40;
44439         ts.maxTabWidth = this.config.maxTabWidth || 250;
44440         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
44441         ts.monitorResize = false;
44442         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44443         ts.bodyEl.addClass('x-layout-tabs-body');
44444         this.panels.each(this.initPanelAsTab, this);
44445     },
44446
44447     initPanelAsTab : function(panel){
44448         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
44449                     this.config.closeOnTab && panel.isClosable());
44450         if(panel.tabTip !== undefined){
44451             ti.setTooltip(panel.tabTip);
44452         }
44453         ti.on("activate", function(){
44454               this.setActivePanel(panel);
44455         }, this);
44456         if(this.config.closeOnTab){
44457             ti.on("beforeclose", function(t, e){
44458                 e.cancel = true;
44459                 this.remove(panel);
44460             }, this);
44461         }
44462         return ti;
44463     },
44464
44465     updatePanelTitle : function(panel, title){
44466         if(this.activePanel == panel){
44467             this.updateTitle(title);
44468         }
44469         if(this.tabs){
44470             var ti = this.tabs.getTab(panel.getEl().id);
44471             ti.setText(title);
44472             if(panel.tabTip !== undefined){
44473                 ti.setTooltip(panel.tabTip);
44474             }
44475         }
44476     },
44477
44478     updateTitle : function(title){
44479         if(this.titleTextEl && !this.config.title){
44480             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
44481         }
44482     },
44483
44484     setActivePanel : function(panel){
44485         panel = this.getPanel(panel);
44486         if(this.activePanel && this.activePanel != panel){
44487             this.activePanel.setActiveState(false);
44488         }
44489         this.activePanel = panel;
44490         panel.setActiveState(true);
44491         if(this.panelSize){
44492             panel.setSize(this.panelSize.width, this.panelSize.height);
44493         }
44494         if(this.closeBtn){
44495             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
44496         }
44497         this.updateTitle(panel.getTitle());
44498         if(this.tabs){
44499             this.fireEvent("invalidated", this);
44500         }
44501         this.fireEvent("panelactivated", this, panel);
44502     },
44503
44504     /**
44505      * Shows the specified panel.
44506      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
44507      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
44508      */
44509     showPanel : function(panel){
44510         if(panel = this.getPanel(panel)){
44511             if(this.tabs){
44512                 var tab = this.tabs.getTab(panel.getEl().id);
44513                 if(tab.isHidden()){
44514                     this.tabs.unhideTab(tab.id);
44515                 }
44516                 tab.activate();
44517             }else{
44518                 this.setActivePanel(panel);
44519             }
44520         }
44521         return panel;
44522     },
44523
44524     /**
44525      * Get the active panel for this region.
44526      * @return {Roo.ContentPanel} The active panel or null
44527      */
44528     getActivePanel : function(){
44529         return this.activePanel;
44530     },
44531
44532     validateVisibility : function(){
44533         if(this.panels.getCount() < 1){
44534             this.updateTitle("&#160;");
44535             this.closeBtn.hide();
44536             this.hide();
44537         }else{
44538             if(!this.isVisible()){
44539                 this.show();
44540             }
44541         }
44542     },
44543
44544     /**
44545      * Adds the passed ContentPanel(s) to this region.
44546      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
44547      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
44548      */
44549     add : function(panel){
44550         if(arguments.length > 1){
44551             for(var i = 0, len = arguments.length; i < len; i++) {
44552                 this.add(arguments[i]);
44553             }
44554             return null;
44555         }
44556         if(this.hasPanel(panel)){
44557             this.showPanel(panel);
44558             return panel;
44559         }
44560         panel.setRegion(this);
44561         this.panels.add(panel);
44562         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
44563             this.bodyEl.dom.appendChild(panel.getEl().dom);
44564             if(panel.background !== true){
44565                 this.setActivePanel(panel);
44566             }
44567             this.fireEvent("paneladded", this, panel);
44568             return panel;
44569         }
44570         if(!this.tabs){
44571             this.initTabs();
44572         }else{
44573             this.initPanelAsTab(panel);
44574         }
44575         if(panel.background !== true){
44576             this.tabs.activate(panel.getEl().id);
44577         }
44578         this.fireEvent("paneladded", this, panel);
44579         return panel;
44580     },
44581
44582     /**
44583      * Hides the tab for the specified panel.
44584      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44585      */
44586     hidePanel : function(panel){
44587         if(this.tabs && (panel = this.getPanel(panel))){
44588             this.tabs.hideTab(panel.getEl().id);
44589         }
44590     },
44591
44592     /**
44593      * Unhides the tab for a previously hidden panel.
44594      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44595      */
44596     unhidePanel : function(panel){
44597         if(this.tabs && (panel = this.getPanel(panel))){
44598             this.tabs.unhideTab(panel.getEl().id);
44599         }
44600     },
44601
44602     clearPanels : function(){
44603         while(this.panels.getCount() > 0){
44604              this.remove(this.panels.first());
44605         }
44606     },
44607
44608     /**
44609      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
44610      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
44611      * @param {Boolean} preservePanel Overrides the config preservePanel option
44612      * @return {Roo.ContentPanel} The panel that was removed
44613      */
44614     remove : function(panel, preservePanel){
44615         panel = this.getPanel(panel);
44616         if(!panel){
44617             return null;
44618         }
44619         var e = {};
44620         this.fireEvent("beforeremove", this, panel, e);
44621         if(e.cancel === true){
44622             return null;
44623         }
44624         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
44625         var panelId = panel.getId();
44626         this.panels.removeKey(panelId);
44627         if(preservePanel){
44628             document.body.appendChild(panel.getEl().dom);
44629         }
44630         if(this.tabs){
44631             this.tabs.removeTab(panel.getEl().id);
44632         }else if (!preservePanel){
44633             this.bodyEl.dom.removeChild(panel.getEl().dom);
44634         }
44635         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
44636             var p = this.panels.first();
44637             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
44638             tempEl.appendChild(p.getEl().dom);
44639             this.bodyEl.update("");
44640             this.bodyEl.dom.appendChild(p.getEl().dom);
44641             tempEl = null;
44642             this.updateTitle(p.getTitle());
44643             this.tabs = null;
44644             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
44645             this.setActivePanel(p);
44646         }
44647         panel.setRegion(null);
44648         if(this.activePanel == panel){
44649             this.activePanel = null;
44650         }
44651         if(this.config.autoDestroy !== false && preservePanel !== true){
44652             try{panel.destroy();}catch(e){}
44653         }
44654         this.fireEvent("panelremoved", this, panel);
44655         return panel;
44656     },
44657
44658     /**
44659      * Returns the TabPanel component used by this region
44660      * @return {Roo.TabPanel}
44661      */
44662     getTabs : function(){
44663         return this.tabs;
44664     },
44665
44666     createTool : function(parentEl, className){
44667         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
44668             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
44669         btn.addClassOnOver("x-layout-tools-button-over");
44670         return btn;
44671     }
44672 });/*
44673  * Based on:
44674  * Ext JS Library 1.1.1
44675  * Copyright(c) 2006-2007, Ext JS, LLC.
44676  *
44677  * Originally Released Under LGPL - original licence link has changed is not relivant.
44678  *
44679  * Fork - LGPL
44680  * <script type="text/javascript">
44681  */
44682  
44683
44684
44685 /**
44686  * @class Roo.SplitLayoutRegion
44687  * @extends Roo.LayoutRegion
44688  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
44689  */
44690 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
44691     this.cursor = cursor;
44692     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
44693 };
44694
44695 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
44696     splitTip : "Drag to resize.",
44697     collapsibleSplitTip : "Drag to resize. Double click to hide.",
44698     useSplitTips : false,
44699
44700     applyConfig : function(config){
44701         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
44702         if(config.split){
44703             if(!this.split){
44704                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
44705                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
44706                 /** The SplitBar for this region 
44707                 * @type Roo.SplitBar */
44708                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
44709                 this.split.on("moved", this.onSplitMove, this);
44710                 this.split.useShim = config.useShim === true;
44711                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
44712                 if(this.useSplitTips){
44713                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
44714                 }
44715                 if(config.collapsible){
44716                     this.split.el.on("dblclick", this.collapse,  this);
44717                 }
44718             }
44719             if(typeof config.minSize != "undefined"){
44720                 this.split.minSize = config.minSize;
44721             }
44722             if(typeof config.maxSize != "undefined"){
44723                 this.split.maxSize = config.maxSize;
44724             }
44725             if(config.hideWhenEmpty || config.hidden || config.collapsed){
44726                 this.hideSplitter();
44727             }
44728         }
44729     },
44730
44731     getHMaxSize : function(){
44732          var cmax = this.config.maxSize || 10000;
44733          var center = this.mgr.getRegion("center");
44734          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
44735     },
44736
44737     getVMaxSize : function(){
44738          var cmax = this.config.maxSize || 10000;
44739          var center = this.mgr.getRegion("center");
44740          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
44741     },
44742
44743     onSplitMove : function(split, newSize){
44744         this.fireEvent("resized", this, newSize);
44745     },
44746     
44747     /** 
44748      * Returns the {@link Roo.SplitBar} for this region.
44749      * @return {Roo.SplitBar}
44750      */
44751     getSplitBar : function(){
44752         return this.split;
44753     },
44754     
44755     hide : function(){
44756         this.hideSplitter();
44757         Roo.SplitLayoutRegion.superclass.hide.call(this);
44758     },
44759
44760     hideSplitter : function(){
44761         if(this.split){
44762             this.split.el.setLocation(-2000,-2000);
44763             this.split.el.hide();
44764         }
44765     },
44766
44767     show : function(){
44768         if(this.split){
44769             this.split.el.show();
44770         }
44771         Roo.SplitLayoutRegion.superclass.show.call(this);
44772     },
44773     
44774     beforeSlide: function(){
44775         if(Roo.isGecko){// firefox overflow auto bug workaround
44776             this.bodyEl.clip();
44777             if(this.tabs) this.tabs.bodyEl.clip();
44778             if(this.activePanel){
44779                 this.activePanel.getEl().clip();
44780                 
44781                 if(this.activePanel.beforeSlide){
44782                     this.activePanel.beforeSlide();
44783                 }
44784             }
44785         }
44786     },
44787     
44788     afterSlide : function(){
44789         if(Roo.isGecko){// firefox overflow auto bug workaround
44790             this.bodyEl.unclip();
44791             if(this.tabs) this.tabs.bodyEl.unclip();
44792             if(this.activePanel){
44793                 this.activePanel.getEl().unclip();
44794                 if(this.activePanel.afterSlide){
44795                     this.activePanel.afterSlide();
44796                 }
44797             }
44798         }
44799     },
44800
44801     initAutoHide : function(){
44802         if(this.autoHide !== false){
44803             if(!this.autoHideHd){
44804                 var st = new Roo.util.DelayedTask(this.slideIn, this);
44805                 this.autoHideHd = {
44806                     "mouseout": function(e){
44807                         if(!e.within(this.el, true)){
44808                             st.delay(500);
44809                         }
44810                     },
44811                     "mouseover" : function(e){
44812                         st.cancel();
44813                     },
44814                     scope : this
44815                 };
44816             }
44817             this.el.on(this.autoHideHd);
44818         }
44819     },
44820
44821     clearAutoHide : function(){
44822         if(this.autoHide !== false){
44823             this.el.un("mouseout", this.autoHideHd.mouseout);
44824             this.el.un("mouseover", this.autoHideHd.mouseover);
44825         }
44826     },
44827
44828     clearMonitor : function(){
44829         Roo.get(document).un("click", this.slideInIf, this);
44830     },
44831
44832     // these names are backwards but not changed for compat
44833     slideOut : function(){
44834         if(this.isSlid || this.el.hasActiveFx()){
44835             return;
44836         }
44837         this.isSlid = true;
44838         if(this.collapseBtn){
44839             this.collapseBtn.hide();
44840         }
44841         this.closeBtnState = this.closeBtn.getStyle('display');
44842         this.closeBtn.hide();
44843         if(this.stickBtn){
44844             this.stickBtn.show();
44845         }
44846         this.el.show();
44847         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
44848         this.beforeSlide();
44849         this.el.setStyle("z-index", 10001);
44850         this.el.slideIn(this.getSlideAnchor(), {
44851             callback: function(){
44852                 this.afterSlide();
44853                 this.initAutoHide();
44854                 Roo.get(document).on("click", this.slideInIf, this);
44855                 this.fireEvent("slideshow", this);
44856             },
44857             scope: this,
44858             block: true
44859         });
44860     },
44861
44862     afterSlideIn : function(){
44863         this.clearAutoHide();
44864         this.isSlid = false;
44865         this.clearMonitor();
44866         this.el.setStyle("z-index", "");
44867         if(this.collapseBtn){
44868             this.collapseBtn.show();
44869         }
44870         this.closeBtn.setStyle('display', this.closeBtnState);
44871         if(this.stickBtn){
44872             this.stickBtn.hide();
44873         }
44874         this.fireEvent("slidehide", this);
44875     },
44876
44877     slideIn : function(cb){
44878         if(!this.isSlid || this.el.hasActiveFx()){
44879             Roo.callback(cb);
44880             return;
44881         }
44882         this.isSlid = false;
44883         this.beforeSlide();
44884         this.el.slideOut(this.getSlideAnchor(), {
44885             callback: function(){
44886                 this.el.setLeftTop(-10000, -10000);
44887                 this.afterSlide();
44888                 this.afterSlideIn();
44889                 Roo.callback(cb);
44890             },
44891             scope: this,
44892             block: true
44893         });
44894     },
44895     
44896     slideInIf : function(e){
44897         if(!e.within(this.el)){
44898             this.slideIn();
44899         }
44900     },
44901
44902     animateCollapse : function(){
44903         this.beforeSlide();
44904         this.el.setStyle("z-index", 20000);
44905         var anchor = this.getSlideAnchor();
44906         this.el.slideOut(anchor, {
44907             callback : function(){
44908                 this.el.setStyle("z-index", "");
44909                 this.collapsedEl.slideIn(anchor, {duration:.3});
44910                 this.afterSlide();
44911                 this.el.setLocation(-10000,-10000);
44912                 this.el.hide();
44913                 this.fireEvent("collapsed", this);
44914             },
44915             scope: this,
44916             block: true
44917         });
44918     },
44919
44920     animateExpand : function(){
44921         this.beforeSlide();
44922         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
44923         this.el.setStyle("z-index", 20000);
44924         this.collapsedEl.hide({
44925             duration:.1
44926         });
44927         this.el.slideIn(this.getSlideAnchor(), {
44928             callback : function(){
44929                 this.el.setStyle("z-index", "");
44930                 this.afterSlide();
44931                 if(this.split){
44932                     this.split.el.show();
44933                 }
44934                 this.fireEvent("invalidated", this);
44935                 this.fireEvent("expanded", this);
44936             },
44937             scope: this,
44938             block: true
44939         });
44940     },
44941
44942     anchors : {
44943         "west" : "left",
44944         "east" : "right",
44945         "north" : "top",
44946         "south" : "bottom"
44947     },
44948
44949     sanchors : {
44950         "west" : "l",
44951         "east" : "r",
44952         "north" : "t",
44953         "south" : "b"
44954     },
44955
44956     canchors : {
44957         "west" : "tl-tr",
44958         "east" : "tr-tl",
44959         "north" : "tl-bl",
44960         "south" : "bl-tl"
44961     },
44962
44963     getAnchor : function(){
44964         return this.anchors[this.position];
44965     },
44966
44967     getCollapseAnchor : function(){
44968         return this.canchors[this.position];
44969     },
44970
44971     getSlideAnchor : function(){
44972         return this.sanchors[this.position];
44973     },
44974
44975     getAlignAdj : function(){
44976         var cm = this.cmargins;
44977         switch(this.position){
44978             case "west":
44979                 return [0, 0];
44980             break;
44981             case "east":
44982                 return [0, 0];
44983             break;
44984             case "north":
44985                 return [0, 0];
44986             break;
44987             case "south":
44988                 return [0, 0];
44989             break;
44990         }
44991     },
44992
44993     getExpandAdj : function(){
44994         var c = this.collapsedEl, cm = this.cmargins;
44995         switch(this.position){
44996             case "west":
44997                 return [-(cm.right+c.getWidth()+cm.left), 0];
44998             break;
44999             case "east":
45000                 return [cm.right+c.getWidth()+cm.left, 0];
45001             break;
45002             case "north":
45003                 return [0, -(cm.top+cm.bottom+c.getHeight())];
45004             break;
45005             case "south":
45006                 return [0, cm.top+cm.bottom+c.getHeight()];
45007             break;
45008         }
45009     }
45010 });/*
45011  * Based on:
45012  * Ext JS Library 1.1.1
45013  * Copyright(c) 2006-2007, Ext JS, LLC.
45014  *
45015  * Originally Released Under LGPL - original licence link has changed is not relivant.
45016  *
45017  * Fork - LGPL
45018  * <script type="text/javascript">
45019  */
45020 /*
45021  * These classes are private internal classes
45022  */
45023 Roo.CenterLayoutRegion = function(mgr, config){
45024     Roo.LayoutRegion.call(this, mgr, config, "center");
45025     this.visible = true;
45026     this.minWidth = config.minWidth || 20;
45027     this.minHeight = config.minHeight || 20;
45028 };
45029
45030 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
45031     hide : function(){
45032         // center panel can't be hidden
45033     },
45034     
45035     show : function(){
45036         // center panel can't be hidden
45037     },
45038     
45039     getMinWidth: function(){
45040         return this.minWidth;
45041     },
45042     
45043     getMinHeight: function(){
45044         return this.minHeight;
45045     }
45046 });
45047
45048
45049 Roo.NorthLayoutRegion = function(mgr, config){
45050     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
45051     if(this.split){
45052         this.split.placement = Roo.SplitBar.TOP;
45053         this.split.orientation = Roo.SplitBar.VERTICAL;
45054         this.split.el.addClass("x-layout-split-v");
45055     }
45056     var size = config.initialSize || config.height;
45057     if(typeof size != "undefined"){
45058         this.el.setHeight(size);
45059     }
45060 };
45061 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
45062     orientation: Roo.SplitBar.VERTICAL,
45063     getBox : function(){
45064         if(this.collapsed){
45065             return this.collapsedEl.getBox();
45066         }
45067         var box = this.el.getBox();
45068         if(this.split){
45069             box.height += this.split.el.getHeight();
45070         }
45071         return box;
45072     },
45073     
45074     updateBox : function(box){
45075         if(this.split && !this.collapsed){
45076             box.height -= this.split.el.getHeight();
45077             this.split.el.setLeft(box.x);
45078             this.split.el.setTop(box.y+box.height);
45079             this.split.el.setWidth(box.width);
45080         }
45081         if(this.collapsed){
45082             this.updateBody(box.width, null);
45083         }
45084         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45085     }
45086 });
45087
45088 Roo.SouthLayoutRegion = function(mgr, config){
45089     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
45090     if(this.split){
45091         this.split.placement = Roo.SplitBar.BOTTOM;
45092         this.split.orientation = Roo.SplitBar.VERTICAL;
45093         this.split.el.addClass("x-layout-split-v");
45094     }
45095     var size = config.initialSize || config.height;
45096     if(typeof size != "undefined"){
45097         this.el.setHeight(size);
45098     }
45099 };
45100 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
45101     orientation: Roo.SplitBar.VERTICAL,
45102     getBox : function(){
45103         if(this.collapsed){
45104             return this.collapsedEl.getBox();
45105         }
45106         var box = this.el.getBox();
45107         if(this.split){
45108             var sh = this.split.el.getHeight();
45109             box.height += sh;
45110             box.y -= sh;
45111         }
45112         return box;
45113     },
45114     
45115     updateBox : function(box){
45116         if(this.split && !this.collapsed){
45117             var sh = this.split.el.getHeight();
45118             box.height -= sh;
45119             box.y += sh;
45120             this.split.el.setLeft(box.x);
45121             this.split.el.setTop(box.y-sh);
45122             this.split.el.setWidth(box.width);
45123         }
45124         if(this.collapsed){
45125             this.updateBody(box.width, null);
45126         }
45127         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45128     }
45129 });
45130
45131 Roo.EastLayoutRegion = function(mgr, config){
45132     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
45133     if(this.split){
45134         this.split.placement = Roo.SplitBar.RIGHT;
45135         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45136         this.split.el.addClass("x-layout-split-h");
45137     }
45138     var size = config.initialSize || config.width;
45139     if(typeof size != "undefined"){
45140         this.el.setWidth(size);
45141     }
45142 };
45143 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
45144     orientation: Roo.SplitBar.HORIZONTAL,
45145     getBox : function(){
45146         if(this.collapsed){
45147             return this.collapsedEl.getBox();
45148         }
45149         var box = this.el.getBox();
45150         if(this.split){
45151             var sw = this.split.el.getWidth();
45152             box.width += sw;
45153             box.x -= sw;
45154         }
45155         return box;
45156     },
45157
45158     updateBox : function(box){
45159         if(this.split && !this.collapsed){
45160             var sw = this.split.el.getWidth();
45161             box.width -= sw;
45162             this.split.el.setLeft(box.x);
45163             this.split.el.setTop(box.y);
45164             this.split.el.setHeight(box.height);
45165             box.x += sw;
45166         }
45167         if(this.collapsed){
45168             this.updateBody(null, box.height);
45169         }
45170         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45171     }
45172 });
45173
45174 Roo.WestLayoutRegion = function(mgr, config){
45175     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
45176     if(this.split){
45177         this.split.placement = Roo.SplitBar.LEFT;
45178         this.split.orientation = Roo.SplitBar.HORIZONTAL;
45179         this.split.el.addClass("x-layout-split-h");
45180     }
45181     var size = config.initialSize || config.width;
45182     if(typeof size != "undefined"){
45183         this.el.setWidth(size);
45184     }
45185 };
45186 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
45187     orientation: Roo.SplitBar.HORIZONTAL,
45188     getBox : function(){
45189         if(this.collapsed){
45190             return this.collapsedEl.getBox();
45191         }
45192         var box = this.el.getBox();
45193         if(this.split){
45194             box.width += this.split.el.getWidth();
45195         }
45196         return box;
45197     },
45198     
45199     updateBox : function(box){
45200         if(this.split && !this.collapsed){
45201             var sw = this.split.el.getWidth();
45202             box.width -= sw;
45203             this.split.el.setLeft(box.x+box.width);
45204             this.split.el.setTop(box.y);
45205             this.split.el.setHeight(box.height);
45206         }
45207         if(this.collapsed){
45208             this.updateBody(null, box.height);
45209         }
45210         Roo.LayoutRegion.prototype.updateBox.call(this, box);
45211     }
45212 });
45213 /*
45214  * Based on:
45215  * Ext JS Library 1.1.1
45216  * Copyright(c) 2006-2007, Ext JS, LLC.
45217  *
45218  * Originally Released Under LGPL - original licence link has changed is not relivant.
45219  *
45220  * Fork - LGPL
45221  * <script type="text/javascript">
45222  */
45223  
45224  
45225 /*
45226  * Private internal class for reading and applying state
45227  */
45228 Roo.LayoutStateManager = function(layout){
45229      // default empty state
45230      this.state = {
45231         north: {},
45232         south: {},
45233         east: {},
45234         west: {}       
45235     };
45236 };
45237
45238 Roo.LayoutStateManager.prototype = {
45239     init : function(layout, provider){
45240         this.provider = provider;
45241         var state = provider.get(layout.id+"-layout-state");
45242         if(state){
45243             var wasUpdating = layout.isUpdating();
45244             if(!wasUpdating){
45245                 layout.beginUpdate();
45246             }
45247             for(var key in state){
45248                 if(typeof state[key] != "function"){
45249                     var rstate = state[key];
45250                     var r = layout.getRegion(key);
45251                     if(r && rstate){
45252                         if(rstate.size){
45253                             r.resizeTo(rstate.size);
45254                         }
45255                         if(rstate.collapsed == true){
45256                             r.collapse(true);
45257                         }else{
45258                             r.expand(null, true);
45259                         }
45260                     }
45261                 }
45262             }
45263             if(!wasUpdating){
45264                 layout.endUpdate();
45265             }
45266             this.state = state; 
45267         }
45268         this.layout = layout;
45269         layout.on("regionresized", this.onRegionResized, this);
45270         layout.on("regioncollapsed", this.onRegionCollapsed, this);
45271         layout.on("regionexpanded", this.onRegionExpanded, this);
45272     },
45273     
45274     storeState : function(){
45275         this.provider.set(this.layout.id+"-layout-state", this.state);
45276     },
45277     
45278     onRegionResized : function(region, newSize){
45279         this.state[region.getPosition()].size = newSize;
45280         this.storeState();
45281     },
45282     
45283     onRegionCollapsed : function(region){
45284         this.state[region.getPosition()].collapsed = true;
45285         this.storeState();
45286     },
45287     
45288     onRegionExpanded : function(region){
45289         this.state[region.getPosition()].collapsed = false;
45290         this.storeState();
45291     }
45292 };/*
45293  * Based on:
45294  * Ext JS Library 1.1.1
45295  * Copyright(c) 2006-2007, Ext JS, LLC.
45296  *
45297  * Originally Released Under LGPL - original licence link has changed is not relivant.
45298  *
45299  * Fork - LGPL
45300  * <script type="text/javascript">
45301  */
45302 /**
45303  * @class Roo.ContentPanel
45304  * @extends Roo.util.Observable
45305  * A basic ContentPanel element.
45306  * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes  (defaults to false)
45307  * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
45308  * @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
45309  * @cfg {Boolean} closable True if the panel can be closed/removed
45310  * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
45311  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
45312  * @cfg {Toolbar} toolbar A toolbar for this panel
45313  * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
45314  * @cfg {String} title The title for this panel
45315  * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
45316  * @cfg {String} url Calls {@link #setUrl} with this value
45317  * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
45318  * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
45319  * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
45320  * @constructor
45321  * Create a new ContentPanel.
45322  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
45323  * @param {String/Object} config A string to set only the title or a config object
45324  * @param {String} content (optional) Set the HTML content for this panel
45325  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
45326  */
45327 Roo.ContentPanel = function(el, config, content){
45328     
45329      
45330     /*
45331     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
45332         config = el;
45333         el = Roo.id();
45334     }
45335     if (config && config.parentLayout) { 
45336         el = config.parentLayout.el.createChild(); 
45337     }
45338     */
45339     if(el.autoCreate){ // xtype is available if this is called from factory
45340         config = el;
45341         el = Roo.id();
45342     }
45343     this.el = Roo.get(el);
45344     if(!this.el && config && config.autoCreate){
45345         if(typeof config.autoCreate == "object"){
45346             if(!config.autoCreate.id){
45347                 config.autoCreate.id = config.id||el;
45348             }
45349             this.el = Roo.DomHelper.append(document.body,
45350                         config.autoCreate, true);
45351         }else{
45352             this.el = Roo.DomHelper.append(document.body,
45353                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
45354         }
45355     }
45356     this.closable = false;
45357     this.loaded = false;
45358     this.active = false;
45359     if(typeof config == "string"){
45360         this.title = config;
45361     }else{
45362         Roo.apply(this, config);
45363     }
45364     
45365     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
45366         this.wrapEl = this.el.wrap();    
45367         this.toolbar = new Roo.Toolbar(this.el.insertSibling(false, 'before'), [] , this.toolbar);
45368         
45369     }
45370     
45371     
45372     
45373     if(this.resizeEl){
45374         this.resizeEl = Roo.get(this.resizeEl, true);
45375     }else{
45376         this.resizeEl = this.el;
45377     }
45378     this.addEvents({
45379         /**
45380          * @event activate
45381          * Fires when this panel is activated. 
45382          * @param {Roo.ContentPanel} this
45383          */
45384         "activate" : true,
45385         /**
45386          * @event deactivate
45387          * Fires when this panel is activated. 
45388          * @param {Roo.ContentPanel} this
45389          */
45390         "deactivate" : true,
45391
45392         /**
45393          * @event resize
45394          * Fires when this panel is resized if fitToFrame is true.
45395          * @param {Roo.ContentPanel} this
45396          * @param {Number} width The width after any component adjustments
45397          * @param {Number} height The height after any component adjustments
45398          */
45399         "resize" : true
45400     });
45401     if(this.autoScroll){
45402         this.resizeEl.setStyle("overflow", "auto");
45403     } else {
45404         // fix randome scrolling
45405         this.el.on('scroll', function() {
45406             this.scrollTo('top',0); 
45407         });
45408     }
45409     content = content || this.content;
45410     if(content){
45411         this.setContent(content);
45412     }
45413     if(config && config.url){
45414         this.setUrl(this.url, this.params, this.loadOnce);
45415     }
45416     
45417     
45418     
45419     Roo.ContentPanel.superclass.constructor.call(this);
45420 };
45421
45422 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
45423     tabTip:'',
45424     setRegion : function(region){
45425         this.region = region;
45426         if(region){
45427            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
45428         }else{
45429            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
45430         } 
45431     },
45432     
45433     /**
45434      * Returns the toolbar for this Panel if one was configured. 
45435      * @return {Roo.Toolbar} 
45436      */
45437     getToolbar : function(){
45438         return this.toolbar;
45439     },
45440     
45441     setActiveState : function(active){
45442         this.active = active;
45443         if(!active){
45444             this.fireEvent("deactivate", this);
45445         }else{
45446             this.fireEvent("activate", this);
45447         }
45448     },
45449     /**
45450      * Updates this panel's element
45451      * @param {String} content The new content
45452      * @param {Boolean} loadScripts (optional) true to look for and process scripts
45453     */
45454     setContent : function(content, loadScripts){
45455         this.el.update(content, loadScripts);
45456     },
45457
45458     ignoreResize : function(w, h){
45459         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
45460             return true;
45461         }else{
45462             this.lastSize = {width: w, height: h};
45463             return false;
45464         }
45465     },
45466     /**
45467      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
45468      * @return {Roo.UpdateManager} The UpdateManager
45469      */
45470     getUpdateManager : function(){
45471         return this.el.getUpdateManager();
45472     },
45473      /**
45474      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
45475      * @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:
45476 <pre><code>
45477 panel.load({
45478     url: "your-url.php",
45479     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
45480     callback: yourFunction,
45481     scope: yourObject, //(optional scope)
45482     discardUrl: false,
45483     nocache: false,
45484     text: "Loading...",
45485     timeout: 30,
45486     scripts: false
45487 });
45488 </code></pre>
45489      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
45490      * 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.
45491      * @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}
45492      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
45493      * @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.
45494      * @return {Roo.ContentPanel} this
45495      */
45496     load : function(){
45497         var um = this.el.getUpdateManager();
45498         um.update.apply(um, arguments);
45499         return this;
45500     },
45501
45502
45503     /**
45504      * 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.
45505      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
45506      * @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)
45507      * @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)
45508      * @return {Roo.UpdateManager} The UpdateManager
45509      */
45510     setUrl : function(url, params, loadOnce){
45511         if(this.refreshDelegate){
45512             this.removeListener("activate", this.refreshDelegate);
45513         }
45514         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
45515         this.on("activate", this.refreshDelegate);
45516         return this.el.getUpdateManager();
45517     },
45518     
45519     _handleRefresh : function(url, params, loadOnce){
45520         if(!loadOnce || !this.loaded){
45521             var updater = this.el.getUpdateManager();
45522             updater.update(url, params, this._setLoaded.createDelegate(this));
45523         }
45524     },
45525     
45526     _setLoaded : function(){
45527         this.loaded = true;
45528     }, 
45529     
45530     /**
45531      * Returns this panel's id
45532      * @return {String} 
45533      */
45534     getId : function(){
45535         return this.el.id;
45536     },
45537     
45538     /** 
45539      * Returns this panel's element - used by regiosn to add.
45540      * @return {Roo.Element} 
45541      */
45542     getEl : function(){
45543         return this.wrapEl || this.el;
45544     },
45545     
45546     adjustForComponents : function(width, height){
45547         if(this.resizeEl != this.el){
45548             width -= this.el.getFrameWidth('lr');
45549             height -= this.el.getFrameWidth('tb');
45550         }
45551         if(this.toolbar){
45552             var te = this.toolbar.getEl();
45553             height -= te.getHeight();
45554             te.setWidth(width);
45555         }
45556         if(this.adjustments){
45557             width += this.adjustments[0];
45558             height += this.adjustments[1];
45559         }
45560         return {"width": width, "height": height};
45561     },
45562     
45563     setSize : function(width, height){
45564         if(this.fitToFrame && !this.ignoreResize(width, height)){
45565             if(this.fitContainer && this.resizeEl != this.el){
45566                 this.el.setSize(width, height);
45567             }
45568             var size = this.adjustForComponents(width, height);
45569             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
45570             this.fireEvent('resize', this, size.width, size.height);
45571         }
45572     },
45573     
45574     /**
45575      * Returns this panel's title
45576      * @return {String} 
45577      */
45578     getTitle : function(){
45579         return this.title;
45580     },
45581     
45582     /**
45583      * Set this panel's title
45584      * @param {String} title
45585      */
45586     setTitle : function(title){
45587         this.title = title;
45588         if(this.region){
45589             this.region.updatePanelTitle(this, title);
45590         }
45591     },
45592     
45593     /**
45594      * Returns true is this panel was configured to be closable
45595      * @return {Boolean} 
45596      */
45597     isClosable : function(){
45598         return this.closable;
45599     },
45600     
45601     beforeSlide : function(){
45602         this.el.clip();
45603         this.resizeEl.clip();
45604     },
45605     
45606     afterSlide : function(){
45607         this.el.unclip();
45608         this.resizeEl.unclip();
45609     },
45610     
45611     /**
45612      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
45613      *   Will fail silently if the {@link #setUrl} method has not been called.
45614      *   This does not activate the panel, just updates its content.
45615      */
45616     refresh : function(){
45617         if(this.refreshDelegate){
45618            this.loaded = false;
45619            this.refreshDelegate();
45620         }
45621     },
45622     
45623     /**
45624      * Destroys this panel
45625      */
45626     destroy : function(){
45627         this.el.removeAllListeners();
45628         var tempEl = document.createElement("span");
45629         tempEl.appendChild(this.el.dom);
45630         tempEl.innerHTML = "";
45631         this.el.remove();
45632         this.el = null;
45633     },
45634     
45635       /**
45636      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
45637      * <pre><code>
45638
45639 layout.addxtype({
45640        xtype : 'Form',
45641        items: [ .... ]
45642    }
45643 );
45644
45645 </code></pre>
45646      * @param {Object} cfg Xtype definition of item to add.
45647      */
45648     
45649     addxtype : function(cfg) {
45650         // add form..
45651         if (cfg.xtype.match(/^Form$/)) {
45652             var el = this.el.createChild();
45653
45654             this.form = new  Roo.form.Form(cfg);
45655             
45656             
45657             if ( this.form.allItems.length) this.form.render(el.dom);
45658             return this.form;
45659         }
45660         if (['View', 'JsonView'].indexOf(cfg.xtype) > -1) {
45661             // views..
45662             cfg.el = this.el.appendChild(document.createElement("div"));
45663             // factory?
45664             var ret = new Roo[cfg.xtype](cfg);
45665             ret.render(false, ''); // render blank..
45666             return ret;
45667             
45668         }
45669         return false;
45670         
45671     }
45672 });
45673
45674 /**
45675  * @class Roo.GridPanel
45676  * @extends Roo.ContentPanel
45677  * @constructor
45678  * Create a new GridPanel.
45679  * @param {Roo.grid.Grid} grid The grid for this panel
45680  * @param {String/Object} config A string to set only the panel's title, or a config object
45681  */
45682 Roo.GridPanel = function(grid, config){
45683     
45684   
45685     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
45686         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
45687         
45688     this.wrapper.dom.appendChild(grid.getGridEl().dom);
45689     
45690     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
45691     
45692     if(this.toolbar){
45693         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
45694     }
45695     // xtype created footer. - not sure if will work as we normally have to render first..
45696     if (this.footer && !this.footer.el && this.footer.xtype) {
45697         
45698         this.footer.container = this.grid.getView().getFooterPanel(true);
45699         this.footer.dataSource = this.grid.dataSource;
45700         this.footer = Roo.factory(this.footer, Roo);
45701         
45702     }
45703     
45704     grid.monitorWindowResize = false; // turn off autosizing
45705     grid.autoHeight = false;
45706     grid.autoWidth = false;
45707     this.grid = grid;
45708     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
45709 };
45710
45711 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
45712     getId : function(){
45713         return this.grid.id;
45714     },
45715     
45716     /**
45717      * Returns the grid for this panel
45718      * @return {Roo.grid.Grid} 
45719      */
45720     getGrid : function(){
45721         return this.grid;    
45722     },
45723     
45724     setSize : function(width, height){
45725         if(!this.ignoreResize(width, height)){
45726             var grid = this.grid;
45727             var size = this.adjustForComponents(width, height);
45728             grid.getGridEl().setSize(size.width, size.height);
45729             grid.autoSize();
45730         }
45731     },
45732     
45733     beforeSlide : function(){
45734         this.grid.getView().scroller.clip();
45735     },
45736     
45737     afterSlide : function(){
45738         this.grid.getView().scroller.unclip();
45739     },
45740     
45741     destroy : function(){
45742         this.grid.destroy();
45743         delete this.grid;
45744         Roo.GridPanel.superclass.destroy.call(this); 
45745     }
45746 });
45747
45748
45749 /**
45750  * @class Roo.NestedLayoutPanel
45751  * @extends Roo.ContentPanel
45752  * @constructor
45753  * Create a new NestedLayoutPanel.
45754  * 
45755  * 
45756  * @param {Roo.BorderLayout} layout The layout for this panel
45757  * @param {String/Object} config A string to set only the title or a config object
45758  */
45759 Roo.NestedLayoutPanel = function(layout, config)
45760 {
45761     // construct with only one argument..
45762     /* FIXME - implement nicer consturctors
45763     if (layout.layout) {
45764         config = layout;
45765         layout = config.layout;
45766         delete config.layout;
45767     }
45768     if (layout.xtype && !layout.getEl) {
45769         // then layout needs constructing..
45770         layout = Roo.factory(layout, Roo);
45771     }
45772     */
45773     
45774     
45775     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
45776     
45777     layout.monitorWindowResize = false; // turn off autosizing
45778     this.layout = layout;
45779     this.layout.getEl().addClass("x-layout-nested-layout");
45780     
45781     
45782     
45783     
45784 };
45785
45786 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
45787
45788     setSize : function(width, height){
45789         if(!this.ignoreResize(width, height)){
45790             var size = this.adjustForComponents(width, height);
45791             var el = this.layout.getEl();
45792             el.setSize(size.width, size.height);
45793             var touch = el.dom.offsetWidth;
45794             this.layout.layout();
45795             // ie requires a double layout on the first pass
45796             if(Roo.isIE && !this.initialized){
45797                 this.initialized = true;
45798                 this.layout.layout();
45799             }
45800         }
45801     },
45802     
45803     // activate all subpanels if not currently active..
45804     
45805     setActiveState : function(active){
45806         this.active = active;
45807         if(!active){
45808             this.fireEvent("deactivate", this);
45809             return;
45810         }
45811         
45812         this.fireEvent("activate", this);
45813         // not sure if this should happen before or after..
45814         if (!this.layout) {
45815             return; // should not happen..
45816         }
45817         var reg = false;
45818         for (var r in this.layout.regions) {
45819             reg = this.layout.getRegion(r);
45820             if (reg.getActivePanel()) {
45821                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
45822                 reg.setActivePanel(reg.getActivePanel());
45823                 continue;
45824             }
45825             if (!reg.panels.length) {
45826                 continue;
45827             }
45828             reg.showPanel(reg.getPanel(0));
45829         }
45830         
45831         
45832         
45833         
45834     },
45835     
45836     /**
45837      * Returns the nested BorderLayout for this panel
45838      * @return {Roo.BorderLayout} 
45839      */
45840     getLayout : function(){
45841         return this.layout;
45842     },
45843     
45844      /**
45845      * Adds a xtype elements to the layout of the nested panel
45846      * <pre><code>
45847
45848 panel.addxtype({
45849        xtype : 'ContentPanel',
45850        region: 'west',
45851        items: [ .... ]
45852    }
45853 );
45854
45855 panel.addxtype({
45856         xtype : 'NestedLayoutPanel',
45857         region: 'west',
45858         layout: {
45859            center: { },
45860            west: { }   
45861         },
45862         items : [ ... list of content panels or nested layout panels.. ]
45863    }
45864 );
45865 </code></pre>
45866      * @param {Object} cfg Xtype definition of item to add.
45867      */
45868     addxtype : function(cfg) {
45869         return this.layout.addxtype(cfg);
45870     
45871     }
45872 });
45873
45874 Roo.ScrollPanel = function(el, config, content){
45875     config = config || {};
45876     config.fitToFrame = true;
45877     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
45878     
45879     this.el.dom.style.overflow = "hidden";
45880     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
45881     this.el.removeClass("x-layout-inactive-content");
45882     this.el.on("mousewheel", this.onWheel, this);
45883
45884     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
45885     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
45886     up.unselectable(); down.unselectable();
45887     up.on("click", this.scrollUp, this);
45888     down.on("click", this.scrollDown, this);
45889     up.addClassOnOver("x-scroller-btn-over");
45890     down.addClassOnOver("x-scroller-btn-over");
45891     up.addClassOnClick("x-scroller-btn-click");
45892     down.addClassOnClick("x-scroller-btn-click");
45893     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
45894
45895     this.resizeEl = this.el;
45896     this.el = wrap; this.up = up; this.down = down;
45897 };
45898
45899 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
45900     increment : 100,
45901     wheelIncrement : 5,
45902     scrollUp : function(){
45903         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
45904     },
45905
45906     scrollDown : function(){
45907         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
45908     },
45909
45910     afterScroll : function(){
45911         var el = this.resizeEl;
45912         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
45913         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45914         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
45915     },
45916
45917     setSize : function(){
45918         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
45919         this.afterScroll();
45920     },
45921
45922     onWheel : function(e){
45923         var d = e.getWheelDelta();
45924         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
45925         this.afterScroll();
45926         e.stopEvent();
45927     },
45928
45929     setContent : function(content, loadScripts){
45930         this.resizeEl.update(content, loadScripts);
45931     }
45932
45933 });
45934
45935
45936
45937
45938
45939
45940
45941
45942
45943 /**
45944  * @class Roo.TreePanel
45945  * @extends Roo.ContentPanel
45946  * @constructor
45947  * Create a new TreePanel. - defaults to fit/scoll contents.
45948  * @param {String/Object} config A string to set only the panel's title, or a config object
45949  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
45950  */
45951 Roo.TreePanel = function(config){
45952     var el = config.el;
45953     var tree = config.tree;
45954     delete config.tree; 
45955     delete config.el; // hopefull!
45956     
45957     // wrapper for IE7 strict & safari scroll issue
45958     
45959     var treeEl = el.createChild();
45960     config.resizeEl = treeEl;
45961     
45962     
45963     
45964     Roo.TreePanel.superclass.constructor.call(this, el, config);
45965  
45966  
45967     this.tree = new Roo.tree.TreePanel(treeEl , tree);
45968     //console.log(tree);
45969     this.on('activate', function()
45970     {
45971         if (this.tree.rendered) {
45972             return;
45973         }
45974         //console.log('render tree');
45975         this.tree.render();
45976     });
45977     
45978     this.on('resize',  function (cp, w, h) {
45979             this.tree.innerCt.setWidth(w);
45980             this.tree.innerCt.setHeight(h);
45981             this.tree.innerCt.setStyle('overflow-y', 'auto');
45982     });
45983
45984         
45985     
45986 };
45987
45988 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
45989     fitToFrame : true,
45990     autoScroll : true
45991 });
45992
45993
45994
45995
45996
45997
45998
45999
46000
46001
46002
46003 /*
46004  * Based on:
46005  * Ext JS Library 1.1.1
46006  * Copyright(c) 2006-2007, Ext JS, LLC.
46007  *
46008  * Originally Released Under LGPL - original licence link has changed is not relivant.
46009  *
46010  * Fork - LGPL
46011  * <script type="text/javascript">
46012  */
46013  
46014
46015 /**
46016  * @class Roo.ReaderLayout
46017  * @extends Roo.BorderLayout
46018  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
46019  * center region containing two nested regions (a top one for a list view and one for item preview below),
46020  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
46021  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
46022  * expedites the setup of the overall layout and regions for this common application style.
46023  * Example:
46024  <pre><code>
46025 var reader = new Roo.ReaderLayout();
46026 var CP = Roo.ContentPanel;  // shortcut for adding
46027
46028 reader.beginUpdate();
46029 reader.add("north", new CP("north", "North"));
46030 reader.add("west", new CP("west", {title: "West"}));
46031 reader.add("east", new CP("east", {title: "East"}));
46032
46033 reader.regions.listView.add(new CP("listView", "List"));
46034 reader.regions.preview.add(new CP("preview", "Preview"));
46035 reader.endUpdate();
46036 </code></pre>
46037 * @constructor
46038 * Create a new ReaderLayout
46039 * @param {Object} config Configuration options
46040 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
46041 * document.body if omitted)
46042 */
46043 Roo.ReaderLayout = function(config, renderTo){
46044     var c = config || {size:{}};
46045     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
46046         north: c.north !== false ? Roo.apply({
46047             split:false,
46048             initialSize: 32,
46049             titlebar: false
46050         }, c.north) : false,
46051         west: c.west !== false ? Roo.apply({
46052             split:true,
46053             initialSize: 200,
46054             minSize: 175,
46055             maxSize: 400,
46056             titlebar: true,
46057             collapsible: true,
46058             animate: true,
46059             margins:{left:5,right:0,bottom:5,top:5},
46060             cmargins:{left:5,right:5,bottom:5,top:5}
46061         }, c.west) : false,
46062         east: c.east !== false ? Roo.apply({
46063             split:true,
46064             initialSize: 200,
46065             minSize: 175,
46066             maxSize: 400,
46067             titlebar: true,
46068             collapsible: true,
46069             animate: true,
46070             margins:{left:0,right:5,bottom:5,top:5},
46071             cmargins:{left:5,right:5,bottom:5,top:5}
46072         }, c.east) : false,
46073         center: Roo.apply({
46074             tabPosition: 'top',
46075             autoScroll:false,
46076             closeOnTab: true,
46077             titlebar:false,
46078             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
46079         }, c.center)
46080     });
46081
46082     this.el.addClass('x-reader');
46083
46084     this.beginUpdate();
46085
46086     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
46087         south: c.preview !== false ? Roo.apply({
46088             split:true,
46089             initialSize: 200,
46090             minSize: 100,
46091             autoScroll:true,
46092             collapsible:true,
46093             titlebar: true,
46094             cmargins:{top:5,left:0, right:0, bottom:0}
46095         }, c.preview) : false,
46096         center: Roo.apply({
46097             autoScroll:false,
46098             titlebar:false,
46099             minHeight:200
46100         }, c.listView)
46101     });
46102     this.add('center', new Roo.NestedLayoutPanel(inner,
46103             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
46104
46105     this.endUpdate();
46106
46107     this.regions.preview = inner.getRegion('south');
46108     this.regions.listView = inner.getRegion('center');
46109 };
46110
46111 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
46112  * Based on:
46113  * Ext JS Library 1.1.1
46114  * Copyright(c) 2006-2007, Ext JS, LLC.
46115  *
46116  * Originally Released Under LGPL - original licence link has changed is not relivant.
46117  *
46118  * Fork - LGPL
46119  * <script type="text/javascript">
46120  */
46121  
46122 /**
46123  * @class Roo.grid.Grid
46124  * @extends Roo.util.Observable
46125  * This class represents the primary interface of a component based grid control.
46126  * <br><br>Usage:<pre><code>
46127  var grid = new Roo.grid.Grid("my-container-id", {
46128      ds: myDataStore,
46129      cm: myColModel,
46130      selModel: mySelectionModel,
46131      autoSizeColumns: true,
46132      monitorWindowResize: false,
46133      trackMouseOver: true
46134  });
46135  // set any options
46136  grid.render();
46137  * </code></pre>
46138  * <b>Common Problems:</b><br/>
46139  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
46140  * element will correct this<br/>
46141  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
46142  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
46143  * are unpredictable.<br/>
46144  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
46145  * grid to calculate dimensions/offsets.<br/>
46146   * @constructor
46147  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
46148  * The container MUST have some type of size defined for the grid to fill. The container will be
46149  * automatically set to position relative if it isn't already.
46150  * @param {Object} config A config object that sets properties on this grid.
46151  */
46152 Roo.grid.Grid = function(container, config){
46153         // initialize the container
46154         this.container = Roo.get(container);
46155         this.container.update("");
46156         this.container.setStyle("overflow", "hidden");
46157     this.container.addClass('x-grid-container');
46158
46159     this.id = this.container.id;
46160
46161     Roo.apply(this, config);
46162     // check and correct shorthanded configs
46163     if(this.ds){
46164         this.dataSource = this.ds;
46165         delete this.ds;
46166     }
46167     if(this.cm){
46168         this.colModel = this.cm;
46169         delete this.cm;
46170     }
46171     if(this.sm){
46172         this.selModel = this.sm;
46173         delete this.sm;
46174     }
46175
46176     if (this.selModel) {
46177         this.selModel = Roo.factory(this.selModel, Roo.grid);
46178         this.sm = this.selModel;
46179         this.sm.xmodule = this.xmodule || false;
46180     }
46181     if (typeof(this.colModel.config) == 'undefined') {
46182         this.colModel = new Roo.grid.ColumnModel(this.colModel);
46183         this.cm = this.colModel;
46184         this.cm.xmodule = this.xmodule || false;
46185     }
46186     if (this.dataSource) {
46187         this.dataSource= Roo.factory(this.dataSource, Roo.data);
46188         this.ds = this.dataSource;
46189         this.ds.xmodule = this.xmodule || false;
46190         
46191     }
46192     
46193     
46194     
46195     if(this.width){
46196         this.container.setWidth(this.width);
46197     }
46198
46199     if(this.height){
46200         this.container.setHeight(this.height);
46201     }
46202     /** @private */
46203         this.addEvents({
46204             // raw events
46205             /**
46206              * @event click
46207              * The raw click event for the entire grid.
46208              * @param {Roo.EventObject} e
46209              */
46210             "click" : true,
46211             /**
46212              * @event dblclick
46213              * The raw dblclick event for the entire grid.
46214              * @param {Roo.EventObject} e
46215              */
46216             "dblclick" : true,
46217             /**
46218              * @event contextmenu
46219              * The raw contextmenu event for the entire grid.
46220              * @param {Roo.EventObject} e
46221              */
46222             "contextmenu" : true,
46223             /**
46224              * @event mousedown
46225              * The raw mousedown event for the entire grid.
46226              * @param {Roo.EventObject} e
46227              */
46228             "mousedown" : true,
46229             /**
46230              * @event mouseup
46231              * The raw mouseup event for the entire grid.
46232              * @param {Roo.EventObject} e
46233              */
46234             "mouseup" : true,
46235             /**
46236              * @event mouseover
46237              * The raw mouseover event for the entire grid.
46238              * @param {Roo.EventObject} e
46239              */
46240             "mouseover" : true,
46241             /**
46242              * @event mouseout
46243              * The raw mouseout event for the entire grid.
46244              * @param {Roo.EventObject} e
46245              */
46246             "mouseout" : true,
46247             /**
46248              * @event keypress
46249              * The raw keypress event for the entire grid.
46250              * @param {Roo.EventObject} e
46251              */
46252             "keypress" : true,
46253             /**
46254              * @event keydown
46255              * The raw keydown event for the entire grid.
46256              * @param {Roo.EventObject} e
46257              */
46258             "keydown" : true,
46259
46260             // custom events
46261
46262             /**
46263              * @event cellclick
46264              * Fires when a cell is clicked
46265              * @param {Grid} this
46266              * @param {Number} rowIndex
46267              * @param {Number} columnIndex
46268              * @param {Roo.EventObject} e
46269              */
46270             "cellclick" : true,
46271             /**
46272              * @event celldblclick
46273              * Fires when a cell is double clicked
46274              * @param {Grid} this
46275              * @param {Number} rowIndex
46276              * @param {Number} columnIndex
46277              * @param {Roo.EventObject} e
46278              */
46279             "celldblclick" : true,
46280             /**
46281              * @event rowclick
46282              * Fires when a row is clicked
46283              * @param {Grid} this
46284              * @param {Number} rowIndex
46285              * @param {Roo.EventObject} e
46286              */
46287             "rowclick" : true,
46288             /**
46289              * @event rowdblclick
46290              * Fires when a row is double clicked
46291              * @param {Grid} this
46292              * @param {Number} rowIndex
46293              * @param {Roo.EventObject} e
46294              */
46295             "rowdblclick" : true,
46296             /**
46297              * @event headerclick
46298              * Fires when a header is clicked
46299              * @param {Grid} this
46300              * @param {Number} columnIndex
46301              * @param {Roo.EventObject} e
46302              */
46303             "headerclick" : true,
46304             /**
46305              * @event headerdblclick
46306              * Fires when a header cell is double clicked
46307              * @param {Grid} this
46308              * @param {Number} columnIndex
46309              * @param {Roo.EventObject} e
46310              */
46311             "headerdblclick" : true,
46312             /**
46313              * @event rowcontextmenu
46314              * Fires when a row is right clicked
46315              * @param {Grid} this
46316              * @param {Number} rowIndex
46317              * @param {Roo.EventObject} e
46318              */
46319             "rowcontextmenu" : true,
46320             /**
46321          * @event cellcontextmenu
46322          * Fires when a cell is right clicked
46323          * @param {Grid} this
46324          * @param {Number} rowIndex
46325          * @param {Number} cellIndex
46326          * @param {Roo.EventObject} e
46327          */
46328          "cellcontextmenu" : true,
46329             /**
46330              * @event headercontextmenu
46331              * Fires when a header is right clicked
46332              * @param {Grid} this
46333              * @param {Number} columnIndex
46334              * @param {Roo.EventObject} e
46335              */
46336             "headercontextmenu" : true,
46337             /**
46338              * @event bodyscroll
46339              * Fires when the body element is scrolled
46340              * @param {Number} scrollLeft
46341              * @param {Number} scrollTop
46342              */
46343             "bodyscroll" : true,
46344             /**
46345              * @event columnresize
46346              * Fires when the user resizes a column
46347              * @param {Number} columnIndex
46348              * @param {Number} newSize
46349              */
46350             "columnresize" : true,
46351             /**
46352              * @event columnmove
46353              * Fires when the user moves a column
46354              * @param {Number} oldIndex
46355              * @param {Number} newIndex
46356              */
46357             "columnmove" : true,
46358             /**
46359              * @event startdrag
46360              * Fires when row(s) start being dragged
46361              * @param {Grid} this
46362              * @param {Roo.GridDD} dd The drag drop object
46363              * @param {event} e The raw browser event
46364              */
46365             "startdrag" : true,
46366             /**
46367              * @event enddrag
46368              * Fires when a drag operation is complete
46369              * @param {Grid} this
46370              * @param {Roo.GridDD} dd The drag drop object
46371              * @param {event} e The raw browser event
46372              */
46373             "enddrag" : true,
46374             /**
46375              * @event dragdrop
46376              * Fires when dragged row(s) are dropped on a valid DD target
46377              * @param {Grid} this
46378              * @param {Roo.GridDD} dd The drag drop object
46379              * @param {String} targetId The target drag drop object
46380              * @param {event} e The raw browser event
46381              */
46382             "dragdrop" : true,
46383             /**
46384              * @event dragover
46385              * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
46386              * @param {Grid} this
46387              * @param {Roo.GridDD} dd The drag drop object
46388              * @param {String} targetId The target drag drop object
46389              * @param {event} e The raw browser event
46390              */
46391             "dragover" : true,
46392             /**
46393              * @event dragenter
46394              *  Fires when the dragged row(s) first cross another DD target while being dragged
46395              * @param {Grid} this
46396              * @param {Roo.GridDD} dd The drag drop object
46397              * @param {String} targetId The target drag drop object
46398              * @param {event} e The raw browser event
46399              */
46400             "dragenter" : true,
46401             /**
46402              * @event dragout
46403              * Fires when the dragged row(s) leave another DD target while being dragged
46404              * @param {Grid} this
46405              * @param {Roo.GridDD} dd The drag drop object
46406              * @param {String} targetId The target drag drop object
46407              * @param {event} e The raw browser event
46408              */
46409             "dragout" : true,
46410         /**
46411          * @event render
46412          * Fires when the grid is rendered
46413          * @param {Grid} grid
46414          */
46415         render : true
46416     });
46417
46418     Roo.grid.Grid.superclass.constructor.call(this);
46419 };
46420 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
46421     
46422     /**
46423      * @cfg {String} ddGroup - drag drop group.
46424          */
46425     
46426     /**
46427      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
46428          */
46429         minColumnWidth : 25,
46430
46431     /**
46432          * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
46433          * <b>on initial render.</b> It is more efficient to explicitly size the columns
46434          * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
46435          */
46436         autoSizeColumns : false,
46437
46438         /**
46439          * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
46440          */
46441         autoSizeHeaders : true,
46442
46443         /**
46444          * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
46445          */
46446         monitorWindowResize : true,
46447
46448         /**
46449          * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
46450          * rows measured to get a columns size. Default is 0 (all rows).
46451          */
46452         maxRowsToMeasure : 0,
46453
46454         /**
46455          * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
46456          */
46457         trackMouseOver : true,
46458
46459     /**
46460          * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
46461          */
46462     
46463         /**
46464          * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
46465          */
46466         enableDragDrop : false,
46467
46468         /**
46469          * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
46470          */
46471         enableColumnMove : true,
46472
46473         /**
46474          * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
46475          */
46476         enableColumnHide : true,
46477
46478         /**
46479          * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
46480          */
46481         enableRowHeightSync : false,
46482
46483         /**
46484          * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
46485          */
46486         stripeRows : true,
46487
46488         /**
46489          * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
46490          */
46491         autoHeight : false,
46492
46493     /**
46494      * @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.
46495      */
46496     autoExpandColumn : false,
46497
46498     /**
46499     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
46500     * Default is 50.
46501     */
46502     autoExpandMin : 50,
46503
46504     /**
46505     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
46506     */
46507     autoExpandMax : 1000,
46508
46509     /**
46510          * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
46511          */
46512         view : null,
46513
46514         /**
46515      * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
46516          */
46517         loadMask : false,
46518     /**
46519      * @cfg {Roo.dd.DropTarget} dragTarget An {@link Roo.dd.DragTarget} config
46520          */
46521         dropTarget: false,
46522     // private
46523     rendered : false,
46524
46525     /**
46526     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
46527     * of a fixed width. Default is false.
46528     */
46529     /**
46530     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
46531     */
46532     /**
46533      * Called once after all setup has been completed and the grid is ready to be rendered.
46534      * @return {Roo.grid.Grid} this
46535      */
46536     render : function(){
46537         var c = this.container;
46538         // try to detect autoHeight/width mode
46539         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
46540             this.autoHeight = true;
46541         }
46542         var view = this.getView();
46543         view.init(this);
46544
46545         c.on("click", this.onClick, this);
46546         c.on("dblclick", this.onDblClick, this);
46547         c.on("contextmenu", this.onContextMenu, this);
46548         c.on("keydown", this.onKeyDown, this);
46549
46550         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
46551
46552         this.getSelectionModel().init(this);
46553
46554         view.render();
46555
46556         if(this.loadMask){
46557             this.loadMask = new Roo.LoadMask(this.container,
46558                     Roo.apply({store:this.dataSource}, this.loadMask));
46559         }
46560         
46561         
46562         if (this.toolbar && this.toolbar.xtype) {
46563             this.toolbar.container = this.getView().getHeaderPanel(true);
46564             this.toolbar = new Ext.Toolbar(this.toolbar);
46565         }
46566         if (this.footer && this.footer.xtype) {
46567             this.footer.dataSource = this.getDataSource();
46568             this.footer.container = this.getView().getFooterPanel(true);
46569             this.footer = Roo.factory(this.footer, Roo);
46570         }
46571         if (this.dropTarget && this.dropTarget.xtype) {
46572             delete this.dropTarget.xtype;
46573             this.dropTarget =  new Ext.dd.DropTarget(this.getView().mainBody, this.dropTarget);
46574         }
46575         
46576         
46577         this.rendered = true;
46578         this.fireEvent('render', this);
46579         return this;
46580     },
46581
46582         /**
46583          * Reconfigures the grid to use a different Store and Column Model.
46584          * The View will be bound to the new objects and refreshed.
46585          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
46586          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
46587          */
46588     reconfigure : function(dataSource, colModel){
46589         if(this.loadMask){
46590             this.loadMask.destroy();
46591             this.loadMask = new Roo.LoadMask(this.container,
46592                     Roo.apply({store:dataSource}, this.loadMask));
46593         }
46594         this.view.bind(dataSource, colModel);
46595         this.dataSource = dataSource;
46596         this.colModel = colModel;
46597         this.view.refresh(true);
46598     },
46599
46600     // private
46601     onKeyDown : function(e){
46602         this.fireEvent("keydown", e);
46603     },
46604
46605     /**
46606      * Destroy this grid.
46607      * @param {Boolean} removeEl True to remove the element
46608      */
46609     destroy : function(removeEl, keepListeners){
46610         if(this.loadMask){
46611             this.loadMask.destroy();
46612         }
46613         var c = this.container;
46614         c.removeAllListeners();
46615         this.view.destroy();
46616         this.colModel.purgeListeners();
46617         if(!keepListeners){
46618             this.purgeListeners();
46619         }
46620         c.update("");
46621         if(removeEl === true){
46622             c.remove();
46623         }
46624     },
46625
46626     // private
46627     processEvent : function(name, e){
46628         this.fireEvent(name, e);
46629         var t = e.getTarget();
46630         var v = this.view;
46631         var header = v.findHeaderIndex(t);
46632         if(header !== false){
46633             this.fireEvent("header" + name, this, header, e);
46634         }else{
46635             var row = v.findRowIndex(t);
46636             var cell = v.findCellIndex(t);
46637             if(row !== false){
46638                 this.fireEvent("row" + name, this, row, e);
46639                 if(cell !== false){
46640                     this.fireEvent("cell" + name, this, row, cell, e);
46641                 }
46642             }
46643         }
46644     },
46645
46646     // private
46647     onClick : function(e){
46648         this.processEvent("click", e);
46649     },
46650
46651     // private
46652     onContextMenu : function(e, t){
46653         this.processEvent("contextmenu", e);
46654     },
46655
46656     // private
46657     onDblClick : function(e){
46658         this.processEvent("dblclick", e);
46659     },
46660
46661     // private
46662     walkCells : function(row, col, step, fn, scope){
46663         var cm = this.colModel, clen = cm.getColumnCount();
46664         var ds = this.dataSource, rlen = ds.getCount(), first = true;
46665         if(step < 0){
46666             if(col < 0){
46667                 row--;
46668                 first = false;
46669             }
46670             while(row >= 0){
46671                 if(!first){
46672                     col = clen-1;
46673                 }
46674                 first = false;
46675                 while(col >= 0){
46676                     if(fn.call(scope || this, row, col, cm) === true){
46677                         return [row, col];
46678                     }
46679                     col--;
46680                 }
46681                 row--;
46682             }
46683         } else {
46684             if(col >= clen){
46685                 row++;
46686                 first = false;
46687             }
46688             while(row < rlen){
46689                 if(!first){
46690                     col = 0;
46691                 }
46692                 first = false;
46693                 while(col < clen){
46694                     if(fn.call(scope || this, row, col, cm) === true){
46695                         return [row, col];
46696                     }
46697                     col++;
46698                 }
46699                 row++;
46700             }
46701         }
46702         return null;
46703     },
46704
46705     // private
46706     getSelections : function(){
46707         return this.selModel.getSelections();
46708     },
46709
46710     /**
46711      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
46712      * but if manual update is required this method will initiate it.
46713      */
46714     autoSize : function(){
46715         if(this.rendered){
46716             this.view.layout();
46717             if(this.view.adjustForScroll){
46718                 this.view.adjustForScroll();
46719             }
46720         }
46721     },
46722
46723     /**
46724      * Returns the grid's underlying element.
46725      * @return {Element} The element
46726      */
46727     getGridEl : function(){
46728         return this.container;
46729     },
46730
46731     // private for compatibility, overridden by editor grid
46732     stopEditing : function(){},
46733
46734     /**
46735      * Returns the grid's SelectionModel.
46736      * @return {SelectionModel}
46737      */
46738     getSelectionModel : function(){
46739         if(!this.selModel){
46740             this.selModel = new Roo.grid.RowSelectionModel();
46741         }
46742         return this.selModel;
46743     },
46744
46745     /**
46746      * Returns the grid's DataSource.
46747      * @return {DataSource}
46748      */
46749     getDataSource : function(){
46750         return this.dataSource;
46751     },
46752
46753     /**
46754      * Returns the grid's ColumnModel.
46755      * @return {ColumnModel}
46756      */
46757     getColumnModel : function(){
46758         return this.colModel;
46759     },
46760
46761     /**
46762      * Returns the grid's GridView object.
46763      * @return {GridView}
46764      */
46765     getView : function(){
46766         if(!this.view){
46767             this.view = new Roo.grid.GridView(this.viewConfig);
46768         }
46769         return this.view;
46770     },
46771     /**
46772      * Called to get grid's drag proxy text, by default returns this.ddText.
46773      * @return {String}
46774      */
46775     getDragDropText : function(){
46776         var count = this.selModel.getCount();
46777         return String.format(this.ddText, count, count == 1 ? '' : 's');
46778     }
46779 });
46780 /**
46781  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
46782  * %0 is replaced with the number of selected rows.
46783  * @type String
46784  */
46785 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
46786  * Based on:
46787  * Ext JS Library 1.1.1
46788  * Copyright(c) 2006-2007, Ext JS, LLC.
46789  *
46790  * Originally Released Under LGPL - original licence link has changed is not relivant.
46791  *
46792  * Fork - LGPL
46793  * <script type="text/javascript">
46794  */
46795  
46796 Roo.grid.AbstractGridView = function(){
46797         this.grid = null;
46798         
46799         this.events = {
46800             "beforerowremoved" : true,
46801             "beforerowsinserted" : true,
46802             "beforerefresh" : true,
46803             "rowremoved" : true,
46804             "rowsinserted" : true,
46805             "rowupdated" : true,
46806             "refresh" : true
46807         };
46808     Roo.grid.AbstractGridView.superclass.constructor.call(this);
46809 };
46810
46811 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
46812     rowClass : "x-grid-row",
46813     cellClass : "x-grid-cell",
46814     tdClass : "x-grid-td",
46815     hdClass : "x-grid-hd",
46816     splitClass : "x-grid-hd-split",
46817     
46818         init: function(grid){
46819         this.grid = grid;
46820                 var cid = this.grid.getGridEl().id;
46821         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
46822         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
46823         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
46824         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
46825         },
46826         
46827         getColumnRenderers : function(){
46828         var renderers = [];
46829         var cm = this.grid.colModel;
46830         var colCount = cm.getColumnCount();
46831         for(var i = 0; i < colCount; i++){
46832             renderers[i] = cm.getRenderer(i);
46833         }
46834         return renderers;
46835     },
46836     
46837     getColumnIds : function(){
46838         var ids = [];
46839         var cm = this.grid.colModel;
46840         var colCount = cm.getColumnCount();
46841         for(var i = 0; i < colCount; i++){
46842             ids[i] = cm.getColumnId(i);
46843         }
46844         return ids;
46845     },
46846     
46847     getDataIndexes : function(){
46848         if(!this.indexMap){
46849             this.indexMap = this.buildIndexMap();
46850         }
46851         return this.indexMap.colToData;
46852     },
46853     
46854     getColumnIndexByDataIndex : function(dataIndex){
46855         if(!this.indexMap){
46856             this.indexMap = this.buildIndexMap();
46857         }
46858         return this.indexMap.dataToCol[dataIndex];
46859     },
46860     
46861     /**
46862      * Set a css style for a column dynamically. 
46863      * @param {Number} colIndex The index of the column
46864      * @param {String} name The css property name
46865      * @param {String} value The css value
46866      */
46867     setCSSStyle : function(colIndex, name, value){
46868         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
46869         Roo.util.CSS.updateRule(selector, name, value);
46870     },
46871     
46872     generateRules : function(cm){
46873         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
46874         Roo.util.CSS.removeStyleSheet(rulesId);
46875         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
46876             var cid = cm.getColumnId(i);
46877             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
46878                          this.tdSelector, cid, " {\n}\n",
46879                          this.hdSelector, cid, " {\n}\n",
46880                          this.splitSelector, cid, " {\n}\n");
46881         }
46882         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
46883     }
46884 });/*
46885  * Based on:
46886  * Ext JS Library 1.1.1
46887  * Copyright(c) 2006-2007, Ext JS, LLC.
46888  *
46889  * Originally Released Under LGPL - original licence link has changed is not relivant.
46890  *
46891  * Fork - LGPL
46892  * <script type="text/javascript">
46893  */
46894
46895 // private
46896 // This is a support class used internally by the Grid components
46897 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
46898     this.grid = grid;
46899     this.view = grid.getView();
46900     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46901     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
46902     if(hd2){
46903         this.setHandleElId(Roo.id(hd));
46904         this.setOuterHandleElId(Roo.id(hd2));
46905     }
46906     this.scroll = false;
46907 };
46908 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
46909     maxDragWidth: 120,
46910     getDragData : function(e){
46911         var t = Roo.lib.Event.getTarget(e);
46912         var h = this.view.findHeaderCell(t);
46913         if(h){
46914             return {ddel: h.firstChild, header:h};
46915         }
46916         return false;
46917     },
46918
46919     onInitDrag : function(e){
46920         this.view.headersDisabled = true;
46921         var clone = this.dragData.ddel.cloneNode(true);
46922         clone.id = Roo.id();
46923         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
46924         this.proxy.update(clone);
46925         return true;
46926     },
46927
46928     afterValidDrop : function(){
46929         var v = this.view;
46930         setTimeout(function(){
46931             v.headersDisabled = false;
46932         }, 50);
46933     },
46934
46935     afterInvalidDrop : function(){
46936         var v = this.view;
46937         setTimeout(function(){
46938             v.headersDisabled = false;
46939         }, 50);
46940     }
46941 });
46942 /*
46943  * Based on:
46944  * Ext JS Library 1.1.1
46945  * Copyright(c) 2006-2007, Ext JS, LLC.
46946  *
46947  * Originally Released Under LGPL - original licence link has changed is not relivant.
46948  *
46949  * Fork - LGPL
46950  * <script type="text/javascript">
46951  */
46952 // private
46953 // This is a support class used internally by the Grid components
46954 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
46955     this.grid = grid;
46956     this.view = grid.getView();
46957     // split the proxies so they don't interfere with mouse events
46958     this.proxyTop = Roo.DomHelper.append(document.body, {
46959         cls:"col-move-top", html:"&#160;"
46960     }, true);
46961     this.proxyBottom = Roo.DomHelper.append(document.body, {
46962         cls:"col-move-bottom", html:"&#160;"
46963     }, true);
46964     this.proxyTop.hide = this.proxyBottom.hide = function(){
46965         this.setLeftTop(-100,-100);
46966         this.setStyle("visibility", "hidden");
46967     };
46968     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
46969     // temporarily disabled
46970     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
46971     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
46972 };
46973 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
46974     proxyOffsets : [-4, -9],
46975     fly: Roo.Element.fly,
46976
46977     getTargetFromEvent : function(e){
46978         var t = Roo.lib.Event.getTarget(e);
46979         var cindex = this.view.findCellIndex(t);
46980         if(cindex !== false){
46981             return this.view.getHeaderCell(cindex);
46982         }
46983     },
46984
46985     nextVisible : function(h){
46986         var v = this.view, cm = this.grid.colModel;
46987         h = h.nextSibling;
46988         while(h){
46989             if(!cm.isHidden(v.getCellIndex(h))){
46990                 return h;
46991             }
46992             h = h.nextSibling;
46993         }
46994         return null;
46995     },
46996
46997     prevVisible : function(h){
46998         var v = this.view, cm = this.grid.colModel;
46999         h = h.prevSibling;
47000         while(h){
47001             if(!cm.isHidden(v.getCellIndex(h))){
47002                 return h;
47003             }
47004             h = h.prevSibling;
47005         }
47006         return null;
47007     },
47008
47009     positionIndicator : function(h, n, e){
47010         var x = Roo.lib.Event.getPageX(e);
47011         var r = Roo.lib.Dom.getRegion(n.firstChild);
47012         var px, pt, py = r.top + this.proxyOffsets[1];
47013         if((r.right - x) <= (r.right-r.left)/2){
47014             px = r.right+this.view.borderWidth;
47015             pt = "after";
47016         }else{
47017             px = r.left;
47018             pt = "before";
47019         }
47020         var oldIndex = this.view.getCellIndex(h);
47021         var newIndex = this.view.getCellIndex(n);
47022
47023         if(this.grid.colModel.isFixed(newIndex)){
47024             return false;
47025         }
47026
47027         var locked = this.grid.colModel.isLocked(newIndex);
47028
47029         if(pt == "after"){
47030             newIndex++;
47031         }
47032         if(oldIndex < newIndex){
47033             newIndex--;
47034         }
47035         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
47036             return false;
47037         }
47038         px +=  this.proxyOffsets[0];
47039         this.proxyTop.setLeftTop(px, py);
47040         this.proxyTop.show();
47041         if(!this.bottomOffset){
47042             this.bottomOffset = this.view.mainHd.getHeight();
47043         }
47044         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
47045         this.proxyBottom.show();
47046         return pt;
47047     },
47048
47049     onNodeEnter : function(n, dd, e, data){
47050         if(data.header != n){
47051             this.positionIndicator(data.header, n, e);
47052         }
47053     },
47054
47055     onNodeOver : function(n, dd, e, data){
47056         var result = false;
47057         if(data.header != n){
47058             result = this.positionIndicator(data.header, n, e);
47059         }
47060         if(!result){
47061             this.proxyTop.hide();
47062             this.proxyBottom.hide();
47063         }
47064         return result ? this.dropAllowed : this.dropNotAllowed;
47065     },
47066
47067     onNodeOut : function(n, dd, e, data){
47068         this.proxyTop.hide();
47069         this.proxyBottom.hide();
47070     },
47071
47072     onNodeDrop : function(n, dd, e, data){
47073         var h = data.header;
47074         if(h != n){
47075             var cm = this.grid.colModel;
47076             var x = Roo.lib.Event.getPageX(e);
47077             var r = Roo.lib.Dom.getRegion(n.firstChild);
47078             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
47079             var oldIndex = this.view.getCellIndex(h);
47080             var newIndex = this.view.getCellIndex(n);
47081             var locked = cm.isLocked(newIndex);
47082             if(pt == "after"){
47083                 newIndex++;
47084             }
47085             if(oldIndex < newIndex){
47086                 newIndex--;
47087             }
47088             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
47089                 return false;
47090             }
47091             cm.setLocked(oldIndex, locked, true);
47092             cm.moveColumn(oldIndex, newIndex);
47093             this.grid.fireEvent("columnmove", oldIndex, newIndex);
47094             return true;
47095         }
47096         return false;
47097     }
47098 });
47099 /*
47100  * Based on:
47101  * Ext JS Library 1.1.1
47102  * Copyright(c) 2006-2007, Ext JS, LLC.
47103  *
47104  * Originally Released Under LGPL - original licence link has changed is not relivant.
47105  *
47106  * Fork - LGPL
47107  * <script type="text/javascript">
47108  */
47109   
47110 /**
47111  * @class Roo.grid.GridView
47112  * @extends Roo.util.Observable
47113  *
47114  * @constructor
47115  * @param {Object} config
47116  */
47117 Roo.grid.GridView = function(config){
47118     Roo.grid.GridView.superclass.constructor.call(this);
47119     this.el = null;
47120
47121     Roo.apply(this, config);
47122 };
47123
47124 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
47125
47126     /**
47127      * Override this function to apply custom css classes to rows during rendering
47128      * @param {Record} record The record
47129      * @param {Number} index
47130      * @method getRowClass
47131      */
47132     rowClass : "x-grid-row",
47133
47134     cellClass : "x-grid-col",
47135
47136     tdClass : "x-grid-td",
47137
47138     hdClass : "x-grid-hd",
47139
47140     splitClass : "x-grid-split",
47141
47142     sortClasses : ["sort-asc", "sort-desc"],
47143
47144     enableMoveAnim : false,
47145
47146     hlColor: "C3DAF9",
47147
47148     dh : Roo.DomHelper,
47149
47150     fly : Roo.Element.fly,
47151
47152     css : Roo.util.CSS,
47153
47154     borderWidth: 1,
47155
47156     splitOffset: 3,
47157
47158     scrollIncrement : 22,
47159
47160     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
47161
47162     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
47163
47164     bind : function(ds, cm){
47165         if(this.ds){
47166             this.ds.un("load", this.onLoad, this);
47167             this.ds.un("datachanged", this.onDataChange, this);
47168             this.ds.un("add", this.onAdd, this);
47169             this.ds.un("remove", this.onRemove, this);
47170             this.ds.un("update", this.onUpdate, this);
47171             this.ds.un("clear", this.onClear, this);
47172         }
47173         if(ds){
47174             ds.on("load", this.onLoad, this);
47175             ds.on("datachanged", this.onDataChange, this);
47176             ds.on("add", this.onAdd, this);
47177             ds.on("remove", this.onRemove, this);
47178             ds.on("update", this.onUpdate, this);
47179             ds.on("clear", this.onClear, this);
47180         }
47181         this.ds = ds;
47182
47183         if(this.cm){
47184             this.cm.un("widthchange", this.onColWidthChange, this);
47185             this.cm.un("headerchange", this.onHeaderChange, this);
47186             this.cm.un("hiddenchange", this.onHiddenChange, this);
47187             this.cm.un("columnmoved", this.onColumnMove, this);
47188             this.cm.un("columnlockchange", this.onColumnLock, this);
47189         }
47190         if(cm){
47191             this.generateRules(cm);
47192             cm.on("widthchange", this.onColWidthChange, this);
47193             cm.on("headerchange", this.onHeaderChange, this);
47194             cm.on("hiddenchange", this.onHiddenChange, this);
47195             cm.on("columnmoved", this.onColumnMove, this);
47196             cm.on("columnlockchange", this.onColumnLock, this);
47197         }
47198         this.cm = cm;
47199     },
47200
47201     init: function(grid){
47202                 Roo.grid.GridView.superclass.init.call(this, grid);
47203
47204                 this.bind(grid.dataSource, grid.colModel);
47205
47206             grid.on("headerclick", this.handleHeaderClick, this);
47207
47208         if(grid.trackMouseOver){
47209             grid.on("mouseover", this.onRowOver, this);
47210                 grid.on("mouseout", this.onRowOut, this);
47211             }
47212             grid.cancelTextSelection = function(){};
47213                 this.gridId = grid.id;
47214
47215                 var tpls = this.templates || {};
47216
47217                 if(!tpls.master){
47218                     tpls.master = new Roo.Template(
47219                        '<div class="x-grid" hidefocus="true">',
47220                           '<div class="x-grid-topbar"></div>',
47221                           '<div class="x-grid-scroller"><div></div></div>',
47222                           '<div class="x-grid-locked">',
47223                               '<div class="x-grid-header">{lockedHeader}</div>',
47224                               '<div class="x-grid-body">{lockedBody}</div>',
47225                           "</div>",
47226                           '<div class="x-grid-viewport">',
47227                               '<div class="x-grid-header">{header}</div>',
47228                               '<div class="x-grid-body">{body}</div>',
47229                           "</div>",
47230                           '<div class="x-grid-bottombar"></div>',
47231                           '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
47232                           '<div class="x-grid-resize-proxy">&#160;</div>',
47233                        "</div>"
47234                     );
47235                     tpls.master.disableformats = true;
47236                 }
47237
47238                 if(!tpls.header){
47239                     tpls.header = new Roo.Template(
47240                        '<table border="0" cellspacing="0" cellpadding="0">',
47241                        '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
47242                        "</table>{splits}"
47243                     );
47244                     tpls.header.disableformats = true;
47245                 }
47246                 tpls.header.compile();
47247
47248                 if(!tpls.hcell){
47249                     tpls.hcell = new Roo.Template(
47250                         '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
47251                         '<div class="x-grid-hd-text" unselectable="on">{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
47252                         "</div></td>"
47253                      );
47254                      tpls.hcell.disableFormats = true;
47255                 }
47256                 tpls.hcell.compile();
47257
47258                 if(!tpls.hsplit){
47259                     tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style}" unselectable="on">&#160;</div>');
47260                     tpls.hsplit.disableFormats = true;
47261                 }
47262                 tpls.hsplit.compile();
47263
47264                 if(!tpls.body){
47265                     tpls.body = new Roo.Template(
47266                        '<table border="0" cellspacing="0" cellpadding="0">',
47267                        "<tbody>{rows}</tbody>",
47268                        "</table>"
47269                     );
47270                     tpls.body.disableFormats = true;
47271                 }
47272                 tpls.body.compile();
47273
47274                 if(!tpls.row){
47275                     tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
47276                     tpls.row.disableFormats = true;
47277                 }
47278                 tpls.row.compile();
47279
47280                 if(!tpls.cell){
47281                     tpls.cell = new Roo.Template(
47282                         '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
47283                         '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text" unselectable="on" {attr}>{value}</div></div>',
47284                         "</td>"
47285                     );
47286             tpls.cell.disableFormats = true;
47287         }
47288                 tpls.cell.compile();
47289
47290                 this.templates = tpls;
47291         },
47292
47293         // remap these for backwards compat
47294     onColWidthChange : function(){
47295         this.updateColumns.apply(this, arguments);
47296     },
47297     onHeaderChange : function(){
47298         this.updateHeaders.apply(this, arguments);
47299     }, 
47300     onHiddenChange : function(){
47301         this.handleHiddenChange.apply(this, arguments);
47302     },
47303     onColumnMove : function(){
47304         this.handleColumnMove.apply(this, arguments);
47305     },
47306     onColumnLock : function(){
47307         this.handleLockChange.apply(this, arguments);
47308     },
47309
47310     onDataChange : function(){
47311         this.refresh();
47312         this.updateHeaderSortState();
47313     },
47314
47315         onClear : function(){
47316         this.refresh();
47317     },
47318
47319         onUpdate : function(ds, record){
47320         this.refreshRow(record);
47321     },
47322
47323     refreshRow : function(record){
47324         var ds = this.ds, index;
47325         if(typeof record == 'number'){
47326             index = record;
47327             record = ds.getAt(index);
47328         }else{
47329             index = ds.indexOf(record);
47330         }
47331         this.insertRows(ds, index, index, true);
47332         this.onRemove(ds, record, index+1, true);
47333         this.syncRowHeights(index, index);
47334         this.layout();
47335         this.fireEvent("rowupdated", this, index, record);
47336     },
47337
47338     onAdd : function(ds, records, index){
47339         this.insertRows(ds, index, index + (records.length-1));
47340     },
47341
47342     onRemove : function(ds, record, index, isUpdate){
47343         if(isUpdate !== true){
47344             this.fireEvent("beforerowremoved", this, index, record);
47345         }
47346         var bt = this.getBodyTable(), lt = this.getLockedTable();
47347         if(bt.rows[index]){
47348             bt.firstChild.removeChild(bt.rows[index]);
47349         }
47350         if(lt.rows[index]){
47351             lt.firstChild.removeChild(lt.rows[index]);
47352         }
47353         if(isUpdate !== true){
47354             this.stripeRows(index);
47355             this.syncRowHeights(index, index);
47356             this.layout();
47357             this.fireEvent("rowremoved", this, index, record);
47358         }
47359     },
47360
47361     onLoad : function(){
47362         this.scrollToTop();
47363     },
47364
47365     /**
47366      * Scrolls the grid to the top
47367      */
47368     scrollToTop : function(){
47369         if(this.scroller){
47370             this.scroller.dom.scrollTop = 0;
47371             this.syncScroll();
47372         }
47373     },
47374
47375     /**
47376      * Gets a panel in the header of the grid that can be used for toolbars etc.
47377      * After modifying the contents of this panel a call to grid.autoSize() may be
47378      * required to register any changes in size.
47379      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
47380      * @return Roo.Element
47381      */
47382     getHeaderPanel : function(doShow){
47383         if(doShow){
47384             this.headerPanel.show();
47385         }
47386         return this.headerPanel;
47387         },
47388
47389         /**
47390      * Gets a panel in the footer of the grid that can be used for toolbars etc.
47391      * After modifying the contents of this panel a call to grid.autoSize() may be
47392      * required to register any changes in size.
47393      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
47394      * @return Roo.Element
47395      */
47396     getFooterPanel : function(doShow){
47397         if(doShow){
47398             this.footerPanel.show();
47399         }
47400         return this.footerPanel;
47401         },
47402
47403         initElements : function(){
47404             var E = Roo.Element;
47405             var el = this.grid.getGridEl().dom.firstChild;
47406             var cs = el.childNodes;
47407
47408             this.el = new E(el);
47409             this.headerPanel = new E(el.firstChild);
47410             this.headerPanel.enableDisplayMode("block");
47411
47412         this.scroller = new E(cs[1]);
47413             this.scrollSizer = new E(this.scroller.dom.firstChild);
47414
47415             this.lockedWrap = new E(cs[2]);
47416             this.lockedHd = new E(this.lockedWrap.dom.firstChild);
47417             this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
47418
47419             this.mainWrap = new E(cs[3]);
47420             this.mainHd = new E(this.mainWrap.dom.firstChild);
47421             this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
47422
47423             this.footerPanel = new E(cs[4]);
47424             this.footerPanel.enableDisplayMode("block");
47425
47426         this.focusEl = new E(cs[5]);
47427         this.focusEl.swallowEvent("click", true);
47428         this.resizeProxy = new E(cs[6]);
47429
47430             this.headerSelector = String.format(
47431                '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
47432                this.lockedHd.id, this.mainHd.id
47433             );
47434
47435             this.splitterSelector = String.format(
47436                '#{0} div.x-grid-split, #{1} div.x-grid-split',
47437                this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
47438             );
47439     },
47440     idToCssName : function(s)
47441     {
47442         return s.replace(/[^a-z0-9]+/ig, '-');
47443     },
47444
47445         getHeaderCell : function(index){
47446             return Roo.DomQuery.select(this.headerSelector)[index];
47447         },
47448
47449         getHeaderCellMeasure : function(index){
47450             return this.getHeaderCell(index).firstChild;
47451         },
47452
47453         getHeaderCellText : function(index){
47454             return this.getHeaderCell(index).firstChild.firstChild;
47455         },
47456
47457         getLockedTable : function(){
47458             return this.lockedBody.dom.firstChild;
47459         },
47460
47461         getBodyTable : function(){
47462             return this.mainBody.dom.firstChild;
47463         },
47464
47465         getLockedRow : function(index){
47466             return this.getLockedTable().rows[index];
47467         },
47468
47469         getRow : function(index){
47470             return this.getBodyTable().rows[index];
47471         },
47472
47473         getRowComposite : function(index){
47474             if(!this.rowEl){
47475                 this.rowEl = new Roo.CompositeElementLite();
47476             }
47477         var els = [], lrow, mrow;
47478         if(lrow = this.getLockedRow(index)){
47479             els.push(lrow);
47480         }
47481         if(mrow = this.getRow(index)){
47482             els.push(mrow);
47483         }
47484         this.rowEl.elements = els;
47485             return this.rowEl;
47486         },
47487
47488         getCell : function(rowIndex, colIndex){
47489             var locked = this.cm.getLockedCount();
47490             var source;
47491             if(colIndex < locked){
47492                 source = this.lockedBody.dom.firstChild;
47493             }else{
47494                 source = this.mainBody.dom.firstChild;
47495                 colIndex -= locked;
47496             }
47497         return source.rows[rowIndex].childNodes[colIndex];
47498         },
47499
47500         getCellText : function(rowIndex, colIndex){
47501             return this.getCell(rowIndex, colIndex).firstChild.firstChild;
47502         },
47503
47504         getCellBox : function(cell){
47505             var b = this.fly(cell).getBox();
47506         if(Roo.isOpera){ // opera fails to report the Y
47507             b.y = cell.offsetTop + this.mainBody.getY();
47508         }
47509         return b;
47510     },
47511
47512     getCellIndex : function(cell){
47513         var id = String(cell.className).match(this.cellRE);
47514         if(id){
47515             return parseInt(id[1], 10);
47516         }
47517         return 0;
47518     },
47519
47520     findHeaderIndex : function(n){
47521         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47522         return r ? this.getCellIndex(r) : false;
47523     },
47524
47525     findHeaderCell : function(n){
47526         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
47527         return r ? r : false;
47528     },
47529
47530     findRowIndex : function(n){
47531         if(!n){
47532             return false;
47533         }
47534         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
47535         return r ? r.rowIndex : false;
47536     },
47537
47538     findCellIndex : function(node){
47539         var stop = this.el.dom;
47540         while(node && node != stop){
47541             if(this.findRE.test(node.className)){
47542                 return this.getCellIndex(node);
47543             }
47544             node = node.parentNode;
47545         }
47546         return false;
47547     },
47548
47549     getColumnId : function(index){
47550             return this.cm.getColumnId(index);
47551         },
47552
47553         getSplitters : function(){
47554             if(this.splitterSelector){
47555                return Roo.DomQuery.select(this.splitterSelector);
47556             }else{
47557                 return null;
47558             }
47559         },
47560
47561         getSplitter : function(index){
47562             return this.getSplitters()[index];
47563         },
47564
47565     onRowOver : function(e, t){
47566         var row;
47567         if((row = this.findRowIndex(t)) !== false){
47568             this.getRowComposite(row).addClass("x-grid-row-over");
47569         }
47570     },
47571
47572     onRowOut : function(e, t){
47573         var row;
47574         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
47575             this.getRowComposite(row).removeClass("x-grid-row-over");
47576         }
47577     },
47578
47579     renderHeaders : function(){
47580             var cm = this.cm;
47581         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
47582         var cb = [], lb = [], sb = [], lsb = [], p = {};
47583         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47584             p.cellId = "x-grid-hd-0-" + i;
47585             p.splitId = "x-grid-csplit-0-" + i;
47586             p.id = cm.getColumnId(i);
47587             p.title = cm.getColumnTooltip(i) || "";
47588             p.value = cm.getColumnHeader(i) || "";
47589             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
47590             if(!cm.isLocked(i)){
47591                 cb[cb.length] = ct.apply(p);
47592                 sb[sb.length] = st.apply(p);
47593             }else{
47594                 lb[lb.length] = ct.apply(p);
47595                 lsb[lsb.length] = st.apply(p);
47596             }
47597         }
47598         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
47599                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
47600         },
47601
47602         updateHeaders : function(){
47603         var html = this.renderHeaders();
47604         this.lockedHd.update(html[0]);
47605         this.mainHd.update(html[1]);
47606     },
47607
47608     /**
47609      * Focuses the specified row.
47610      * @param {Number} row The row index
47611      */
47612     focusRow : function(row){
47613         var x = this.scroller.dom.scrollLeft;
47614         this.focusCell(row, 0, false);
47615         this.scroller.dom.scrollLeft = x;
47616     },
47617
47618     /**
47619      * Focuses the specified cell.
47620      * @param {Number} row The row index
47621      * @param {Number} col The column index
47622      * @param {Boolean} hscroll false to disable horizontal scrolling
47623      */
47624     focusCell : function(row, col, hscroll){
47625         var el = this.ensureVisible(row, col, hscroll);
47626         this.focusEl.alignTo(el, "tl-tl");
47627         if(Roo.isGecko){
47628             this.focusEl.focus();
47629         }else{
47630             this.focusEl.focus.defer(1, this.focusEl);
47631         }
47632     },
47633
47634     /**
47635      * Scrolls the specified cell into view
47636      * @param {Number} row The row index
47637      * @param {Number} col The column index
47638      * @param {Boolean} hscroll false to disable horizontal scrolling
47639      */
47640     ensureVisible : function(row, col, hscroll){
47641         if(typeof row != "number"){
47642             row = row.rowIndex;
47643         }
47644         if(row < 0 && row >= this.ds.getCount()){
47645             return;
47646         }
47647         col = (col !== undefined ? col : 0);
47648         var cm = this.grid.colModel;
47649         while(cm.isHidden(col)){
47650             col++;
47651         }
47652
47653         var el = this.getCell(row, col);
47654         if(!el){
47655             return;
47656         }
47657         var c = this.scroller.dom;
47658
47659         var ctop = parseInt(el.offsetTop, 10);
47660         var cleft = parseInt(el.offsetLeft, 10);
47661         var cbot = ctop + el.offsetHeight;
47662         var cright = cleft + el.offsetWidth;
47663
47664         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
47665         var stop = parseInt(c.scrollTop, 10);
47666         var sleft = parseInt(c.scrollLeft, 10);
47667         var sbot = stop + ch;
47668         var sright = sleft + c.clientWidth;
47669
47670         if(ctop < stop){
47671                 c.scrollTop = ctop;
47672         }else if(cbot > sbot){
47673             c.scrollTop = cbot-ch;
47674         }
47675
47676         if(hscroll !== false){
47677             if(cleft < sleft){
47678                 c.scrollLeft = cleft;
47679             }else if(cright > sright){
47680                 c.scrollLeft = cright-c.clientWidth;
47681             }
47682         }
47683         return el;
47684     },
47685
47686     updateColumns : function(){
47687         this.grid.stopEditing();
47688         var cm = this.grid.colModel, colIds = this.getColumnIds();
47689         //var totalWidth = cm.getTotalWidth();
47690         var pos = 0;
47691         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47692             //if(cm.isHidden(i)) continue;
47693             var w = cm.getColumnWidth(i);
47694             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47695             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
47696         }
47697         this.updateSplitters();
47698     },
47699
47700     generateRules : function(cm){
47701         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
47702         Roo.util.CSS.removeStyleSheet(rulesId);
47703         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47704             var cid = cm.getColumnId(i);
47705             var align = '';
47706             if(cm.config[i].align){
47707                 align = 'text-align:'+cm.config[i].align+';';
47708             }
47709             var hidden = '';
47710             if(cm.isHidden(i)){
47711                 hidden = 'display:none;';
47712             }
47713             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
47714             ruleBuf.push(
47715                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
47716                     this.hdSelector, cid, " {\n", align, width, "}\n",
47717                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
47718                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
47719         }
47720         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
47721     },
47722
47723     updateSplitters : function(){
47724         var cm = this.cm, s = this.getSplitters();
47725         if(s){ // splitters not created yet
47726             var pos = 0, locked = true;
47727             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
47728                 if(cm.isHidden(i)) continue;
47729                 var w = cm.getColumnWidth(i);
47730                 if(!cm.isLocked(i) && locked){
47731                     pos = 0;
47732                     locked = false;
47733                 }
47734                 pos += w;
47735                 s[i].style.left = (pos-this.splitOffset) + "px";
47736             }
47737         }
47738     },
47739
47740     handleHiddenChange : function(colModel, colIndex, hidden){
47741         if(hidden){
47742             this.hideColumn(colIndex);
47743         }else{
47744             this.unhideColumn(colIndex);
47745         }
47746     },
47747
47748     hideColumn : function(colIndex){
47749         var cid = this.getColumnId(colIndex);
47750         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
47751         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
47752         if(Roo.isSafari){
47753             this.updateHeaders();
47754         }
47755         this.updateSplitters();
47756         this.layout();
47757     },
47758
47759     unhideColumn : function(colIndex){
47760         var cid = this.getColumnId(colIndex);
47761         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
47762         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
47763
47764         if(Roo.isSafari){
47765             this.updateHeaders();
47766         }
47767         this.updateSplitters();
47768         this.layout();
47769     },
47770
47771     insertRows : function(dm, firstRow, lastRow, isUpdate){
47772         if(firstRow == 0 && lastRow == dm.getCount()-1){
47773             this.refresh();
47774         }else{
47775             if(!isUpdate){
47776                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
47777             }
47778             var s = this.getScrollState();
47779             var markup = this.renderRows(firstRow, lastRow);
47780             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
47781             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
47782             this.restoreScroll(s);
47783             if(!isUpdate){
47784                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
47785                 this.syncRowHeights(firstRow, lastRow);
47786                 this.stripeRows(firstRow);
47787                 this.layout();
47788             }
47789         }
47790     },
47791
47792     bufferRows : function(markup, target, index){
47793         var before = null, trows = target.rows, tbody = target.tBodies[0];
47794         if(index < trows.length){
47795             before = trows[index];
47796         }
47797         var b = document.createElement("div");
47798         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
47799         var rows = b.firstChild.rows;
47800         for(var i = 0, len = rows.length; i < len; i++){
47801             if(before){
47802                 tbody.insertBefore(rows[0], before);
47803             }else{
47804                 tbody.appendChild(rows[0]);
47805             }
47806         }
47807         b.innerHTML = "";
47808         b = null;
47809     },
47810
47811     deleteRows : function(dm, firstRow, lastRow){
47812         if(dm.getRowCount()<1){
47813             this.fireEvent("beforerefresh", this);
47814             this.mainBody.update("");
47815             this.lockedBody.update("");
47816             this.fireEvent("refresh", this);
47817         }else{
47818             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
47819             var bt = this.getBodyTable();
47820             var tbody = bt.firstChild;
47821             var rows = bt.rows;
47822             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
47823                 tbody.removeChild(rows[firstRow]);
47824             }
47825             this.stripeRows(firstRow);
47826             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
47827         }
47828     },
47829
47830     updateRows : function(dataSource, firstRow, lastRow){
47831         var s = this.getScrollState();
47832         this.refresh();
47833         this.restoreScroll(s);
47834     },
47835
47836     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
47837         if(!noRefresh){
47838            this.refresh();
47839         }
47840         this.updateHeaderSortState();
47841     },
47842
47843     getScrollState : function(){
47844         var sb = this.scroller.dom;
47845         return {left: sb.scrollLeft, top: sb.scrollTop};
47846     },
47847
47848     stripeRows : function(startRow){
47849         if(!this.grid.stripeRows || this.ds.getCount() < 1){
47850             return;
47851         }
47852         startRow = startRow || 0;
47853         var rows = this.getBodyTable().rows;
47854         var lrows = this.getLockedTable().rows;
47855         var cls = ' x-grid-row-alt ';
47856         for(var i = startRow, len = rows.length; i < len; i++){
47857             var row = rows[i], lrow = lrows[i];
47858             var isAlt = ((i+1) % 2 == 0);
47859             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
47860             if(isAlt == hasAlt){
47861                 continue;
47862             }
47863             if(isAlt){
47864                 row.className += " x-grid-row-alt";
47865             }else{
47866                 row.className = row.className.replace("x-grid-row-alt", "");
47867             }
47868             if(lrow){
47869                 lrow.className = row.className;
47870             }
47871         }
47872     },
47873
47874     restoreScroll : function(state){
47875         var sb = this.scroller.dom;
47876         sb.scrollLeft = state.left;
47877         sb.scrollTop = state.top;
47878         this.syncScroll();
47879     },
47880
47881     syncScroll : function(){
47882         var sb = this.scroller.dom;
47883         var sh = this.mainHd.dom;
47884         var bs = this.mainBody.dom;
47885         var lv = this.lockedBody.dom;
47886         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
47887         lv.scrollTop = bs.scrollTop = sb.scrollTop;
47888     },
47889
47890     handleScroll : function(e){
47891         this.syncScroll();
47892         var sb = this.scroller.dom;
47893         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
47894         e.stopEvent();
47895     },
47896
47897     handleWheel : function(e){
47898         var d = e.getWheelDelta();
47899         this.scroller.dom.scrollTop -= d*22;
47900         // set this here to prevent jumpy scrolling on large tables
47901         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
47902         e.stopEvent();
47903     },
47904
47905     renderRows : function(startRow, endRow){
47906         // pull in all the crap needed to render rows
47907         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
47908         var colCount = cm.getColumnCount();
47909
47910         if(ds.getCount() < 1){
47911             return ["", ""];
47912         }
47913
47914         // build a map for all the columns
47915         var cs = [];
47916         for(var i = 0; i < colCount; i++){
47917             var name = cm.getDataIndex(i);
47918             cs[i] = {
47919                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
47920                 renderer : cm.getRenderer(i),
47921                 id : cm.getColumnId(i),
47922                 locked : cm.isLocked(i)
47923             };
47924         }
47925
47926         startRow = startRow || 0;
47927         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
47928
47929         // records to render
47930         var rs = ds.getRange(startRow, endRow);
47931
47932         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
47933     },
47934
47935     // As much as I hate to duplicate code, this was branched because FireFox really hates
47936     // [].join("") on strings. The performance difference was substantial enough to
47937     // branch this function
47938     doRender : Roo.isGecko ?
47939             function(cs, rs, ds, startRow, colCount, stripe){
47940                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47941                 // buffers
47942                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47943                 for(var j = 0, len = rs.length; j < len; j++){
47944                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
47945                     for(var i = 0; i < colCount; i++){
47946                         c = cs[i];
47947                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47948                         p.id = c.id;
47949                         p.css = p.attr = "";
47950                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47951                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47952                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47953                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47954                         }
47955                         var markup = ct.apply(p);
47956                         if(!c.locked){
47957                             cb+= markup;
47958                         }else{
47959                             lcb+= markup;
47960                         }
47961                     }
47962                     var alt = [];
47963                     if(stripe && ((rowIndex+1) % 2 == 0)){
47964                         alt[0] = "x-grid-row-alt";
47965                     }
47966                     if(r.dirty){
47967                         alt[1] = " x-grid-dirty-row";
47968                     }
47969                     rp.cells = lcb;
47970                     if(this.getRowClass){
47971                         alt[2] = this.getRowClass(r, rowIndex);
47972                     }
47973                     rp.alt = alt.join(" ");
47974                     lbuf+= rt.apply(rp);
47975                     rp.cells = cb;
47976                     buf+=  rt.apply(rp);
47977                 }
47978                 return [lbuf, buf];
47979             } :
47980             function(cs, rs, ds, startRow, colCount, stripe){
47981                 var ts = this.templates, ct = ts.cell, rt = ts.row;
47982                 // buffers
47983                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
47984                 for(var j = 0, len = rs.length; j < len; j++){
47985                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
47986                     for(var i = 0; i < colCount; i++){
47987                         c = cs[i];
47988                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
47989                         p.id = c.id;
47990                         p.css = p.attr = "";
47991                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
47992                         if(p.value == undefined || p.value === "") p.value = "&#160;";
47993                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
47994                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
47995                         }
47996                         var markup = ct.apply(p);
47997                         if(!c.locked){
47998                             cb[cb.length] = markup;
47999                         }else{
48000                             lcb[lcb.length] = markup;
48001                         }
48002                     }
48003                     var alt = [];
48004                     if(stripe && ((rowIndex+1) % 2 == 0)){
48005                         alt[0] = "x-grid-row-alt";
48006                     }
48007                     if(r.dirty){
48008                         alt[1] = " x-grid-dirty-row";
48009                     }
48010                     rp.cells = lcb;
48011                     if(this.getRowClass){
48012                         alt[2] = this.getRowClass(r, rowIndex);
48013                     }
48014                     rp.alt = alt.join(" ");
48015                     rp.cells = lcb.join("");
48016                     lbuf[lbuf.length] = rt.apply(rp);
48017                     rp.cells = cb.join("");
48018                     buf[buf.length] =  rt.apply(rp);
48019                 }
48020                 return [lbuf.join(""), buf.join("")];
48021             },
48022
48023     renderBody : function(){
48024         var markup = this.renderRows();
48025         var bt = this.templates.body;
48026         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
48027     },
48028
48029     /**
48030      * Refreshes the grid
48031      * @param {Boolean} headersToo
48032      */
48033     refresh : function(headersToo){
48034         this.fireEvent("beforerefresh", this);
48035         this.grid.stopEditing();
48036         var result = this.renderBody();
48037         this.lockedBody.update(result[0]);
48038         this.mainBody.update(result[1]);
48039         if(headersToo === true){
48040             this.updateHeaders();
48041             this.updateColumns();
48042             this.updateSplitters();
48043             this.updateHeaderSortState();
48044         }
48045         this.syncRowHeights();
48046         this.layout();
48047         this.fireEvent("refresh", this);
48048     },
48049
48050     handleColumnMove : function(cm, oldIndex, newIndex){
48051         this.indexMap = null;
48052         var s = this.getScrollState();
48053         this.refresh(true);
48054         this.restoreScroll(s);
48055         this.afterMove(newIndex);
48056     },
48057
48058     afterMove : function(colIndex){
48059         if(this.enableMoveAnim && Roo.enableFx){
48060             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
48061         }
48062     },
48063
48064     updateCell : function(dm, rowIndex, dataIndex){
48065         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
48066         if(typeof colIndex == "undefined"){ // not present in grid
48067             return;
48068         }
48069         var cm = this.grid.colModel;
48070         var cell = this.getCell(rowIndex, colIndex);
48071         var cellText = this.getCellText(rowIndex, colIndex);
48072
48073         var p = {
48074             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
48075             id : cm.getColumnId(colIndex),
48076             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
48077         };
48078         var renderer = cm.getRenderer(colIndex);
48079         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
48080         if(typeof val == "undefined" || val === "") val = "&#160;";
48081         cellText.innerHTML = val;
48082         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
48083         this.syncRowHeights(rowIndex, rowIndex);
48084     },
48085
48086     calcColumnWidth : function(colIndex, maxRowsToMeasure){
48087         var maxWidth = 0;
48088         if(this.grid.autoSizeHeaders){
48089             var h = this.getHeaderCellMeasure(colIndex);
48090             maxWidth = Math.max(maxWidth, h.scrollWidth);
48091         }
48092         var tb, index;
48093         if(this.cm.isLocked(colIndex)){
48094             tb = this.getLockedTable();
48095             index = colIndex;
48096         }else{
48097             tb = this.getBodyTable();
48098             index = colIndex - this.cm.getLockedCount();
48099         }
48100         if(tb && tb.rows){
48101             var rows = tb.rows;
48102             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
48103             for(var i = 0; i < stopIndex; i++){
48104                 var cell = rows[i].childNodes[index].firstChild;
48105                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
48106             }
48107         }
48108         return maxWidth + /*margin for error in IE*/ 5;
48109     },
48110     /**
48111      * Autofit a column to its content.
48112      * @param {Number} colIndex
48113      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
48114      */
48115      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
48116          if(this.cm.isHidden(colIndex)){
48117              return; // can't calc a hidden column
48118          }
48119         if(forceMinSize){
48120             var cid = this.cm.getColumnId(colIndex);
48121             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
48122            if(this.grid.autoSizeHeaders){
48123                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
48124            }
48125         }
48126         var newWidth = this.calcColumnWidth(colIndex);
48127         this.cm.setColumnWidth(colIndex,
48128             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
48129         if(!suppressEvent){
48130             this.grid.fireEvent("columnresize", colIndex, newWidth);
48131         }
48132     },
48133
48134     /**
48135      * Autofits all columns to their content and then expands to fit any extra space in the grid
48136      */
48137      autoSizeColumns : function(){
48138         var cm = this.grid.colModel;
48139         var colCount = cm.getColumnCount();
48140         for(var i = 0; i < colCount; i++){
48141             this.autoSizeColumn(i, true, true);
48142         }
48143         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
48144             this.fitColumns();
48145         }else{
48146             this.updateColumns();
48147             this.layout();
48148         }
48149     },
48150
48151     /**
48152      * Autofits all columns to the grid's width proportionate with their current size
48153      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
48154      */
48155     fitColumns : function(reserveScrollSpace){
48156         var cm = this.grid.colModel;
48157         var colCount = cm.getColumnCount();
48158         var cols = [];
48159         var width = 0;
48160         var i, w;
48161         for (i = 0; i < colCount; i++){
48162             if(!cm.isHidden(i) && !cm.isFixed(i)){
48163                 w = cm.getColumnWidth(i);
48164                 cols.push(i);
48165                 cols.push(w);
48166                 width += w;
48167             }
48168         }
48169         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
48170         if(reserveScrollSpace){
48171             avail -= 17;
48172         }
48173         var frac = (avail - cm.getTotalWidth())/width;
48174         while (cols.length){
48175             w = cols.pop();
48176             i = cols.pop();
48177             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
48178         }
48179         this.updateColumns();
48180         this.layout();
48181     },
48182
48183     onRowSelect : function(rowIndex){
48184         var row = this.getRowComposite(rowIndex);
48185         row.addClass("x-grid-row-selected");
48186     },
48187
48188     onRowDeselect : function(rowIndex){
48189         var row = this.getRowComposite(rowIndex);
48190         row.removeClass("x-grid-row-selected");
48191     },
48192
48193     onCellSelect : function(row, col){
48194         var cell = this.getCell(row, col);
48195         if(cell){
48196             Roo.fly(cell).addClass("x-grid-cell-selected");
48197         }
48198     },
48199
48200     onCellDeselect : function(row, col){
48201         var cell = this.getCell(row, col);
48202         if(cell){
48203             Roo.fly(cell).removeClass("x-grid-cell-selected");
48204         }
48205     },
48206
48207     updateHeaderSortState : function(){
48208         var state = this.ds.getSortState();
48209         if(!state){
48210             return;
48211         }
48212         this.sortState = state;
48213         var sortColumn = this.cm.findColumnIndex(state.field);
48214         if(sortColumn != -1){
48215             var sortDir = state.direction;
48216             var sc = this.sortClasses;
48217             var hds = this.el.select(this.headerSelector).removeClass(sc);
48218             hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
48219         }
48220     },
48221
48222     handleHeaderClick : function(g, index){
48223         if(this.headersDisabled){
48224             return;
48225         }
48226         var dm = g.dataSource, cm = g.colModel;
48227             if(!cm.isSortable(index)){
48228             return;
48229         }
48230             g.stopEditing();
48231         dm.sort(cm.getDataIndex(index));
48232     },
48233
48234
48235     destroy : function(){
48236         if(this.colMenu){
48237             this.colMenu.removeAll();
48238             Roo.menu.MenuMgr.unregister(this.colMenu);
48239             this.colMenu.getEl().remove();
48240             delete this.colMenu;
48241         }
48242         if(this.hmenu){
48243             this.hmenu.removeAll();
48244             Roo.menu.MenuMgr.unregister(this.hmenu);
48245             this.hmenu.getEl().remove();
48246             delete this.hmenu;
48247         }
48248         if(this.grid.enableColumnMove){
48249             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48250             if(dds){
48251                 for(var dd in dds){
48252                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
48253                         var elid = dds[dd].dragElId;
48254                         dds[dd].unreg();
48255                         Roo.get(elid).remove();
48256                     } else if(dds[dd].config.isTarget){
48257                         dds[dd].proxyTop.remove();
48258                         dds[dd].proxyBottom.remove();
48259                         dds[dd].unreg();
48260                     }
48261                     if(Roo.dd.DDM.locationCache[dd]){
48262                         delete Roo.dd.DDM.locationCache[dd];
48263                     }
48264                 }
48265                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
48266             }
48267         }
48268         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
48269         this.bind(null, null);
48270         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
48271     },
48272
48273     handleLockChange : function(){
48274         this.refresh(true);
48275     },
48276
48277     onDenyColumnLock : function(){
48278
48279     },
48280
48281     onDenyColumnHide : function(){
48282
48283     },
48284
48285     handleHdMenuClick : function(item){
48286         var index = this.hdCtxIndex;
48287         var cm = this.cm, ds = this.ds;
48288         switch(item.id){
48289             case "asc":
48290                 ds.sort(cm.getDataIndex(index), "ASC");
48291                 break;
48292             case "desc":
48293                 ds.sort(cm.getDataIndex(index), "DESC");
48294                 break;
48295             case "lock":
48296                 var lc = cm.getLockedCount();
48297                 if(cm.getColumnCount(true) <= lc+1){
48298                     this.onDenyColumnLock();
48299                     return;
48300                 }
48301                 if(lc != index){
48302                     cm.setLocked(index, true, true);
48303                     cm.moveColumn(index, lc);
48304                     this.grid.fireEvent("columnmove", index, lc);
48305                 }else{
48306                     cm.setLocked(index, true);
48307                 }
48308             break;
48309             case "unlock":
48310                 var lc = cm.getLockedCount();
48311                 if((lc-1) != index){
48312                     cm.setLocked(index, false, true);
48313                     cm.moveColumn(index, lc-1);
48314                     this.grid.fireEvent("columnmove", index, lc-1);
48315                 }else{
48316                     cm.setLocked(index, false);
48317                 }
48318             break;
48319             default:
48320                 index = cm.getIndexById(item.id.substr(4));
48321                 if(index != -1){
48322                     if(item.checked && cm.getColumnCount(true) <= 1){
48323                         this.onDenyColumnHide();
48324                         return false;
48325                     }
48326                     cm.setHidden(index, item.checked);
48327                 }
48328         }
48329         return true;
48330     },
48331
48332     beforeColMenuShow : function(){
48333         var cm = this.cm,  colCount = cm.getColumnCount();
48334         this.colMenu.removeAll();
48335         for(var i = 0; i < colCount; i++){
48336             this.colMenu.add(new Roo.menu.CheckItem({
48337                 id: "col-"+cm.getColumnId(i),
48338                 text: cm.getColumnHeader(i),
48339                 checked: !cm.isHidden(i),
48340                 hideOnClick:false
48341             }));
48342         }
48343     },
48344
48345     handleHdCtx : function(g, index, e){
48346         e.stopEvent();
48347         var hd = this.getHeaderCell(index);
48348         this.hdCtxIndex = index;
48349         var ms = this.hmenu.items, cm = this.cm;
48350         ms.get("asc").setDisabled(!cm.isSortable(index));
48351         ms.get("desc").setDisabled(!cm.isSortable(index));
48352         if(this.grid.enableColLock !== false){
48353             ms.get("lock").setDisabled(cm.isLocked(index));
48354             ms.get("unlock").setDisabled(!cm.isLocked(index));
48355         }
48356         this.hmenu.show(hd, "tl-bl");
48357     },
48358
48359     handleHdOver : function(e){
48360         var hd = this.findHeaderCell(e.getTarget());
48361         if(hd && !this.headersDisabled){
48362             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
48363                this.fly(hd).addClass("x-grid-hd-over");
48364             }
48365         }
48366     },
48367
48368     handleHdOut : function(e){
48369         var hd = this.findHeaderCell(e.getTarget());
48370         if(hd){
48371             this.fly(hd).removeClass("x-grid-hd-over");
48372         }
48373     },
48374
48375     handleSplitDblClick : function(e, t){
48376         var i = this.getCellIndex(t);
48377         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
48378             this.autoSizeColumn(i, true);
48379             this.layout();
48380         }
48381     },
48382
48383     render : function(){
48384
48385         var cm = this.cm;
48386         var colCount = cm.getColumnCount();
48387
48388         if(this.grid.monitorWindowResize === true){
48389             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48390         }
48391         var header = this.renderHeaders();
48392         var body = this.templates.body.apply({rows:""});
48393         var html = this.templates.master.apply({
48394             lockedBody: body,
48395             body: body,
48396             lockedHeader: header[0],
48397             header: header[1]
48398         });
48399
48400         //this.updateColumns();
48401
48402         this.grid.getGridEl().dom.innerHTML = html;
48403
48404         this.initElements();
48405         
48406         // a kludge to fix the random scolling effect in webkit
48407         this.el.on("scroll", function() {
48408             this.el.dom.scrollTop=0; // hopefully not recursive..
48409         },this);
48410
48411         this.scroller.on("scroll", this.handleScroll, this);
48412         this.lockedBody.on("mousewheel", this.handleWheel, this);
48413         this.mainBody.on("mousewheel", this.handleWheel, this);
48414
48415         this.mainHd.on("mouseover", this.handleHdOver, this);
48416         this.mainHd.on("mouseout", this.handleHdOut, this);
48417         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
48418                 {delegate: "."+this.splitClass});
48419
48420         this.lockedHd.on("mouseover", this.handleHdOver, this);
48421         this.lockedHd.on("mouseout", this.handleHdOut, this);
48422         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
48423                 {delegate: "."+this.splitClass});
48424
48425         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
48426             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48427         }
48428
48429         this.updateSplitters();
48430
48431         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
48432             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48433             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
48434         }
48435
48436         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
48437             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
48438             this.hmenu.add(
48439                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
48440                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
48441             );
48442             if(this.grid.enableColLock !== false){
48443                 this.hmenu.add('-',
48444                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
48445                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
48446                 );
48447             }
48448             if(this.grid.enableColumnHide !== false){
48449
48450                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
48451                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
48452                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
48453
48454                 this.hmenu.add('-',
48455                     {id:"columns", text: this.columnsText, menu: this.colMenu}
48456                 );
48457             }
48458             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
48459
48460             this.grid.on("headercontextmenu", this.handleHdCtx, this);
48461         }
48462
48463         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
48464             this.dd = new Roo.grid.GridDragZone(this.grid, {
48465                 ddGroup : this.grid.ddGroup || 'GridDD'
48466             });
48467         }
48468
48469         /*
48470         for(var i = 0; i < colCount; i++){
48471             if(cm.isHidden(i)){
48472                 this.hideColumn(i);
48473             }
48474             if(cm.config[i].align){
48475                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
48476                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
48477             }
48478         }*/
48479         
48480         this.updateHeaderSortState();
48481
48482         this.beforeInitialResize();
48483         this.layout(true);
48484
48485         // two part rendering gives faster view to the user
48486         this.renderPhase2.defer(1, this);
48487     },
48488
48489     renderPhase2 : function(){
48490         // render the rows now
48491         this.refresh();
48492         if(this.grid.autoSizeColumns){
48493             this.autoSizeColumns();
48494         }
48495     },
48496
48497     beforeInitialResize : function(){
48498
48499     },
48500
48501     onColumnSplitterMoved : function(i, w){
48502         this.userResized = true;
48503         var cm = this.grid.colModel;
48504         cm.setColumnWidth(i, w, true);
48505         var cid = cm.getColumnId(i);
48506         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48507         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
48508         this.updateSplitters();
48509         this.layout();
48510         this.grid.fireEvent("columnresize", i, w);
48511     },
48512
48513     syncRowHeights : function(startIndex, endIndex){
48514         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
48515             startIndex = startIndex || 0;
48516             var mrows = this.getBodyTable().rows;
48517             var lrows = this.getLockedTable().rows;
48518             var len = mrows.length-1;
48519             endIndex = Math.min(endIndex || len, len);
48520             for(var i = startIndex; i <= endIndex; i++){
48521                 var m = mrows[i], l = lrows[i];
48522                 var h = Math.max(m.offsetHeight, l.offsetHeight);
48523                 m.style.height = l.style.height = h + "px";
48524             }
48525         }
48526     },
48527
48528     layout : function(initialRender, is2ndPass){
48529         var g = this.grid;
48530         var auto = g.autoHeight;
48531         var scrollOffset = 16;
48532         var c = g.getGridEl(), cm = this.cm,
48533                 expandCol = g.autoExpandColumn,
48534                 gv = this;
48535         //c.beginMeasure();
48536
48537         if(!c.dom.offsetWidth){ // display:none?
48538             if(initialRender){
48539                 this.lockedWrap.show();
48540                 this.mainWrap.show();
48541             }
48542             return;
48543         }
48544
48545         var hasLock = this.cm.isLocked(0);
48546
48547         var tbh = this.headerPanel.getHeight();
48548         var bbh = this.footerPanel.getHeight();
48549
48550         if(auto){
48551             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
48552             var newHeight = ch + c.getBorderWidth("tb");
48553             if(g.maxHeight){
48554                 newHeight = Math.min(g.maxHeight, newHeight);
48555             }
48556             c.setHeight(newHeight);
48557         }
48558
48559         if(g.autoWidth){
48560             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
48561         }
48562
48563         var s = this.scroller;
48564
48565         var csize = c.getSize(true);
48566
48567         this.el.setSize(csize.width, csize.height);
48568
48569         this.headerPanel.setWidth(csize.width);
48570         this.footerPanel.setWidth(csize.width);
48571
48572         var hdHeight = this.mainHd.getHeight();
48573         var vw = csize.width;
48574         var vh = csize.height - (tbh + bbh);
48575
48576         s.setSize(vw, vh);
48577
48578         var bt = this.getBodyTable();
48579         var ltWidth = hasLock ?
48580                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
48581
48582         var scrollHeight = bt.offsetHeight;
48583         var scrollWidth = ltWidth + bt.offsetWidth;
48584         var vscroll = false, hscroll = false;
48585
48586         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
48587
48588         var lw = this.lockedWrap, mw = this.mainWrap;
48589         var lb = this.lockedBody, mb = this.mainBody;
48590
48591         setTimeout(function(){
48592             var t = s.dom.offsetTop;
48593             var w = s.dom.clientWidth,
48594                 h = s.dom.clientHeight;
48595
48596             lw.setTop(t);
48597             lw.setSize(ltWidth, h);
48598
48599             mw.setLeftTop(ltWidth, t);
48600             mw.setSize(w-ltWidth, h);
48601
48602             lb.setHeight(h-hdHeight);
48603             mb.setHeight(h-hdHeight);
48604
48605             if(is2ndPass !== true && !gv.userResized && expandCol){
48606                 // high speed resize without full column calculation
48607                 
48608                 var ci = cm.getIndexById(expandCol);
48609                 if (ci < 0) {
48610                     ci = cm.findColumnIndex(expandCol);
48611                 }
48612                 ci = Math.max(0, ci); // make sure it's got at least the first col.
48613                 var expandId = cm.getColumnId(ci);
48614                 var  tw = cm.getTotalWidth(false);
48615                 var currentWidth = cm.getColumnWidth(ci);
48616                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
48617                 if(currentWidth != cw){
48618                     cm.setColumnWidth(ci, cw, true);
48619                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48620                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
48621                     gv.updateSplitters();
48622                     gv.layout(false, true);
48623                 }
48624             }
48625
48626             if(initialRender){
48627                 lw.show();
48628                 mw.show();
48629             }
48630             //c.endMeasure();
48631         }, 10);
48632     },
48633
48634     onWindowResize : function(){
48635         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
48636             return;
48637         }
48638         this.layout();
48639     },
48640
48641     appendFooter : function(parentEl){
48642         return null;
48643     },
48644
48645     sortAscText : "Sort Ascending",
48646     sortDescText : "Sort Descending",
48647     lockText : "Lock Column",
48648     unlockText : "Unlock Column",
48649     columnsText : "Columns"
48650 });
48651
48652
48653 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
48654     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
48655     this.proxy.el.addClass('x-grid3-col-dd');
48656 };
48657
48658 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
48659     handleMouseDown : function(e){
48660
48661     },
48662
48663     callHandleMouseDown : function(e){
48664         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
48665     }
48666 });
48667 /*
48668  * Based on:
48669  * Ext JS Library 1.1.1
48670  * Copyright(c) 2006-2007, Ext JS, LLC.
48671  *
48672  * Originally Released Under LGPL - original licence link has changed is not relivant.
48673  *
48674  * Fork - LGPL
48675  * <script type="text/javascript">
48676  */
48677  
48678 // private
48679 // This is a support class used internally by the Grid components
48680 Roo.grid.SplitDragZone = function(grid, hd, hd2){
48681     this.grid = grid;
48682     this.view = grid.getView();
48683     this.proxy = this.view.resizeProxy;
48684     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
48685         "gridSplitters" + this.grid.getGridEl().id, {
48686         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
48687     });
48688     this.setHandleElId(Roo.id(hd));
48689     this.setOuterHandleElId(Roo.id(hd2));
48690     this.scroll = false;
48691 };
48692 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
48693     fly: Roo.Element.fly,
48694
48695     b4StartDrag : function(x, y){
48696         this.view.headersDisabled = true;
48697         this.proxy.setHeight(this.view.mainWrap.getHeight());
48698         var w = this.cm.getColumnWidth(this.cellIndex);
48699         var minw = Math.max(w-this.grid.minColumnWidth, 0);
48700         this.resetConstraints();
48701         this.setXConstraint(minw, 1000);
48702         this.setYConstraint(0, 0);
48703         this.minX = x - minw;
48704         this.maxX = x + 1000;
48705         this.startPos = x;
48706         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
48707     },
48708
48709
48710     handleMouseDown : function(e){
48711         ev = Roo.EventObject.setEvent(e);
48712         var t = this.fly(ev.getTarget());
48713         if(t.hasClass("x-grid-split")){
48714             this.cellIndex = this.view.getCellIndex(t.dom);
48715             this.split = t.dom;
48716             this.cm = this.grid.colModel;
48717             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
48718                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
48719             }
48720         }
48721     },
48722
48723     endDrag : function(e){
48724         this.view.headersDisabled = false;
48725         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
48726         var diff = endX - this.startPos;
48727         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
48728     },
48729
48730     autoOffset : function(){
48731         this.setDelta(0,0);
48732     }
48733 });/*
48734  * Based on:
48735  * Ext JS Library 1.1.1
48736  * Copyright(c) 2006-2007, Ext JS, LLC.
48737  *
48738  * Originally Released Under LGPL - original licence link has changed is not relivant.
48739  *
48740  * Fork - LGPL
48741  * <script type="text/javascript">
48742  */
48743  
48744 // private
48745 // This is a support class used internally by the Grid components
48746 Roo.grid.GridDragZone = function(grid, config){
48747     this.view = grid.getView();
48748     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
48749     if(this.view.lockedBody){
48750         this.setHandleElId(Roo.id(this.view.mainBody.dom));
48751         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
48752     }
48753     this.scroll = false;
48754     this.grid = grid;
48755     this.ddel = document.createElement('div');
48756     this.ddel.className = 'x-grid-dd-wrap';
48757 };
48758
48759 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
48760     ddGroup : "GridDD",
48761
48762     getDragData : function(e){
48763         var t = Roo.lib.Event.getTarget(e);
48764         var rowIndex = this.view.findRowIndex(t);
48765         if(rowIndex !== false){
48766             var sm = this.grid.selModel;
48767             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
48768               //  sm.mouseDown(e, t);
48769             //}
48770             if (e.hasModifier()){
48771                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
48772             }
48773             return {grid: this.grid, ddel: this.ddel, rowIndex: rowIndex, selections:sm.getSelections()};
48774         }
48775         return false;
48776     },
48777
48778     onInitDrag : function(e){
48779         var data = this.dragData;
48780         this.ddel.innerHTML = this.grid.getDragDropText();
48781         this.proxy.update(this.ddel);
48782         // fire start drag?
48783     },
48784
48785     afterRepair : function(){
48786         this.dragging = false;
48787     },
48788
48789     getRepairXY : function(e, data){
48790         return false;
48791     },
48792
48793     onEndDrag : function(data, e){
48794         // fire end drag?
48795     },
48796
48797     onValidDrop : function(dd, e, id){
48798         // fire drag drop?
48799         this.hideProxy();
48800     },
48801
48802     beforeInvalidDrop : function(e, id){
48803
48804     }
48805 });/*
48806  * Based on:
48807  * Ext JS Library 1.1.1
48808  * Copyright(c) 2006-2007, Ext JS, LLC.
48809  *
48810  * Originally Released Under LGPL - original licence link has changed is not relivant.
48811  *
48812  * Fork - LGPL
48813  * <script type="text/javascript">
48814  */
48815  
48816
48817 /**
48818  * @class Roo.grid.ColumnModel
48819  * @extends Roo.util.Observable
48820  * This is the default implementation of a ColumnModel used by the Grid. It defines
48821  * the columns in the grid.
48822  * <br>Usage:<br>
48823  <pre><code>
48824  var colModel = new Roo.grid.ColumnModel([
48825         {header: "Ticker", width: 60, sortable: true, locked: true},
48826         {header: "Company Name", width: 150, sortable: true},
48827         {header: "Market Cap.", width: 100, sortable: true},
48828         {header: "$ Sales", width: 100, sortable: true, renderer: money},
48829         {header: "Employees", width: 100, sortable: true, resizable: false}
48830  ]);
48831  </code></pre>
48832  * <p>
48833  
48834  * The config options listed for this class are options which may appear in each
48835  * individual column definition.
48836  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
48837  * @constructor
48838  * @param {Object} config An Array of column config objects. See this class's
48839  * config objects for details.
48840 */
48841 Roo.grid.ColumnModel = function(config){
48842         /**
48843      * The config passed into the constructor
48844      */
48845     this.config = config;
48846     this.lookup = {};
48847
48848     // if no id, create one
48849     // if the column does not have a dataIndex mapping,
48850     // map it to the order it is in the config
48851     for(var i = 0, len = config.length; i < len; i++){
48852         var c = config[i];
48853         if(typeof c.dataIndex == "undefined"){
48854             c.dataIndex = i;
48855         }
48856         if(typeof c.renderer == "string"){
48857             c.renderer = Roo.util.Format[c.renderer];
48858         }
48859         if(typeof c.id == "undefined"){
48860             c.id = Roo.id();
48861         }
48862         if(c.editor && c.editor.xtype){
48863             c.editor  = Roo.factory(c.editor, Roo.grid);
48864         }
48865         if(c.editor && c.editor.isFormField){
48866             c.editor = new Roo.grid.GridEditor(c.editor);
48867         }
48868         this.lookup[c.id] = c;
48869     }
48870
48871     /**
48872      * The width of columns which have no width specified (defaults to 100)
48873      * @type Number
48874      */
48875     this.defaultWidth = 100;
48876
48877     /**
48878      * Default sortable of columns which have no sortable specified (defaults to false)
48879      * @type Boolean
48880      */
48881     this.defaultSortable = false;
48882
48883     this.addEvents({
48884         /**
48885              * @event widthchange
48886              * Fires when the width of a column changes.
48887              * @param {ColumnModel} this
48888              * @param {Number} columnIndex The column index
48889              * @param {Number} newWidth The new width
48890              */
48891             "widthchange": true,
48892         /**
48893              * @event headerchange
48894              * Fires when the text of a header changes.
48895              * @param {ColumnModel} this
48896              * @param {Number} columnIndex The column index
48897              * @param {Number} newText The new header text
48898              */
48899             "headerchange": true,
48900         /**
48901              * @event hiddenchange
48902              * Fires when a column is hidden or "unhidden".
48903              * @param {ColumnModel} this
48904              * @param {Number} columnIndex The column index
48905              * @param {Boolean} hidden true if hidden, false otherwise
48906              */
48907             "hiddenchange": true,
48908             /**
48909          * @event columnmoved
48910          * Fires when a column is moved.
48911          * @param {ColumnModel} this
48912          * @param {Number} oldIndex
48913          * @param {Number} newIndex
48914          */
48915         "columnmoved" : true,
48916         /**
48917          * @event columlockchange
48918          * Fires when a column's locked state is changed
48919          * @param {ColumnModel} this
48920          * @param {Number} colIndex
48921          * @param {Boolean} locked true if locked
48922          */
48923         "columnlockchange" : true
48924     });
48925     Roo.grid.ColumnModel.superclass.constructor.call(this);
48926 };
48927 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
48928     /**
48929      * @cfg {String} header The header text to display in the Grid view.
48930      */
48931     /**
48932      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
48933      * {@link Roo.data.Record} definition from which to draw the column's value. If not
48934      * specified, the column's index is used as an index into the Record's data Array.
48935      */
48936     /**
48937      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
48938      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
48939      */
48940     /**
48941      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
48942      * Defaults to the value of the {@link #defaultSortable} property.
48943      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
48944      */
48945     /**
48946      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
48947      */
48948     /**
48949      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
48950      */
48951     /**
48952      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
48953      */
48954     /**
48955      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
48956      */
48957     /**
48958      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
48959      * given the cell's data value. See {@link #setRenderer}. If not specified, the
48960      * default renderer uses the raw data value.
48961      */
48962        /**
48963      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
48964      */
48965     /**
48966      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
48967      */
48968
48969     /**
48970      * Returns the id of the column at the specified index.
48971      * @param {Number} index The column index
48972      * @return {String} the id
48973      */
48974     getColumnId : function(index){
48975         return this.config[index].id;
48976     },
48977
48978     /**
48979      * Returns the column for a specified id.
48980      * @param {String} id The column id
48981      * @return {Object} the column
48982      */
48983     getColumnById : function(id){
48984         return this.lookup[id];
48985     },
48986
48987     
48988     /**
48989      * Returns the column for a specified dataIndex.
48990      * @param {String} dataIndex The column dataIndex
48991      * @return {Object|Boolean} the column or false if not found
48992      */
48993     getColumnByDataIndex: function(dataIndex){
48994         var index = this.findColumnIndex(dataIndex);
48995         return index > -1 ? this.config[index] : false;
48996     },
48997     
48998     /**
48999      * Returns the index for a specified column id.
49000      * @param {String} id The column id
49001      * @return {Number} the index, or -1 if not found
49002      */
49003     getIndexById : function(id){
49004         for(var i = 0, len = this.config.length; i < len; i++){
49005             if(this.config[i].id == id){
49006                 return i;
49007             }
49008         }
49009         return -1;
49010     },
49011     
49012     /**
49013      * Returns the index for a specified column dataIndex.
49014      * @param {String} dataIndex The column dataIndex
49015      * @return {Number} the index, or -1 if not found
49016      */
49017     
49018     findColumnIndex : function(dataIndex){
49019         for(var i = 0, len = this.config.length; i < len; i++){
49020             if(this.config[i].dataIndex == dataIndex){
49021                 return i;
49022             }
49023         }
49024         return -1;
49025     },
49026     
49027     
49028     moveColumn : function(oldIndex, newIndex){
49029         var c = this.config[oldIndex];
49030         this.config.splice(oldIndex, 1);
49031         this.config.splice(newIndex, 0, c);
49032         this.dataMap = null;
49033         this.fireEvent("columnmoved", this, oldIndex, newIndex);
49034     },
49035
49036     isLocked : function(colIndex){
49037         return this.config[colIndex].locked === true;
49038     },
49039
49040     setLocked : function(colIndex, value, suppressEvent){
49041         if(this.isLocked(colIndex) == value){
49042             return;
49043         }
49044         this.config[colIndex].locked = value;
49045         if(!suppressEvent){
49046             this.fireEvent("columnlockchange", this, colIndex, value);
49047         }
49048     },
49049
49050     getTotalLockedWidth : function(){
49051         var totalWidth = 0;
49052         for(var i = 0; i < this.config.length; i++){
49053             if(this.isLocked(i) && !this.isHidden(i)){
49054                 this.totalWidth += this.getColumnWidth(i);
49055             }
49056         }
49057         return totalWidth;
49058     },
49059
49060     getLockedCount : function(){
49061         for(var i = 0, len = this.config.length; i < len; i++){
49062             if(!this.isLocked(i)){
49063                 return i;
49064             }
49065         }
49066     },
49067
49068     /**
49069      * Returns the number of columns.
49070      * @return {Number}
49071      */
49072     getColumnCount : function(visibleOnly){
49073         if(visibleOnly === true){
49074             var c = 0;
49075             for(var i = 0, len = this.config.length; i < len; i++){
49076                 if(!this.isHidden(i)){
49077                     c++;
49078                 }
49079             }
49080             return c;
49081         }
49082         return this.config.length;
49083     },
49084
49085     /**
49086      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
49087      * @param {Function} fn
49088      * @param {Object} scope (optional)
49089      * @return {Array} result
49090      */
49091     getColumnsBy : function(fn, scope){
49092         var r = [];
49093         for(var i = 0, len = this.config.length; i < len; i++){
49094             var c = this.config[i];
49095             if(fn.call(scope||this, c, i) === true){
49096                 r[r.length] = c;
49097             }
49098         }
49099         return r;
49100     },
49101
49102     /**
49103      * Returns true if the specified column is sortable.
49104      * @param {Number} col The column index
49105      * @return {Boolean}
49106      */
49107     isSortable : function(col){
49108         if(typeof this.config[col].sortable == "undefined"){
49109             return this.defaultSortable;
49110         }
49111         return this.config[col].sortable;
49112     },
49113
49114     /**
49115      * Returns the rendering (formatting) function defined for the column.
49116      * @param {Number} col The column index.
49117      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
49118      */
49119     getRenderer : function(col){
49120         if(!this.config[col].renderer){
49121             return Roo.grid.ColumnModel.defaultRenderer;
49122         }
49123         return this.config[col].renderer;
49124     },
49125
49126     /**
49127      * Sets the rendering (formatting) function for a column.
49128      * @param {Number} col The column index
49129      * @param {Function} fn The function to use to process the cell's raw data
49130      * to return HTML markup for the grid view. The render function is called with
49131      * the following parameters:<ul>
49132      * <li>Data value.</li>
49133      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
49134      * <li>css A CSS style string to apply to the table cell.</li>
49135      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
49136      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
49137      * <li>Row index</li>
49138      * <li>Column index</li>
49139      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
49140      */
49141     setRenderer : function(col, fn){
49142         this.config[col].renderer = fn;
49143     },
49144
49145     /**
49146      * Returns the width for the specified column.
49147      * @param {Number} col The column index
49148      * @return {Number}
49149      */
49150     getColumnWidth : function(col){
49151         return this.config[col].width || this.defaultWidth;
49152     },
49153
49154     /**
49155      * Sets the width for a column.
49156      * @param {Number} col The column index
49157      * @param {Number} width The new width
49158      */
49159     setColumnWidth : function(col, width, suppressEvent){
49160         this.config[col].width = width;
49161         this.totalWidth = null;
49162         if(!suppressEvent){
49163              this.fireEvent("widthchange", this, col, width);
49164         }
49165     },
49166
49167     /**
49168      * Returns the total width of all columns.
49169      * @param {Boolean} includeHidden True to include hidden column widths
49170      * @return {Number}
49171      */
49172     getTotalWidth : function(includeHidden){
49173         if(!this.totalWidth){
49174             this.totalWidth = 0;
49175             for(var i = 0, len = this.config.length; i < len; i++){
49176                 if(includeHidden || !this.isHidden(i)){
49177                     this.totalWidth += this.getColumnWidth(i);
49178                 }
49179             }
49180         }
49181         return this.totalWidth;
49182     },
49183
49184     /**
49185      * Returns the header for the specified column.
49186      * @param {Number} col The column index
49187      * @return {String}
49188      */
49189     getColumnHeader : function(col){
49190         return this.config[col].header;
49191     },
49192
49193     /**
49194      * Sets the header for a column.
49195      * @param {Number} col The column index
49196      * @param {String} header The new header
49197      */
49198     setColumnHeader : function(col, header){
49199         this.config[col].header = header;
49200         this.fireEvent("headerchange", this, col, header);
49201     },
49202
49203     /**
49204      * Returns the tooltip for the specified column.
49205      * @param {Number} col The column index
49206      * @return {String}
49207      */
49208     getColumnTooltip : function(col){
49209             return this.config[col].tooltip;
49210     },
49211     /**
49212      * Sets the tooltip for a column.
49213      * @param {Number} col The column index
49214      * @param {String} tooltip The new tooltip
49215      */
49216     setColumnTooltip : function(col, tooltip){
49217             this.config[col].tooltip = tooltip;
49218     },
49219
49220     /**
49221      * Returns the dataIndex for the specified column.
49222      * @param {Number} col The column index
49223      * @return {Number}
49224      */
49225     getDataIndex : function(col){
49226         return this.config[col].dataIndex;
49227     },
49228
49229     /**
49230      * Sets the dataIndex for a column.
49231      * @param {Number} col The column index
49232      * @param {Number} dataIndex The new dataIndex
49233      */
49234     setDataIndex : function(col, dataIndex){
49235         this.config[col].dataIndex = dataIndex;
49236     },
49237
49238     
49239     
49240     /**
49241      * Returns true if the cell is editable.
49242      * @param {Number} colIndex The column index
49243      * @param {Number} rowIndex The row index
49244      * @return {Boolean}
49245      */
49246     isCellEditable : function(colIndex, rowIndex){
49247         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
49248     },
49249
49250     /**
49251      * Returns the editor defined for the cell/column.
49252      * return false or null to disable editing.
49253      * @param {Number} colIndex The column index
49254      * @param {Number} rowIndex The row index
49255      * @return {Object}
49256      */
49257     getCellEditor : function(colIndex, rowIndex){
49258         return this.config[colIndex].editor;
49259     },
49260
49261     /**
49262      * Sets if a column is editable.
49263      * @param {Number} col The column index
49264      * @param {Boolean} editable True if the column is editable
49265      */
49266     setEditable : function(col, editable){
49267         this.config[col].editable = editable;
49268     },
49269
49270
49271     /**
49272      * Returns true if the column is hidden.
49273      * @param {Number} colIndex The column index
49274      * @return {Boolean}
49275      */
49276     isHidden : function(colIndex){
49277         return this.config[colIndex].hidden;
49278     },
49279
49280
49281     /**
49282      * Returns true if the column width cannot be changed
49283      */
49284     isFixed : function(colIndex){
49285         return this.config[colIndex].fixed;
49286     },
49287
49288     /**
49289      * Returns true if the column can be resized
49290      * @return {Boolean}
49291      */
49292     isResizable : function(colIndex){
49293         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
49294     },
49295     /**
49296      * Sets if a column is hidden.
49297      * @param {Number} colIndex The column index
49298      * @param {Boolean} hidden True if the column is hidden
49299      */
49300     setHidden : function(colIndex, hidden){
49301         this.config[colIndex].hidden = hidden;
49302         this.totalWidth = null;
49303         this.fireEvent("hiddenchange", this, colIndex, hidden);
49304     },
49305
49306     /**
49307      * Sets the editor for a column.
49308      * @param {Number} col The column index
49309      * @param {Object} editor The editor object
49310      */
49311     setEditor : function(col, editor){
49312         this.config[col].editor = editor;
49313     }
49314 });
49315
49316 Roo.grid.ColumnModel.defaultRenderer = function(value){
49317         if(typeof value == "string" && value.length < 1){
49318             return "&#160;";
49319         }
49320         return value;
49321 };
49322
49323 // Alias for backwards compatibility
49324 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
49325 /*
49326  * Based on:
49327  * Ext JS Library 1.1.1
49328  * Copyright(c) 2006-2007, Ext JS, LLC.
49329  *
49330  * Originally Released Under LGPL - original licence link has changed is not relivant.
49331  *
49332  * Fork - LGPL
49333  * <script type="text/javascript">
49334  */
49335
49336 /**
49337  * @class Roo.grid.AbstractSelectionModel
49338  * @extends Roo.util.Observable
49339  * Abstract base class for grid SelectionModels.  It provides the interface that should be
49340  * implemented by descendant classes.  This class should not be directly instantiated.
49341  * @constructor
49342  */
49343 Roo.grid.AbstractSelectionModel = function(){
49344     this.locked = false;
49345     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
49346 };
49347
49348 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
49349     /** @ignore Called by the grid automatically. Do not call directly. */
49350     init : function(grid){
49351         this.grid = grid;
49352         this.initEvents();
49353     },
49354
49355     /**
49356      * Locks the selections.
49357      */
49358     lock : function(){
49359         this.locked = true;
49360     },
49361
49362     /**
49363      * Unlocks the selections.
49364      */
49365     unlock : function(){
49366         this.locked = false;
49367     },
49368
49369     /**
49370      * Returns true if the selections are locked.
49371      * @return {Boolean}
49372      */
49373     isLocked : function(){
49374         return this.locked;
49375     }
49376 });/*
49377  * Based on:
49378  * Ext JS Library 1.1.1
49379  * Copyright(c) 2006-2007, Ext JS, LLC.
49380  *
49381  * Originally Released Under LGPL - original licence link has changed is not relivant.
49382  *
49383  * Fork - LGPL
49384  * <script type="text/javascript">
49385  */
49386 /**
49387  * @extends Roo.grid.AbstractSelectionModel
49388  * @class Roo.grid.RowSelectionModel
49389  * The default SelectionModel used by {@link Roo.grid.Grid}.
49390  * It supports multiple selections and keyboard selection/navigation. 
49391  * @constructor
49392  * @param {Object} config
49393  */
49394 Roo.grid.RowSelectionModel = function(config){
49395     Roo.apply(this, config);
49396     this.selections = new Roo.util.MixedCollection(false, function(o){
49397         return o.id;
49398     });
49399
49400     this.last = false;
49401     this.lastActive = false;
49402
49403     this.addEvents({
49404         /**
49405              * @event selectionchange
49406              * Fires when the selection changes
49407              * @param {SelectionModel} this
49408              */
49409             "selectionchange" : true,
49410         /**
49411              * @event afterselectionchange
49412              * Fires after the selection changes (eg. by key press or clicking)
49413              * @param {SelectionModel} this
49414              */
49415             "afterselectionchange" : true,
49416         /**
49417              * @event beforerowselect
49418              * Fires when a row is selected being selected, return false to cancel.
49419              * @param {SelectionModel} this
49420              * @param {Number} rowIndex The selected index
49421              * @param {Boolean} keepExisting False if other selections will be cleared
49422              */
49423             "beforerowselect" : true,
49424         /**
49425              * @event rowselect
49426              * Fires when a row is selected.
49427              * @param {SelectionModel} this
49428              * @param {Number} rowIndex The selected index
49429              * @param {Roo.data.Record} r The record
49430              */
49431             "rowselect" : true,
49432         /**
49433              * @event rowdeselect
49434              * Fires when a row is deselected.
49435              * @param {SelectionModel} this
49436              * @param {Number} rowIndex The selected index
49437              */
49438         "rowdeselect" : true
49439     });
49440     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
49441     this.locked = false;
49442 };
49443
49444 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
49445     /**
49446      * @cfg {Boolean} singleSelect
49447      * True to allow selection of only one row at a time (defaults to false)
49448      */
49449     singleSelect : false,
49450
49451     // private
49452     initEvents : function(){
49453
49454         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
49455             this.grid.on("mousedown", this.handleMouseDown, this);
49456         }else{ // allow click to work like normal
49457             this.grid.on("rowclick", this.handleDragableRowClick, this);
49458         }
49459
49460         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
49461             "up" : function(e){
49462                 if(!e.shiftKey){
49463                     this.selectPrevious(e.shiftKey);
49464                 }else if(this.last !== false && this.lastActive !== false){
49465                     var last = this.last;
49466                     this.selectRange(this.last,  this.lastActive-1);
49467                     this.grid.getView().focusRow(this.lastActive);
49468                     if(last !== false){
49469                         this.last = last;
49470                     }
49471                 }else{
49472                     this.selectFirstRow();
49473                 }
49474                 this.fireEvent("afterselectionchange", this);
49475             },
49476             "down" : function(e){
49477                 if(!e.shiftKey){
49478                     this.selectNext(e.shiftKey);
49479                 }else if(this.last !== false && this.lastActive !== false){
49480                     var last = this.last;
49481                     this.selectRange(this.last,  this.lastActive+1);
49482                     this.grid.getView().focusRow(this.lastActive);
49483                     if(last !== false){
49484                         this.last = last;
49485                     }
49486                 }else{
49487                     this.selectFirstRow();
49488                 }
49489                 this.fireEvent("afterselectionchange", this);
49490             },
49491             scope: this
49492         });
49493
49494         var view = this.grid.view;
49495         view.on("refresh", this.onRefresh, this);
49496         view.on("rowupdated", this.onRowUpdated, this);
49497         view.on("rowremoved", this.onRemove, this);
49498     },
49499
49500     // private
49501     onRefresh : function(){
49502         var ds = this.grid.dataSource, i, v = this.grid.view;
49503         var s = this.selections;
49504         s.each(function(r){
49505             if((i = ds.indexOfId(r.id)) != -1){
49506                 v.onRowSelect(i);
49507             }else{
49508                 s.remove(r);
49509             }
49510         });
49511     },
49512
49513     // private
49514     onRemove : function(v, index, r){
49515         this.selections.remove(r);
49516     },
49517
49518     // private
49519     onRowUpdated : function(v, index, r){
49520         if(this.isSelected(r)){
49521             v.onRowSelect(index);
49522         }
49523     },
49524
49525     /**
49526      * Select records.
49527      * @param {Array} records The records to select
49528      * @param {Boolean} keepExisting (optional) True to keep existing selections
49529      */
49530     selectRecords : function(records, keepExisting){
49531         if(!keepExisting){
49532             this.clearSelections();
49533         }
49534         var ds = this.grid.dataSource;
49535         for(var i = 0, len = records.length; i < len; i++){
49536             this.selectRow(ds.indexOf(records[i]), true);
49537         }
49538     },
49539
49540     /**
49541      * Gets the number of selected rows.
49542      * @return {Number}
49543      */
49544     getCount : function(){
49545         return this.selections.length;
49546     },
49547
49548     /**
49549      * Selects the first row in the grid.
49550      */
49551     selectFirstRow : function(){
49552         this.selectRow(0);
49553     },
49554
49555     /**
49556      * Select the last row.
49557      * @param {Boolean} keepExisting (optional) True to keep existing selections
49558      */
49559     selectLastRow : function(keepExisting){
49560         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
49561     },
49562
49563     /**
49564      * Selects the row immediately following the last selected row.
49565      * @param {Boolean} keepExisting (optional) True to keep existing selections
49566      */
49567     selectNext : function(keepExisting){
49568         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
49569             this.selectRow(this.last+1, keepExisting);
49570             this.grid.getView().focusRow(this.last);
49571         }
49572     },
49573
49574     /**
49575      * Selects the row that precedes the last selected row.
49576      * @param {Boolean} keepExisting (optional) True to keep existing selections
49577      */
49578     selectPrevious : function(keepExisting){
49579         if(this.last){
49580             this.selectRow(this.last-1, keepExisting);
49581             this.grid.getView().focusRow(this.last);
49582         }
49583     },
49584
49585     /**
49586      * Returns the selected records
49587      * @return {Array} Array of selected records
49588      */
49589     getSelections : function(){
49590         return [].concat(this.selections.items);
49591     },
49592
49593     /**
49594      * Returns the first selected record.
49595      * @return {Record}
49596      */
49597     getSelected : function(){
49598         return this.selections.itemAt(0);
49599     },
49600
49601
49602     /**
49603      * Clears all selections.
49604      */
49605     clearSelections : function(fast){
49606         if(this.locked) return;
49607         if(fast !== true){
49608             var ds = this.grid.dataSource;
49609             var s = this.selections;
49610             s.each(function(r){
49611                 this.deselectRow(ds.indexOfId(r.id));
49612             }, this);
49613             s.clear();
49614         }else{
49615             this.selections.clear();
49616         }
49617         this.last = false;
49618     },
49619
49620
49621     /**
49622      * Selects all rows.
49623      */
49624     selectAll : function(){
49625         if(this.locked) return;
49626         this.selections.clear();
49627         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
49628             this.selectRow(i, true);
49629         }
49630     },
49631
49632     /**
49633      * Returns True if there is a selection.
49634      * @return {Boolean}
49635      */
49636     hasSelection : function(){
49637         return this.selections.length > 0;
49638     },
49639
49640     /**
49641      * Returns True if the specified row is selected.
49642      * @param {Number/Record} record The record or index of the record to check
49643      * @return {Boolean}
49644      */
49645     isSelected : function(index){
49646         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
49647         return (r && this.selections.key(r.id) ? true : false);
49648     },
49649
49650     /**
49651      * Returns True if the specified record id is selected.
49652      * @param {String} id The id of record to check
49653      * @return {Boolean}
49654      */
49655     isIdSelected : function(id){
49656         return (this.selections.key(id) ? true : false);
49657     },
49658
49659     // private
49660     handleMouseDown : function(e, t){
49661         var view = this.grid.getView(), rowIndex;
49662         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
49663             return;
49664         };
49665         if(e.shiftKey && this.last !== false){
49666             var last = this.last;
49667             this.selectRange(last, rowIndex, e.ctrlKey);
49668             this.last = last; // reset the last
49669             view.focusRow(rowIndex);
49670         }else{
49671             var isSelected = this.isSelected(rowIndex);
49672             if(e.button !== 0 && isSelected){
49673                 view.focusRow(rowIndex);
49674             }else if(e.ctrlKey && isSelected){
49675                 this.deselectRow(rowIndex);
49676             }else if(!isSelected){
49677                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
49678                 view.focusRow(rowIndex);
49679             }
49680         }
49681         this.fireEvent("afterselectionchange", this);
49682     },
49683     // private
49684     handleDragableRowClick :  function(grid, rowIndex, e) 
49685     {
49686         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
49687             this.selectRow(rowIndex, false);
49688             grid.view.focusRow(rowIndex);
49689              this.fireEvent("afterselectionchange", this);
49690         }
49691     },
49692     
49693     /**
49694      * Selects multiple rows.
49695      * @param {Array} rows Array of the indexes of the row to select
49696      * @param {Boolean} keepExisting (optional) True to keep existing selections
49697      */
49698     selectRows : function(rows, keepExisting){
49699         if(!keepExisting){
49700             this.clearSelections();
49701         }
49702         for(var i = 0, len = rows.length; i < len; i++){
49703             this.selectRow(rows[i], true);
49704         }
49705     },
49706
49707     /**
49708      * Selects a range of rows. All rows in between startRow and endRow are also selected.
49709      * @param {Number} startRow The index of the first row in the range
49710      * @param {Number} endRow The index of the last row in the range
49711      * @param {Boolean} keepExisting (optional) True to retain existing selections
49712      */
49713     selectRange : function(startRow, endRow, keepExisting){
49714         if(this.locked) return;
49715         if(!keepExisting){
49716             this.clearSelections();
49717         }
49718         if(startRow <= endRow){
49719             for(var i = startRow; i <= endRow; i++){
49720                 this.selectRow(i, true);
49721             }
49722         }else{
49723             for(var i = startRow; i >= endRow; i--){
49724                 this.selectRow(i, true);
49725             }
49726         }
49727     },
49728
49729     /**
49730      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
49731      * @param {Number} startRow The index of the first row in the range
49732      * @param {Number} endRow The index of the last row in the range
49733      */
49734     deselectRange : function(startRow, endRow, preventViewNotify){
49735         if(this.locked) return;
49736         for(var i = startRow; i <= endRow; i++){
49737             this.deselectRow(i, preventViewNotify);
49738         }
49739     },
49740
49741     /**
49742      * Selects a row.
49743      * @param {Number} row The index of the row to select
49744      * @param {Boolean} keepExisting (optional) True to keep existing selections
49745      */
49746     selectRow : function(index, keepExisting, preventViewNotify){
49747         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
49748         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
49749             if(!keepExisting || this.singleSelect){
49750                 this.clearSelections();
49751             }
49752             var r = this.grid.dataSource.getAt(index);
49753             this.selections.add(r);
49754             this.last = this.lastActive = index;
49755             if(!preventViewNotify){
49756                 this.grid.getView().onRowSelect(index);
49757             }
49758             this.fireEvent("rowselect", this, index, r);
49759             this.fireEvent("selectionchange", this);
49760         }
49761     },
49762
49763     /**
49764      * Deselects a row.
49765      * @param {Number} row The index of the row to deselect
49766      */
49767     deselectRow : function(index, preventViewNotify){
49768         if(this.locked) return;
49769         if(this.last == index){
49770             this.last = false;
49771         }
49772         if(this.lastActive == index){
49773             this.lastActive = false;
49774         }
49775         var r = this.grid.dataSource.getAt(index);
49776         this.selections.remove(r);
49777         if(!preventViewNotify){
49778             this.grid.getView().onRowDeselect(index);
49779         }
49780         this.fireEvent("rowdeselect", this, index);
49781         this.fireEvent("selectionchange", this);
49782     },
49783
49784     // private
49785     restoreLast : function(){
49786         if(this._last){
49787             this.last = this._last;
49788         }
49789     },
49790
49791     // private
49792     acceptsNav : function(row, col, cm){
49793         return !cm.isHidden(col) && cm.isCellEditable(col, row);
49794     },
49795
49796     // private
49797     onEditorKey : function(field, e){
49798         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
49799         if(k == e.TAB){
49800             e.stopEvent();
49801             ed.completeEdit();
49802             if(e.shiftKey){
49803                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
49804             }else{
49805                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
49806             }
49807         }else if(k == e.ENTER && !e.ctrlKey){
49808             e.stopEvent();
49809             ed.completeEdit();
49810             if(e.shiftKey){
49811                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
49812             }else{
49813                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
49814             }
49815         }else if(k == e.ESC){
49816             ed.cancelEdit();
49817         }
49818         if(newCell){
49819             g.startEditing(newCell[0], newCell[1]);
49820         }
49821     }
49822 });/*
49823  * Based on:
49824  * Ext JS Library 1.1.1
49825  * Copyright(c) 2006-2007, Ext JS, LLC.
49826  *
49827  * Originally Released Under LGPL - original licence link has changed is not relivant.
49828  *
49829  * Fork - LGPL
49830  * <script type="text/javascript">
49831  */
49832 /**
49833  * @class Roo.grid.CellSelectionModel
49834  * @extends Roo.grid.AbstractSelectionModel
49835  * This class provides the basic implementation for cell selection in a grid.
49836  * @constructor
49837  * @param {Object} config The object containing the configuration of this model.
49838  */
49839 Roo.grid.CellSelectionModel = function(config){
49840     Roo.apply(this, config);
49841
49842     this.selection = null;
49843
49844     this.addEvents({
49845         /**
49846              * @event beforerowselect
49847              * Fires before a cell is selected.
49848              * @param {SelectionModel} this
49849              * @param {Number} rowIndex The selected row index
49850              * @param {Number} colIndex The selected cell index
49851              */
49852             "beforecellselect" : true,
49853         /**
49854              * @event cellselect
49855              * Fires when a cell is selected.
49856              * @param {SelectionModel} this
49857              * @param {Number} rowIndex The selected row index
49858              * @param {Number} colIndex The selected cell index
49859              */
49860             "cellselect" : true,
49861         /**
49862              * @event selectionchange
49863              * Fires when the active selection changes.
49864              * @param {SelectionModel} this
49865              * @param {Object} selection null for no selection or an object (o) with two properties
49866                 <ul>
49867                 <li>o.record: the record object for the row the selection is in</li>
49868                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
49869                 </ul>
49870              */
49871             "selectionchange" : true
49872     });
49873     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
49874 };
49875
49876 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
49877
49878     /** @ignore */
49879     initEvents : function(){
49880         this.grid.on("mousedown", this.handleMouseDown, this);
49881         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
49882         var view = this.grid.view;
49883         view.on("refresh", this.onViewChange, this);
49884         view.on("rowupdated", this.onRowUpdated, this);
49885         view.on("beforerowremoved", this.clearSelections, this);
49886         view.on("beforerowsinserted", this.clearSelections, this);
49887         if(this.grid.isEditor){
49888             this.grid.on("beforeedit", this.beforeEdit,  this);
49889         }
49890     },
49891
49892         //private
49893     beforeEdit : function(e){
49894         this.select(e.row, e.column, false, true, e.record);
49895     },
49896
49897         //private
49898     onRowUpdated : function(v, index, r){
49899         if(this.selection && this.selection.record == r){
49900             v.onCellSelect(index, this.selection.cell[1]);
49901         }
49902     },
49903
49904         //private
49905     onViewChange : function(){
49906         this.clearSelections(true);
49907     },
49908
49909         /**
49910          * Returns the currently selected cell,.
49911          * @return {Array} The selected cell (row, column) or null if none selected.
49912          */
49913     getSelectedCell : function(){
49914         return this.selection ? this.selection.cell : null;
49915     },
49916
49917     /**
49918      * Clears all selections.
49919      * @param {Boolean} true to prevent the gridview from being notified about the change.
49920      */
49921     clearSelections : function(preventNotify){
49922         var s = this.selection;
49923         if(s){
49924             if(preventNotify !== true){
49925                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
49926             }
49927             this.selection = null;
49928             this.fireEvent("selectionchange", this, null);
49929         }
49930     },
49931
49932     /**
49933      * Returns true if there is a selection.
49934      * @return {Boolean}
49935      */
49936     hasSelection : function(){
49937         return this.selection ? true : false;
49938     },
49939
49940     /** @ignore */
49941     handleMouseDown : function(e, t){
49942         var v = this.grid.getView();
49943         if(this.isLocked()){
49944             return;
49945         };
49946         var row = v.findRowIndex(t);
49947         var cell = v.findCellIndex(t);
49948         if(row !== false && cell !== false){
49949             this.select(row, cell);
49950         }
49951     },
49952
49953     /**
49954      * Selects a cell.
49955      * @param {Number} rowIndex
49956      * @param {Number} collIndex
49957      */
49958     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
49959         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
49960             this.clearSelections();
49961             r = r || this.grid.dataSource.getAt(rowIndex);
49962             this.selection = {
49963                 record : r,
49964                 cell : [rowIndex, colIndex]
49965             };
49966             if(!preventViewNotify){
49967                 var v = this.grid.getView();
49968                 v.onCellSelect(rowIndex, colIndex);
49969                 if(preventFocus !== true){
49970                     v.focusCell(rowIndex, colIndex);
49971                 }
49972             }
49973             this.fireEvent("cellselect", this, rowIndex, colIndex);
49974             this.fireEvent("selectionchange", this, this.selection);
49975         }
49976     },
49977
49978         //private
49979     isSelectable : function(rowIndex, colIndex, cm){
49980         return !cm.isHidden(colIndex);
49981     },
49982
49983     /** @ignore */
49984     handleKeyDown : function(e){
49985         Roo.log('Cell Sel Model handleKeyDown');
49986         if(!e.isNavKeyPress()){
49987             return;
49988         }
49989         var g = this.grid, s = this.selection;
49990         if(!s){
49991             e.stopEvent();
49992             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
49993             if(cell){
49994                 this.select(cell[0], cell[1]);
49995             }
49996             return;
49997         }
49998         var sm = this;
49999         var walk = function(row, col, step){
50000             return g.walkCells(row, col, step, sm.isSelectable,  sm);
50001         };
50002         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
50003         var newCell;
50004
50005         switch(k){
50006             case e.TAB:
50007                 // handled by onEditorKey
50008                 if (g.isEditor && g.editing) {
50009                     return;
50010                 }
50011                 if(e.shiftKey){
50012                      newCell = walk(r, c-1, -1);
50013                 }else{
50014                      newCell = walk(r, c+1, 1);
50015                 }
50016              break;
50017              case e.DOWN:
50018                  newCell = walk(r+1, c, 1);
50019              break;
50020              case e.UP:
50021                  newCell = walk(r-1, c, -1);
50022              break;
50023              case e.RIGHT:
50024                  newCell = walk(r, c+1, 1);
50025              break;
50026              case e.LEFT:
50027                  newCell = walk(r, c-1, -1);
50028              break;
50029              case e.ENTER:
50030                  if(g.isEditor && !g.editing){
50031                     g.startEditing(r, c);
50032                     e.stopEvent();
50033                     return;
50034                 }
50035              break;
50036         };
50037         if(newCell){
50038             this.select(newCell[0], newCell[1]);
50039             e.stopEvent();
50040         }
50041     },
50042
50043     acceptsNav : function(row, col, cm){
50044         return !cm.isHidden(col) && cm.isCellEditable(col, row);
50045     },
50046
50047     onEditorKey : function(field, e){
50048         
50049         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
50050         ///Roo.log('onEditorKey' + k);
50051         
50052         if(k == e.TAB){
50053             if(e.shiftKey){
50054                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
50055             }else{
50056                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50057             }
50058             e.stopEvent();
50059         }else if(k == e.ENTER && !e.ctrlKey){
50060             ed.completeEdit();
50061             e.stopEvent();
50062             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
50063         }else if(k == e.ESC){
50064             ed.cancelEdit();
50065         }
50066         
50067         
50068         if(newCell){
50069             //Roo.log('next cell after edit');
50070             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
50071         }
50072     }
50073 });/*
50074  * Based on:
50075  * Ext JS Library 1.1.1
50076  * Copyright(c) 2006-2007, Ext JS, LLC.
50077  *
50078  * Originally Released Under LGPL - original licence link has changed is not relivant.
50079  *
50080  * Fork - LGPL
50081  * <script type="text/javascript">
50082  */
50083  
50084 /**
50085  * @class Roo.grid.EditorGrid
50086  * @extends Roo.grid.Grid
50087  * Class for creating and editable grid.
50088  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
50089  * The container MUST have some type of size defined for the grid to fill. The container will be 
50090  * automatically set to position relative if it isn't already.
50091  * @param {Object} dataSource The data model to bind to
50092  * @param {Object} colModel The column model with info about this grid's columns
50093  */
50094 Roo.grid.EditorGrid = function(container, config){
50095     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
50096     this.getGridEl().addClass("xedit-grid");
50097
50098     if(!this.selModel){
50099         this.selModel = new Roo.grid.CellSelectionModel();
50100     }
50101
50102     this.activeEditor = null;
50103
50104         this.addEvents({
50105             /**
50106              * @event beforeedit
50107              * Fires before cell editing is triggered. The edit event object has the following properties <br />
50108              * <ul style="padding:5px;padding-left:16px;">
50109              * <li>grid - This grid</li>
50110              * <li>record - The record being edited</li>
50111              * <li>field - The field name being edited</li>
50112              * <li>value - The value for the field being edited.</li>
50113              * <li>row - The grid row index</li>
50114              * <li>column - The grid column index</li>
50115              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50116              * </ul>
50117              * @param {Object} e An edit event (see above for description)
50118              */
50119             "beforeedit" : true,
50120             /**
50121              * @event afteredit
50122              * Fires after a cell is edited. <br />
50123              * <ul style="padding:5px;padding-left:16px;">
50124              * <li>grid - This grid</li>
50125              * <li>record - The record being edited</li>
50126              * <li>field - The field name being edited</li>
50127              * <li>value - The value being set</li>
50128              * <li>originalValue - The original value for the field, before the edit.</li>
50129              * <li>row - The grid row index</li>
50130              * <li>column - The grid column index</li>
50131              * </ul>
50132              * @param {Object} e An edit event (see above for description)
50133              */
50134             "afteredit" : true,
50135             /**
50136              * @event validateedit
50137              * Fires after a cell is edited, but before the value is set in the record. 
50138          * You can use this to modify the value being set in the field, Return false
50139              * to cancel the change. The edit event object has the following properties <br />
50140              * <ul style="padding:5px;padding-left:16px;">
50141          * <li>editor - This editor</li>
50142              * <li>grid - This grid</li>
50143              * <li>record - The record being edited</li>
50144              * <li>field - The field name being edited</li>
50145              * <li>value - The value being set</li>
50146              * <li>originalValue - The original value for the field, before the edit.</li>
50147              * <li>row - The grid row index</li>
50148              * <li>column - The grid column index</li>
50149              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
50150              * </ul>
50151              * @param {Object} e An edit event (see above for description)
50152              */
50153             "validateedit" : true
50154         });
50155     this.on("bodyscroll", this.stopEditing,  this);
50156     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
50157 };
50158
50159 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
50160     /**
50161      * @cfg {Number} clicksToEdit
50162      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
50163      */
50164     clicksToEdit: 2,
50165
50166     // private
50167     isEditor : true,
50168     // private
50169     trackMouseOver: false, // causes very odd FF errors
50170
50171     onCellDblClick : function(g, row, col){
50172         this.startEditing(row, col);
50173     },
50174
50175     onEditComplete : function(ed, value, startValue){
50176         this.editing = false;
50177         this.activeEditor = null;
50178         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
50179         var r = ed.record;
50180         var field = this.colModel.getDataIndex(ed.col);
50181         var e = {
50182             grid: this,
50183             record: r,
50184             field: field,
50185             originalValue: startValue,
50186             value: value,
50187             row: ed.row,
50188             column: ed.col,
50189             cancel:false,
50190             editor: ed
50191         };
50192         if(String(value) !== String(startValue)){
50193             
50194             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
50195                 r.set(field, e.value);
50196                 // if we are dealing with a combo box..
50197                 // then we also set the 'name' colum to be the displayField
50198                 if (ed.field.displayField && ed.field.name) {
50199                     r.set(ed.field.name, ed.field.el.dom.value);
50200                 }
50201                 
50202                 delete e.cancel; //?? why!!!
50203                 this.fireEvent("afteredit", e);
50204             }
50205         } else {
50206             this.fireEvent("afteredit", e); // always fire it!
50207         }
50208         this.view.focusCell(ed.row, ed.col);
50209     },
50210
50211     /**
50212      * Starts editing the specified for the specified row/column
50213      * @param {Number} rowIndex
50214      * @param {Number} colIndex
50215      */
50216     startEditing : function(row, col){
50217         this.stopEditing();
50218         if(this.colModel.isCellEditable(col, row)){
50219             this.view.ensureVisible(row, col, true);
50220             var r = this.dataSource.getAt(row);
50221             var field = this.colModel.getDataIndex(col);
50222             var e = {
50223                 grid: this,
50224                 record: r,
50225                 field: field,
50226                 value: r.data[field],
50227                 row: row,
50228                 column: col,
50229                 cancel:false
50230             };
50231             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
50232                 this.editing = true;
50233                 var ed = this.colModel.getCellEditor(col, row);
50234                 
50235                 if (!ed) {
50236                     return;
50237                 }
50238                 if(!ed.rendered){
50239                     ed.render(ed.parentEl || document.body);
50240                 }
50241                 ed.field.reset();
50242                 (function(){ // complex but required for focus issues in safari, ie and opera
50243                     ed.row = row;
50244                     ed.col = col;
50245                     ed.record = r;
50246                     ed.on("complete", this.onEditComplete, this, {single: true});
50247                     ed.on("specialkey", this.selModel.onEditorKey, this.selModel);
50248                     this.activeEditor = ed;
50249                     var v = r.data[field];
50250                     ed.startEdit(this.view.getCell(row, col), v);
50251                     // combo's with 'displayField and name set
50252                     if (ed.field.displayField && ed.field.name) {
50253                         ed.field.el.dom.value = r.data[ed.field.name];
50254                     }
50255                     
50256                     
50257                 }).defer(50, this);
50258             }
50259         }
50260     },
50261         
50262     /**
50263      * Stops any active editing
50264      */
50265     stopEditing : function(){
50266         if(this.activeEditor){
50267             this.activeEditor.completeEdit();
50268         }
50269         this.activeEditor = null;
50270     }
50271 });/*
50272  * Based on:
50273  * Ext JS Library 1.1.1
50274  * Copyright(c) 2006-2007, Ext JS, LLC.
50275  *
50276  * Originally Released Under LGPL - original licence link has changed is not relivant.
50277  *
50278  * Fork - LGPL
50279  * <script type="text/javascript">
50280  */
50281
50282 // private - not really -- you end up using it !
50283 // This is a support class used internally by the Grid components
50284
50285 /**
50286  * @class Roo.grid.GridEditor
50287  * @extends Roo.Editor
50288  * Class for creating and editable grid elements.
50289  * @param {Object} config any settings (must include field)
50290  */
50291 Roo.grid.GridEditor = function(field, config){
50292     if (!config && field.field) {
50293         config = field;
50294         field = Roo.factory(config.field, Roo.form);
50295     }
50296     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
50297     field.monitorTab = false;
50298 };
50299
50300 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
50301     
50302     /**
50303      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
50304      */
50305     
50306     alignment: "tl-tl",
50307     autoSize: "width",
50308     hideEl : false,
50309     cls: "x-small-editor x-grid-editor",
50310     shim:false,
50311     shadow:"frame"
50312 });/*
50313  * Based on:
50314  * Ext JS Library 1.1.1
50315  * Copyright(c) 2006-2007, Ext JS, LLC.
50316  *
50317  * Originally Released Under LGPL - original licence link has changed is not relivant.
50318  *
50319  * Fork - LGPL
50320  * <script type="text/javascript">
50321  */
50322   
50323
50324   
50325 Roo.grid.PropertyRecord = Roo.data.Record.create([
50326     {name:'name',type:'string'},  'value'
50327 ]);
50328
50329
50330 Roo.grid.PropertyStore = function(grid, source){
50331     this.grid = grid;
50332     this.store = new Roo.data.Store({
50333         recordType : Roo.grid.PropertyRecord
50334     });
50335     this.store.on('update', this.onUpdate,  this);
50336     if(source){
50337         this.setSource(source);
50338     }
50339     Roo.grid.PropertyStore.superclass.constructor.call(this);
50340 };
50341
50342
50343
50344 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
50345     setSource : function(o){
50346         this.source = o;
50347         this.store.removeAll();
50348         var data = [];
50349         for(var k in o){
50350             if(this.isEditableValue(o[k])){
50351                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
50352             }
50353         }
50354         this.store.loadRecords({records: data}, {}, true);
50355     },
50356
50357     onUpdate : function(ds, record, type){
50358         if(type == Roo.data.Record.EDIT){
50359             var v = record.data['value'];
50360             var oldValue = record.modified['value'];
50361             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
50362                 this.source[record.id] = v;
50363                 record.commit();
50364                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
50365             }else{
50366                 record.reject();
50367             }
50368         }
50369     },
50370
50371     getProperty : function(row){
50372        return this.store.getAt(row);
50373     },
50374
50375     isEditableValue: function(val){
50376         if(val && val instanceof Date){
50377             return true;
50378         }else if(typeof val == 'object' || typeof val == 'function'){
50379             return false;
50380         }
50381         return true;
50382     },
50383
50384     setValue : function(prop, value){
50385         this.source[prop] = value;
50386         this.store.getById(prop).set('value', value);
50387     },
50388
50389     getSource : function(){
50390         return this.source;
50391     }
50392 });
50393
50394 Roo.grid.PropertyColumnModel = function(grid, store){
50395     this.grid = grid;
50396     var g = Roo.grid;
50397     g.PropertyColumnModel.superclass.constructor.call(this, [
50398         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
50399         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
50400     ]);
50401     this.store = store;
50402     this.bselect = Roo.DomHelper.append(document.body, {
50403         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
50404             {tag: 'option', value: 'true', html: 'true'},
50405             {tag: 'option', value: 'false', html: 'false'}
50406         ]
50407     });
50408     Roo.id(this.bselect);
50409     var f = Roo.form;
50410     this.editors = {
50411         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
50412         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
50413         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
50414         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
50415         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
50416     };
50417     this.renderCellDelegate = this.renderCell.createDelegate(this);
50418     this.renderPropDelegate = this.renderProp.createDelegate(this);
50419 };
50420
50421 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
50422     
50423     
50424     nameText : 'Name',
50425     valueText : 'Value',
50426     
50427     dateFormat : 'm/j/Y',
50428     
50429     
50430     renderDate : function(dateVal){
50431         return dateVal.dateFormat(this.dateFormat);
50432     },
50433
50434     renderBool : function(bVal){
50435         return bVal ? 'true' : 'false';
50436     },
50437
50438     isCellEditable : function(colIndex, rowIndex){
50439         return colIndex == 1;
50440     },
50441
50442     getRenderer : function(col){
50443         return col == 1 ?
50444             this.renderCellDelegate : this.renderPropDelegate;
50445     },
50446
50447     renderProp : function(v){
50448         return this.getPropertyName(v);
50449     },
50450
50451     renderCell : function(val){
50452         var rv = val;
50453         if(val instanceof Date){
50454             rv = this.renderDate(val);
50455         }else if(typeof val == 'boolean'){
50456             rv = this.renderBool(val);
50457         }
50458         return Roo.util.Format.htmlEncode(rv);
50459     },
50460
50461     getPropertyName : function(name){
50462         var pn = this.grid.propertyNames;
50463         return pn && pn[name] ? pn[name] : name;
50464     },
50465
50466     getCellEditor : function(colIndex, rowIndex){
50467         var p = this.store.getProperty(rowIndex);
50468         var n = p.data['name'], val = p.data['value'];
50469         
50470         if(typeof(this.grid.customEditors[n]) == 'string'){
50471             return this.editors[this.grid.customEditors[n]];
50472         }
50473         if(typeof(this.grid.customEditors[n]) != 'undefined'){
50474             return this.grid.customEditors[n];
50475         }
50476         if(val instanceof Date){
50477             return this.editors['date'];
50478         }else if(typeof val == 'number'){
50479             return this.editors['number'];
50480         }else if(typeof val == 'boolean'){
50481             return this.editors['boolean'];
50482         }else{
50483             return this.editors['string'];
50484         }
50485     }
50486 });
50487
50488 /**
50489  * @class Roo.grid.PropertyGrid
50490  * @extends Roo.grid.EditorGrid
50491  * This class represents the  interface of a component based property grid control.
50492  * <br><br>Usage:<pre><code>
50493  var grid = new Roo.grid.PropertyGrid("my-container-id", {
50494       
50495  });
50496  // set any options
50497  grid.render();
50498  * </code></pre>
50499   
50500  * @constructor
50501  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
50502  * The container MUST have some type of size defined for the grid to fill. The container will be
50503  * automatically set to position relative if it isn't already.
50504  * @param {Object} config A config object that sets properties on this grid.
50505  */
50506 Roo.grid.PropertyGrid = function(container, config){
50507     config = config || {};
50508     var store = new Roo.grid.PropertyStore(this);
50509     this.store = store;
50510     var cm = new Roo.grid.PropertyColumnModel(this, store);
50511     store.store.sort('name', 'ASC');
50512     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
50513         ds: store.store,
50514         cm: cm,
50515         enableColLock:false,
50516         enableColumnMove:false,
50517         stripeRows:false,
50518         trackMouseOver: false,
50519         clicksToEdit:1
50520     }, config));
50521     this.getGridEl().addClass('x-props-grid');
50522     this.lastEditRow = null;
50523     this.on('columnresize', this.onColumnResize, this);
50524     this.addEvents({
50525          /**
50526              * @event beforepropertychange
50527              * Fires before a property changes (return false to stop?)
50528              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50529              * @param {String} id Record Id
50530              * @param {String} newval New Value
50531          * @param {String} oldval Old Value
50532              */
50533         "beforepropertychange": true,
50534         /**
50535              * @event propertychange
50536              * Fires after a property changes
50537              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
50538              * @param {String} id Record Id
50539              * @param {String} newval New Value
50540          * @param {String} oldval Old Value
50541              */
50542         "propertychange": true
50543     });
50544     this.customEditors = this.customEditors || {};
50545 };
50546 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
50547     
50548      /**
50549      * @cfg {Object} customEditors map of colnames=> custom editors.
50550      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
50551      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
50552      * false disables editing of the field.
50553          */
50554     
50555       /**
50556      * @cfg {Object} propertyNames map of property Names to their displayed value
50557          */
50558     
50559     render : function(){
50560         Roo.grid.PropertyGrid.superclass.render.call(this);
50561         this.autoSize.defer(100, this);
50562     },
50563
50564     autoSize : function(){
50565         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
50566         if(this.view){
50567             this.view.fitColumns();
50568         }
50569     },
50570
50571     onColumnResize : function(){
50572         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
50573         this.autoSize();
50574     },
50575     /**
50576      * Sets the data for the Grid
50577      * accepts a Key => Value object of all the elements avaiable.
50578      * @param {Object} data  to appear in grid.
50579      */
50580     setSource : function(source){
50581         this.store.setSource(source);
50582         //this.autoSize();
50583     },
50584     /**
50585      * Gets all the data from the grid.
50586      * @return {Object} data  data stored in grid
50587      */
50588     getSource : function(){
50589         return this.store.getSource();
50590     }
50591 });/*
50592  * Based on:
50593  * Ext JS Library 1.1.1
50594  * Copyright(c) 2006-2007, Ext JS, LLC.
50595  *
50596  * Originally Released Under LGPL - original licence link has changed is not relivant.
50597  *
50598  * Fork - LGPL
50599  * <script type="text/javascript">
50600  */
50601  
50602 /**
50603  * @class Roo.LoadMask
50604  * A simple utility class for generically masking elements while loading data.  If the element being masked has
50605  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
50606  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
50607  * element's UpdateManager load indicator and will be destroyed after the initial load.
50608  * @constructor
50609  * Create a new LoadMask
50610  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
50611  * @param {Object} config The config object
50612  */
50613 Roo.LoadMask = function(el, config){
50614     this.el = Roo.get(el);
50615     Roo.apply(this, config);
50616     if(this.store){
50617         this.store.on('beforeload', this.onBeforeLoad, this);
50618         this.store.on('load', this.onLoad, this);
50619         this.store.on('loadexception', this.onLoad, this);
50620         this.removeMask = false;
50621     }else{
50622         var um = this.el.getUpdateManager();
50623         um.showLoadIndicator = false; // disable the default indicator
50624         um.on('beforeupdate', this.onBeforeLoad, this);
50625         um.on('update', this.onLoad, this);
50626         um.on('failure', this.onLoad, this);
50627         this.removeMask = true;
50628     }
50629 };
50630
50631 Roo.LoadMask.prototype = {
50632     /**
50633      * @cfg {Boolean} removeMask
50634      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
50635      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
50636      */
50637     /**
50638      * @cfg {String} msg
50639      * The text to display in a centered loading message box (defaults to 'Loading...')
50640      */
50641     msg : 'Loading...',
50642     /**
50643      * @cfg {String} msgCls
50644      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
50645      */
50646     msgCls : 'x-mask-loading',
50647
50648     /**
50649      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
50650      * @type Boolean
50651      */
50652     disabled: false,
50653
50654     /**
50655      * Disables the mask to prevent it from being displayed
50656      */
50657     disable : function(){
50658        this.disabled = true;
50659     },
50660
50661     /**
50662      * Enables the mask so that it can be displayed
50663      */
50664     enable : function(){
50665         this.disabled = false;
50666     },
50667
50668     // private
50669     onLoad : function(){
50670         this.el.unmask(this.removeMask);
50671     },
50672
50673     // private
50674     onBeforeLoad : function(){
50675         if(!this.disabled){
50676             this.el.mask(this.msg, this.msgCls);
50677         }
50678     },
50679
50680     // private
50681     destroy : function(){
50682         if(this.store){
50683             this.store.un('beforeload', this.onBeforeLoad, this);
50684             this.store.un('load', this.onLoad, this);
50685             this.store.un('loadexception', this.onLoad, this);
50686         }else{
50687             var um = this.el.getUpdateManager();
50688             um.un('beforeupdate', this.onBeforeLoad, this);
50689             um.un('update', this.onLoad, this);
50690             um.un('failure', this.onLoad, this);
50691         }
50692     }
50693 };/*
50694  * Based on:
50695  * Ext JS Library 1.1.1
50696  * Copyright(c) 2006-2007, Ext JS, LLC.
50697  *
50698  * Originally Released Under LGPL - original licence link has changed is not relivant.
50699  *
50700  * Fork - LGPL
50701  * <script type="text/javascript">
50702  */
50703 Roo.XTemplate = function(){
50704     Roo.XTemplate.superclass.constructor.apply(this, arguments);
50705     var s = this.html;
50706
50707     s = ['<tpl>', s, '</tpl>'].join('');
50708
50709     var re = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/;
50710
50711     var nameRe = /^<tpl\b[^>]*?for="(.*?)"/;
50712     var ifRe = /^<tpl\b[^>]*?if="(.*?)"/;
50713     var execRe = /^<tpl\b[^>]*?exec="(.*?)"/;
50714     var m, id = 0;
50715     var tpls = [];
50716
50717     while(m = s.match(re)){
50718        var m2 = m[0].match(nameRe);
50719        var m3 = m[0].match(ifRe);
50720        var m4 = m[0].match(execRe);
50721        var exp = null, fn = null, exec = null;
50722        var name = m2 && m2[1] ? m2[1] : '';
50723        if(m3){
50724            exp = m3 && m3[1] ? m3[1] : null;
50725            if(exp){
50726                fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
50727            }
50728        }
50729        if(m4){
50730            exp = m4 && m4[1] ? m4[1] : null;
50731            if(exp){
50732                exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
50733            }
50734        }
50735        if(name){
50736            switch(name){
50737                case '.': name = new Function('values', 'parent', 'with(values){ return values; }'); break;
50738                case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
50739                default: name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
50740            }
50741        }
50742        tpls.push({
50743             id: id,
50744             target: name,
50745             exec: exec,
50746             test: fn,
50747             body: m[1]||''
50748         });
50749        s = s.replace(m[0], '{xtpl'+ id + '}');
50750        ++id;
50751     }
50752     for(var i = tpls.length-1; i >= 0; --i){
50753         this.compileTpl(tpls[i]);
50754     }
50755     this.master = tpls[tpls.length-1];
50756     this.tpls = tpls;
50757 };
50758 Roo.extend(Roo.XTemplate, Roo.Template, {
50759
50760     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
50761
50762     applySubTemplate : function(id, values, parent){
50763         var t = this.tpls[id];
50764         if(t.test && !t.test.call(this, values, parent)){
50765             return '';
50766         }
50767         if(t.exec && t.exec.call(this, values, parent)){
50768             return '';
50769         }
50770         var vs = t.target ? t.target.call(this, values, parent) : values;
50771         parent = t.target ? values : parent;
50772         if(t.target && vs instanceof Array){
50773             var buf = [];
50774             for(var i = 0, len = vs.length; i < len; i++){
50775                 buf[buf.length] = t.compiled.call(this, vs[i], parent);
50776             }
50777             return buf.join('');
50778         }
50779         return t.compiled.call(this, vs, parent);
50780     },
50781
50782     compileTpl : function(tpl){
50783         var fm = Roo.util.Format;
50784         var useF = this.disableFormats !== true;
50785         var sep = Roo.isGecko ? "+" : ",";
50786         var fn = function(m, name, format, args){
50787             if(name.substr(0, 4) == 'xtpl'){
50788                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
50789             }
50790             var v;
50791             if(name.indexOf('.') != -1){
50792                 v = name;
50793             }else{
50794                 v = "values['" + name + "']";
50795             }
50796             if(format && useF){
50797                 args = args ? ',' + args : "";
50798                 if(format.substr(0, 5) != "this."){
50799                     format = "fm." + format + '(';
50800                 }else{
50801                     format = 'this.call("'+ format.substr(5) + '", ';
50802                     args = ", values";
50803                 }
50804             }else{
50805                 args= ''; format = "("+v+" === undefined ? '' : ";
50806             }
50807             return "'"+ sep + format + v + args + ")"+sep+"'";
50808         };
50809         var body;
50810         // branched to use + in gecko and [].join() in others
50811         if(Roo.isGecko){
50812             body = "tpl.compiled = function(values, parent){ return '" +
50813                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
50814                     "';};";
50815         }else{
50816             body = ["tpl.compiled = function(values, parent){ return ['"];
50817             body.push(tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
50818             body.push("'].join('');};");
50819             body = body.join('');
50820         }
50821         /** eval:var:zzzzzzz */
50822         eval(body);
50823         return this;
50824     },
50825
50826     applyTemplate : function(values){
50827         return this.master.compiled.call(this, values, {});
50828         var s = this.subs;
50829     },
50830
50831     apply : function(){
50832         return this.applyTemplate.apply(this, arguments);
50833     },
50834
50835     compile : function(){return this;}
50836 });
50837
50838 Roo.XTemplate.from = function(el){
50839     el = Roo.getDom(el);
50840     return new Roo.XTemplate(el.value || el.innerHTML);
50841 };/*
50842  * Original code for Roojs - LGPL
50843  * <script type="text/javascript">
50844  */
50845  
50846 /**
50847  * @class Roo.XComponent
50848  * A delayed Element creator...
50849  * 
50850  * Mypart.xyx = new Roo.XComponent({
50851
50852     parent : 'Mypart.xyz', // empty == document.element.!!
50853     order : '001',
50854     name : 'xxxx'
50855     region : 'xxxx'
50856     disabled : function() {} 
50857      
50858     tree : function() { // return an tree of xtype declared components
50859         var MODULE = this;
50860         return 
50861         {
50862             xtype : 'NestedLayoutPanel',
50863             // technicall
50864         }
50865      ]
50866  *})
50867  * @extends Roo.util.Observable
50868  * @constructor
50869  * @param cfg {Object} configuration of component
50870  * 
50871  */
50872 Roo.XComponent = function(cfg) {
50873     Roo.apply(this, cfg);
50874     this.addEvents({ 
50875         /**
50876              * @event built
50877              * Fires when this the componnt is built
50878              * @param {Roo.XComponent} c the component
50879              */
50880         'built' : true,
50881         /**
50882              * @event buildcomplete
50883              * Fires on the top level element when all elements have been built
50884              * @param {Roo.XComponent} c the top level component.
50885          */
50886         'buildcomplete' : true
50887         
50888     });
50889     
50890     Roo.XComponent.register(this);
50891     this.modules = false;
50892     this.el = false; // where the layout goes..
50893     
50894     
50895 }
50896 Roo.extend(Roo.XComponent, Roo.util.Observable, {
50897     /**
50898      * @property el
50899      * The created element (with Roo.factory())
50900      * @type {Roo.Layout}
50901      */
50902     el  : false,
50903     
50904     /**
50905      * @property el
50906      * for BC  - use el in new code
50907      * @type {Roo.Layout}
50908      */
50909     panel : false,
50910     
50911     /**
50912      * @property layout
50913      * for BC  - use el in new code
50914      * @type {Roo.Layout}
50915      */
50916     layout : false,
50917     
50918      /**
50919      * @cfg {Function|boolean} disabled
50920      * If this module is disabled by some rule, return true from the funtion
50921      */
50922     disabled : false,
50923     
50924     /**
50925      * @cfg {String} parent 
50926      * Name of parent element which it get xtype added to..
50927      */
50928     parent: false,
50929     
50930     /**
50931      * @cfg {String} order
50932      * Used to set the order in which elements are created (usefull for multiple tabs)
50933      */
50934     
50935     order : false,
50936     /**
50937      * @cfg {String} name
50938      * String to display while loading.
50939      */
50940     name : false,
50941     /**
50942      * @cfg {Array} items
50943      * A single item array - the first element is the root of the tree..
50944      * It's done this way to stay compatible with the Xtype system...
50945      */
50946     items : false
50947      
50948      
50949     
50950 });
50951
50952 Roo.apply(Roo.XComponent, {
50953     
50954     /**
50955      * @property  buildCompleted
50956      * True when the builder has completed building the interface.
50957      * @type Boolean
50958      */
50959     buildCompleted : false,
50960      
50961     /**
50962      * @property  topModule
50963      * the upper most module - uses document.element as it's constructor.
50964      * @type Object
50965      */
50966      
50967     topModule  : false,
50968       
50969     /**
50970      * @property  modules
50971      * array of modules to be created by registration system.
50972      * @type Roo.XComponent
50973      */
50974     
50975     modules : [],
50976       
50977     
50978     /**
50979      * Register components to be built later.
50980      *
50981      * This solves the following issues
50982      * - Building is not done on page load, but after an authentication process has occured.
50983      * - Interface elements are registered on page load
50984      * - Parent Interface elements may not be loaded before child, so this handles that..
50985      * 
50986      *
50987      * example:
50988      * 
50989      * MyApp.register({
50990           order : '000001',
50991           module : 'Pman.Tab.projectMgr',
50992           region : 'center',
50993           parent : 'Pman.layout',
50994           disabled : false,  // or use a function..
50995         })
50996      
50997      * * @param {Object} details about module
50998      */
50999     register : function(obj) {
51000         this.modules.push(obj);
51001          
51002     },
51003     /**
51004      * convert a string to an object..
51005      * 
51006      */
51007     
51008     toObject : function(str)
51009     {
51010         if (!str || typeof(str) == 'object') {
51011             return str;
51012         }
51013         var ar = str.split('.');
51014         var rt, o;
51015         rt = ar.shift();
51016             /** eval:var:o */
51017         eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
51018         if (o === false) {
51019             throw "Module not found : " + str;
51020         }
51021         Roo.each(ar, function(e) {
51022             if (typeof(o[e]) == 'undefined') {
51023                 throw "Module not found : " + str;
51024             }
51025             o = o[e];
51026         });
51027         return o;
51028         
51029     },
51030     
51031     
51032     /**
51033      * move modules into their correct place in the tree..
51034      * 
51035      */
51036     preBuild : function ()
51037     {
51038         
51039         Roo.each(this.modules , function (obj)
51040         {
51041             obj.parent = this.toObject(obj.parent);
51042             
51043             if (!obj.parent) {
51044                 this.topModule = obj;
51045                 return;
51046             }
51047             
51048             if (!obj.parent.modules) {
51049                 obj.parent.modules = new Roo.util.MixedCollection(false, 
51050                     function(o) { return o.order + '' }
51051                 );
51052             }
51053             
51054             obj.parent.modules.add(obj);
51055         }, this);
51056     },
51057     
51058      /**
51059      * make a list of modules to build.
51060      * @return {Array} list of modules. 
51061      */ 
51062     
51063     buildOrder : function()
51064     {
51065         var _this = this;
51066         var cmp = function(a,b) {   
51067             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
51068         };
51069         
51070         if (!this.topModule || !this.topModule.modules) {
51071             throw "No top level modules to build";
51072         }
51073        
51074         // make a flat list in order of modules to build.
51075         var mods = [ this.topModule ];
51076         
51077         
51078         // add modules to their parents..
51079         var addMod = function(m) {
51080            // Roo.debug && Roo.log(m.modKey);
51081             
51082             mods.push(m);
51083             if (m.modules) {
51084                 m.modules.keySort('ASC',  cmp );
51085                 m.modules.each(addMod);
51086             }
51087             // not sure if this is used any more..
51088             if (m.finalize) {
51089                 m.finalize.name = m.name + " (clean up) ";
51090                 mods.push(m.finalize);
51091             }
51092             
51093         }
51094         this.topModule.modules.keySort('ASC',  cmp );
51095         this.topModule.modules.each(addMod);
51096         return mods;
51097     },
51098     
51099      /**
51100      * Build the registered modules.
51101      * @param {Object} parent element.
51102      * @param {Function} optional method to call after module has been added.
51103      * 
51104      */ 
51105    
51106     build : function() 
51107     {
51108         
51109         this.preBuild();
51110         var mods = this.buildOrder();
51111       
51112         //this.allmods = mods;
51113         //Roo.debug && Roo.log(mods);
51114         //return;
51115         if (!mods.length) { // should not happen
51116             throw "NO modules!!!";
51117         }
51118         
51119         
51120         
51121         // flash it up as modal - so we store the mask!?
51122         Roo.MessageBox.show({ title: 'loading' });
51123         Roo.MessageBox.show({
51124            title: "Please wait...",
51125            msg: "Building Interface...",
51126            width:450,
51127            progress:true,
51128            closable:false,
51129            modal: false
51130           
51131         });
51132         var total = mods.length;
51133         
51134         var _this = this;
51135         var progressRun = function() {
51136             if (!mods.length) {
51137                 Roo.debug && Roo.log('hide?');
51138                 Roo.MessageBox.hide();
51139                 _this.topModule.fireEvent('buildcomplete', _this.topModule);
51140                 return;    
51141             }
51142             
51143             var m = mods.shift();
51144             Roo.debug && Roo.log(m);
51145             if (typeof(m) == 'function') { // not sure if this is supported any more..
51146                 m.call(this);
51147                 return progressRun.defer(10, _this);
51148             } 
51149             
51150             Roo.MessageBox.updateProgress(
51151                 (total  - mods.length)/total,  "Building Interface " + (total  - mods.length) + 
51152                     " of " + total + 
51153                     (m.name ? (' - ' + m.name) : '')
51154                     );
51155             
51156          
51157             
51158             var disabled = (typeof(m.disabled) == 'function') ?
51159                 m.disabled.call(m.module.disabled) : m.disabled;    
51160             
51161             
51162             if (disabled) {
51163                 return progressRun(); // we do not update the display!
51164             }
51165             
51166             if (!m.parent) {
51167                 // it's a top level one..
51168                 var layoutbase = new Ext.BorderLayout(document.body, {
51169                
51170                     center: {
51171                          titlebar: false,
51172                          autoScroll:false,
51173                          closeOnTab: true,
51174                          tabPosition: 'top',
51175                          //resizeTabs: true,
51176                          alwaysShowTabs: true,
51177                          minTabWidth: 140
51178                     }
51179                 });
51180                 var tree = m.tree();
51181                 tree.region = 'center';
51182                 m.el = layoutbase.addxtype(tree);
51183                 m.panel = m.el;
51184                 m.layout = m.panel.layout;    
51185                 return progressRun.defer(10, _this);
51186             }
51187             
51188             var tree = m.tree();
51189             tree.region = tree.region || m.region;
51190             m.el = m.parent.el.addxtype(tree);
51191             m.fireEvent('built', m);
51192             m.panel = m.el;
51193             m.layout = m.panel.layout;    
51194             progressRun.defer(10, _this); 
51195             
51196         }
51197         progressRun.defer(1, _this);
51198      
51199         
51200         
51201     }
51202      
51203    
51204     
51205     
51206 });
51207  //<script type="text/javascript">
51208
51209
51210 /**
51211  * @class Roo.Login
51212  * @extends Roo.LayoutDialog
51213  * A generic Login Dialog..... - only one needed in theory!?!?
51214  *
51215  * Fires XComponent builder on success...
51216  * 
51217  * Sends 
51218  *    username,password, lang = for login actions.
51219  *    check = 1 for periodic checking that sesion is valid.
51220  *    passwordRequest = email request password
51221  *    logout = 1 = to logout
51222  * 
51223  * Affects: (this id="????" elements)
51224  *   loading  (removed) (used to indicate application is loading)
51225  *   loading-mask (hides) (used to hide application when it's building loading)
51226  *   
51227  * 
51228  * Usage: 
51229  *    
51230  * 
51231  * Myapp.login = Roo.Login({
51232      url: xxxx,
51233    
51234      realm : 'Myapp', 
51235      
51236      
51237      method : 'POST',
51238      
51239      
51240      * 
51241  })
51242  * 
51243  * 
51244  * 
51245  **/
51246  
51247 Roo.Login = function(cfg)
51248 {
51249     this.addEvents({
51250         'refreshed' : true
51251     });
51252     
51253     Roo.apply(this,cfg);
51254     
51255     Roo.onReady(function() {
51256         this.onLoad();
51257     }, this);
51258     // call parent..
51259     
51260    
51261     Roo.Login.superclass.constructor.call(this, this);
51262     //this.addxtype(this.items[0]);
51263     
51264     
51265 }
51266
51267
51268 Roo.extend(Roo.Login, Roo.LayoutDialog, {
51269     
51270     /**
51271      * @cfg {String} method
51272      * Method used to query for login details.
51273      */
51274     
51275     method : 'POST',
51276     /**
51277      * @cfg {String} url
51278      * URL to query login data. - eg. baseURL + '/Login.php'
51279      */
51280     url : '',
51281     
51282     /**
51283      * @property user
51284      * The user data - if user.id < 0 then login will be bypassed. (used for inital setup situation.
51285      * @type {Object} 
51286      */
51287     user : false,
51288     /**
51289      * @property checkFails
51290      * Number of times we have attempted to get authentication check, and failed.
51291      * @type {Number} 
51292      */
51293     checkFails : 0,
51294       /**
51295      * @property intervalID
51296      * The window interval that does the constant login checking.
51297      * @type {Number} 
51298      */
51299     intervalID : 0,
51300     
51301     
51302     onLoad : function() // called on page load...
51303     {
51304         // load 
51305          
51306         if (Roo.get('loading')) { // clear any loading indicator..
51307             Roo.get('loading').remove();
51308         }
51309         
51310         //this.switchLang('en'); // set the language to english..
51311        
51312         this.check({
51313             success:  function(response, opts)  {  // check successfull...
51314             
51315                 var res = this.processResponse(response);
51316                 this.checkFails =0;
51317                 if (!res.success) { // error!
51318                     this.checkFails = 5;
51319                     //console.log('call failure');
51320                     return this.failure(response,opts);
51321                 }
51322                 
51323                 if (!res.data.id) { // id=0 == login failure.
51324                     return this.show();
51325                 }
51326                 
51327                               
51328                         //console.log(success);
51329                 this.fillAuth(res.data);   
51330                 this.checkFails =0;
51331                 Roo.XComponent.build();
51332             },
51333             failure : this.show
51334         });
51335         
51336     }, 
51337     
51338     
51339     check: function(cfg) // called every so often to refresh cookie etc..
51340     {
51341         if (cfg.again) { // could be undefined..
51342             this.checkFails++;
51343         } else {
51344             this.checkFails = 0;
51345         }
51346         var _this = this;
51347         if (this.sending) {
51348             if ( this.checkFails > 4) {
51349                 Roo.MessageBox.alert("Error",  
51350                     "Error getting authentication status. - try reloading, or wait a while", function() {
51351                         _this.sending = false;
51352                     }); 
51353                 return;
51354             }
51355             cfg.again = true;
51356             _this.check.defer(10000, _this, [ cfg ]); // check in 10 secs.
51357             return;
51358         }
51359         this.sending = true;
51360         
51361         Roo.Ajax.request({  
51362             url: this.url,
51363             params: {
51364                 getAuthUser: true
51365             },  
51366             method: this.method,
51367             success:  cfg.success || this.success,
51368             failure : cfg.failure || this.failure,
51369             scope : this,
51370             callCfg : cfg
51371               
51372         });  
51373     }, 
51374     
51375     
51376     logout: function()
51377     {
51378         window.onbeforeunload = function() { }; // false does not work for IE..
51379         this.user = false;
51380         var _this = this;
51381         
51382         Roo.Ajax.request({  
51383             url: this.url,
51384             params: {
51385                 logout: 1
51386             },  
51387             method: 'GET',
51388             failure : function() {
51389                 Roo.MessageBox.alert("Error", "Error logging out. - continuing anyway.", function() {
51390                     document.location = document.location.toString() + '?ts=' + Math.random();
51391                 });
51392                 
51393             },
51394             success : function() {
51395                 _this.user = false;
51396                 this.checkFails =0;
51397                 // fixme..
51398                 document.location = document.location.toString() + '?ts=' + Math.random();
51399             }
51400               
51401               
51402         }); 
51403     },
51404     
51405     processResponse : function (response)
51406     {
51407         var res = '';
51408         try {
51409             res = Roo.decode(response.responseText);
51410             // oops...
51411             if (typeof(res) != 'object') {
51412                 res = { success : false, errorMsg : res, errors : true };
51413             }
51414             if (typeof(res.success) == 'undefined') {
51415                 res.success = false;
51416             }
51417             
51418         } catch(e) {
51419             res = { success : false,  errorMsg : response.responseText, errors : true };
51420         }
51421         return res;
51422     },
51423     
51424     success : function(response, opts)  // check successfull...
51425     {  
51426         this.sending = false;
51427         var res = this.processResponse(response);
51428         if (!res.success) {
51429             return this.failure(response, opts);
51430         }
51431         if (!res.data || !res.data.id) {
51432             return this.failure(response,opts);
51433         }
51434         //console.log(res);
51435         this.fillAuth(res.data);
51436         
51437         this.checkFails =0;
51438         
51439     },
51440     
51441     
51442     failure : function (response, opts) // called if login 'check' fails.. (causes re-check)
51443     {
51444         this.authUser = -1;
51445         this.sending = false;
51446         var res = this.processResponse(response);
51447         //console.log(res);
51448         if ( this.checkFails > 2) {
51449         
51450             Roo.MessageBox.alert("Error", res.errorMsg ? res.errorMsg : 
51451                 "Error getting authentication status. - try reloading"); 
51452             return;
51453         }
51454         opts.callCfg.again = true;
51455         this.check.defer(1000, this, [ opts.callCfg ]);
51456         return;  
51457     },
51458     
51459     
51460     
51461     fillAuth: function(au) {
51462         this.startAuthCheck();
51463         this.authUserId = au.id;
51464         this.authUser = au;
51465         this.lastChecked = new Date();
51466         this.fireEvent('refreshed', au);
51467         //Pman.Tab.FaxQueue.newMaxId(au.faxMax);
51468         //Pman.Tab.FaxTab.setTitle(au.faxNumPending);
51469         au.lang = au.lang || 'en';
51470         //this.switchLang(Roo.state.Manager.get('Pman.Login.lang', 'en'));
51471         Roo.state.Manager.set( this.realm + 'lang' , au.lang);
51472         this.switchLang(au.lang );
51473         
51474      
51475         // open system... - -on setyp..
51476         if (this.authUserId  < 0) {
51477             Roo.MessageBox.alert("Warning", 
51478                 "This is an open system - please set up a admin user with a password.");  
51479         }
51480          
51481         //Pman.onload(); // which should do nothing if it's a re-auth result...
51482         
51483              
51484     },
51485     
51486     startAuthCheck : function() // starter for timeout checking..
51487     {
51488         if (this.intervalID) { // timer already in place...
51489             return false;
51490         }
51491         var _this = this;
51492         this.intervalID =  window.setInterval(function() {
51493               _this.check(false);
51494             }, 120000); // every 120 secs = 2mins..
51495         
51496         
51497     },
51498          
51499     
51500     switchLang : function (lang) 
51501     {
51502         _T = typeof(_T) == 'undefined' ? false : _T;
51503           if (!_T || !lang.length) {
51504             return;
51505         }
51506         
51507         if (!_T && lang != 'en') {
51508             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51509             return;
51510         }
51511         
51512         if (typeof(_T.en) == 'undefined') {
51513             _T.en = {};
51514             Roo.apply(_T.en, _T);
51515         }
51516         
51517         if (typeof(_T[lang]) == 'undefined') {
51518             Roo.MessageBox.alert("Sorry", "Language not available yet (" + lang +')');
51519             return;
51520         }
51521         
51522         
51523         Roo.apply(_T, _T[lang]);
51524         // just need to set the text values for everything...
51525         var _this = this;
51526         /* this will not work ...
51527         if (this.form) { 
51528             
51529                
51530             function formLabel(name, val) {
51531                 _this.form.findField(name).fieldEl.child('label').dom.innerHTML  = val;
51532             }
51533             
51534             formLabel('password', "Password"+':');
51535             formLabel('username', "Email Address"+':');
51536             formLabel('lang', "Language"+':');
51537             this.dialog.setTitle("Login");
51538             this.dialog.buttons[0].setText("Forgot Password");
51539             this.dialog.buttons[1].setText("Login");
51540         }
51541         */
51542         
51543         
51544     },
51545     
51546     
51547     title: "Login",
51548     modal: true,
51549     width:  350,
51550     //height: 230,
51551     height: 180,
51552     shadow: true,
51553     minWidth:200,
51554     minHeight:180,
51555     //proxyDrag: true,
51556     closable: false,
51557     draggable: false,
51558     collapsible: false,
51559     resizable: false,
51560     center: {  // needed??
51561         autoScroll:false,
51562         titlebar: false,
51563        // tabPosition: 'top',
51564         hideTabs: true,
51565         closeOnTab: true,
51566         alwaysShowTabs: false
51567     } ,
51568     listeners : {
51569         
51570         show  : function(dlg)
51571         {
51572             //console.log(this);
51573             this.form = this.layout.getRegion('center').activePanel.form;
51574             this.form.dialog = dlg;
51575             this.buttons[0].form = this.form;
51576             this.buttons[0].dialog = dlg;
51577             this.buttons[1].form = this.form;
51578             this.buttons[1].dialog = dlg;
51579            
51580            //this.resizeToLogo.defer(1000,this);
51581             // this is all related to resizing for logos..
51582             //var sz = Roo.get(Pman.Login.form.el.query('img')[0]).getSize();
51583            //// if (!sz) {
51584              //   this.resizeToLogo.defer(1000,this);
51585              //   return;
51586            // }
51587             //var w = Ext.lib.Dom.getViewWidth() - 100;
51588             //var h = Ext.lib.Dom.getViewHeight() - 100;
51589             //this.resizeTo(Math.max(350, Math.min(sz.width + 30, w)),Math.min(sz.height+200, h));
51590             //this.center();
51591             if (this.disabled) {
51592                 this.hide();
51593                 return;
51594             }
51595             
51596             if (this.user.id < 0) { // used for inital setup situations.
51597                 return;
51598             }
51599             
51600             if (this.intervalID) {
51601                 // remove the timer
51602                 window.clearInterval(this.intervalID);
51603                 this.intervalID = false;
51604             }
51605             
51606             
51607             if (Roo.get('loading')) {
51608                 Roo.get('loading').remove();
51609             }
51610             if (Roo.get('loading-mask')) {
51611                 Roo.get('loading-mask').hide();
51612             }
51613             
51614             //incomming._node = tnode;
51615             this.form.reset();
51616             //this.dialog.modal = !modal;
51617             //this.dialog.show();
51618             this.el.unmask(); 
51619             
51620             
51621             this.form.setValues({
51622                 'username' : Roo.state.Manager.get(this.realm + '.username', ''),
51623                 'lang' : Roo.state.Manager.get(this.realm + '.lang', 'en')
51624             });
51625             
51626             this.switchLang(Roo.state.Manager.get(this.realm + '.lang', 'en'));
51627             if (this.form.findField('username').getValue().length > 0 ){
51628                 this.form.findField('password').focus();
51629             } else {
51630                this.form.findField('username').focus();
51631             }
51632     
51633         }
51634     },
51635     items : [
51636          {
51637        
51638             xtype : 'ContentPanel',
51639             xns : Roo,
51640             region: 'center',
51641             fitToFrame : true,
51642             
51643             items : [
51644     
51645                 {
51646                
51647                     xtype : 'Form',
51648                     xns : Roo.form,
51649                     labelWidth: 100,
51650                     style : 'margin: 10px;',
51651                     
51652                     listeners : {
51653                         actionfailed : function(f, act) {
51654                             // form can return { errors: .... }
51655                                 
51656                             //act.result.errors // invalid form element list...
51657                             //act.result.errorMsg// invalid form element list...
51658                             
51659                             this.dialog.el.unmask();
51660                             Roo.MessageBox.alert("Error", act.result.errorMsg ? act.result.errorMsg : 
51661                                         "Login failed - communication error - try again.");
51662                                       
51663                         },
51664                         actioncomplete: function(re, act) {
51665                              
51666                             Roo.state.Manager.set(
51667                                 this.dialog.realm + '.username',  
51668                                     this.findField('username').getValue()
51669                             );
51670                             Roo.state.Manager.set(
51671                                 this.dialog.realm + '.lang',  
51672                                 this.findField('lang').getValue() 
51673                             );
51674                             
51675                             this.dialog.fillAuth(act.result.data);
51676                               
51677                             this.dialog.hide();
51678                             
51679                             if (Roo.get('loading-mask')) {
51680                                 Roo.get('loading-mask').show();
51681                             }
51682                             Roo.XComponent.build();
51683                             
51684                              
51685                             
51686                         }
51687                     },
51688                     items : [
51689                         {
51690                             xtype : 'TextField',
51691                             xns : Roo.form,
51692                             fieldLabel: "Email Address",
51693                             name: 'username',
51694                             width:200,
51695                             autoCreate : {tag: "input", type: "text", size: "20"}
51696                         },
51697                         {
51698                             xtype : 'TextField',
51699                             xns : Roo.form,
51700                             fieldLabel: "Password",
51701                             inputType: 'password',
51702                             name: 'password',
51703                             width:200,
51704                             autoCreate : {tag: "input", type: "text", size: "20"},
51705                             listeners : {
51706                                 specialkey : function(e,ev) {
51707                                     if (ev.keyCode == 13) {
51708                                         this.form.dialog.el.mask("Logging in");
51709                                         this.form.doAction('submit', {
51710                                             url: this.form.dialog.url,
51711                                             method: this.form.dialog.method
51712                                         });
51713                                     }
51714                                 }
51715                             }  
51716                         },
51717                         {
51718                             xtype : 'ComboBox',
51719                             xns : Roo.form,
51720                             fieldLabel: "Language",
51721                             name : 'langdisp',
51722                             store: {
51723                                 xtype : 'SimpleStore',
51724                                 fields: ['lang', 'ldisp'],
51725                                 data : [
51726                                     [ 'en', 'English' ],
51727                                     [ 'zh_HK' , '\u7E41\u4E2D' ],
51728                                     [ 'zh_CN', '\u7C21\u4E2D' ]
51729                                 ]
51730                             },
51731                             
51732                             valueField : 'lang',
51733                             hiddenName:  'lang',
51734                             width: 200,
51735                             displayField:'ldisp',
51736                             typeAhead: false,
51737                             editable: false,
51738                             mode: 'local',
51739                             triggerAction: 'all',
51740                             emptyText:'Select a Language...',
51741                             selectOnFocus:true,
51742                             listeners : {
51743                                 select :  function(cb, rec, ix) {
51744                                     this.form.switchLang(rec.data.lang);
51745                                 }
51746                             }
51747                         
51748                         }
51749                     ]
51750                 }
51751                   
51752                 
51753             ]
51754         }
51755     ],
51756     buttons : [
51757         {
51758             xtype : 'Button',
51759             xns : 'Roo',
51760             text : "Forgot Password",
51761             listeners : {
51762                 click : function() {
51763                     //console.log(this);
51764                     var n = this.form.findField('username').getValue();
51765                     if (!n.length) {
51766                         Roo.MessageBox.alert("Error", "Fill in your email address");
51767                         return;
51768                     }
51769                     Roo.Ajax.request({
51770                         url: this.dialog.url,
51771                         params: {
51772                             passwordRequest: n
51773                         },
51774                         method: this.dialog.method,
51775                         success:  function(response, opts)  {  // check successfull...
51776                         
51777                             var res = this.dialog.processResponse(response);
51778                             if (!res.success) { // error!
51779                                Roo.MessageBox.alert("Error" ,
51780                                     res.errorMsg ? res.errorMsg  : "Problem Requesting Password Reset");
51781                                return;
51782                             }
51783                             Roo.MessageBox.alert("Notice" ,
51784                                 "Please check you email for the Password Reset message");
51785                         },
51786                         failure : function() {
51787                             Roo.MessageBox.alert("Error" , "Problem Requesting Password Reset");
51788                         }
51789                         
51790                     });
51791                 }
51792             }
51793         },
51794         {
51795             xtype : 'Button',
51796             xns : 'Roo',
51797             text : "Login",
51798             listeners : {
51799                 
51800                 click : function () {
51801                         
51802                     this.dialog.el.mask("Logging in");
51803                     this.form.doAction('submit', {
51804                             url: this.dialog.url,
51805                             method: this.dialog.method
51806                     });
51807                 }
51808             }
51809         }
51810     ]
51811   
51812   
51813 })
51814  
51815
51816
51817